Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
81 commits
Select commit Hold shift + click to select a range
125723a
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
e4cbcad
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
c43060f
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
0604b72
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
5099871
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
12a734a
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
e20bb93
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
c5e1502
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
f832490
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
726869a
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
13d8631
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
eda1537
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
4f0ce6d
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
8d2473c
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
055655d
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
4906f63
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
de16c45
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
3466de3
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
41a6c20
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
6d9dd41
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
4a5caa7
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
819bd2c
feat(sandbox): a local process runtime for Sandboxes, LAYERFS_SANDBOX…
jiangkoumo Oct 22, 2026
13a791a
fix(sandbox): address the first review of the local runtime (#402)
jiangkoumo Oct 22, 2026
13361ba
fix(sandbox): address the first review of the local runtime (#402)
jiangkoumo Oct 22, 2026
fed0249
fix(sandbox): address the first review of the local runtime (#402)
jiangkoumo Oct 22, 2026
603b561
fix(sandbox): address the first review of the local runtime (#402)
jiangkoumo Oct 22, 2026
1f2307e
fix(sandbox): address the first review of the local runtime (#402)
jiangkoumo Oct 22, 2026
4a66f35
fix(sandbox): address the first review of the local runtime (#402)
jiangkoumo Oct 22, 2026
cba6af0
fix(sandbox): address the first review of the local runtime (#402)
jiangkoumo Oct 22, 2026
f71df4a
fix(sandbox): address the first review of the local runtime (#402)
jiangkoumo Oct 22, 2026
0466af2
fix(sandbox): address the first review of the local runtime (#402)
jiangkoumo Oct 22, 2026
9e0b6a0
fix(sandbox): address the first review of the local runtime (#402)
jiangkoumo Oct 22, 2026
fd35180
fix(sandbox): address the first review of the local runtime (#402)
jiangkoumo Oct 22, 2026
0cf539a
fix(sandbox): address the first review of the local runtime (#402)
jiangkoumo Oct 22, 2026
e88bd55
fix(sandbox): address the first review of the local runtime (#402)
jiangkoumo Oct 22, 2026
0f156e3
fix(sandbox): address the first review of the local runtime (#402)
jiangkoumo Oct 22, 2026
3fa366c
fix(sandbox): address the first review of the local runtime (#402)
jiangkoumo Oct 22, 2026
d53d4fb
fix(sandbox): address the first review of the local runtime (#402)
jiangkoumo Oct 22, 2026
d463067
fix(sandbox): address the first review of the local runtime (#402)
jiangkoumo Oct 22, 2026
8c22d17
fix(sandbox): address the first review of the local runtime (#402)
jiangkoumo Oct 22, 2026
5f83fab
Merge PR #402 head 8c22d1754 (fork feat/sandbox-local-backend) into m…
jiangkoumo Oct 22, 2026
e63af4c
feat(nfs): a loopback NFSv3 export of one mounted Workspace (layerfs-…
jiangkoumo Oct 22, 2026
757e448
feat(daemon): NFS as a kernel mount entry beside FUSE (LAYERFS_KERNEL…
jiangkoumo Oct 22, 2026
6c8ca0e
feat(sandbox): local Sandboxes on macOS and the SDK's native route (L…
jiangkoumo Oct 22, 2026
1deb780
feat(store-service): open the Store on macOS for the native route; bu…
jiangkoumo Oct 22, 2026
5169c30
feat(dsh): the LayerFS Host on the native macOS route (sandbox: nativ…
jiangkoumo Oct 22, 2026
94f15b7
Merge upstream main 1aa56ece7 into feat/macos-native-on-main
jiangkoumo Oct 23, 2026
833acf7
Merge upstream main e8f129c1b into feat/macos-native-on-main
jiangkoumo Oct 23, 2026
ada323c
Merge upstream main 4497b0cbf (v1.1.0, #428) into PR #402 (local port)
jiangkoumo Oct 24, 2026
da213ce
Merge PR #402 local port (ada323c90, with upstream main 4497b0cbf v1.…
jiangkoumo Oct 24, 2026
009931c
layerfs-nfs: macOS mount tolerates slow answers; LOOKUP carries the d…
jiangkoumo Oct 24, 2026
915b622
Merge upstream main c77281e49 (#434 Ruff format, #440) into feat/maco…
jiangkoumo Oct 24, 2026
0cc863a
Merge upstream main ad78914e3 (#449, per-Store overlay name) into PR …
jiangkoumo Oct 25, 2026
4deeefb
Merge upstream main ad78914e3 (#449, per-Store overlay name) into fea…
jiangkoumo Oct 25, 2026
a700af8
layerfs-nfs: keep AppleDouble files in the export's memory instead of…
jiangkoumo Oct 25, 2026
4c1648e
fix(native): bound NFS helper reaping and detach before daemon stop
jiangkoumo Oct 25, 2026
b3f8f80
fix(binding): release Sandbox ownership while unmount waits
jiangkoumo Oct 26, 2026
d3a2a8c
fix(binding): use libc::O_NOFOLLOW for the unmount lease
jiangkoumo Oct 26, 2026
766e67e
fix(sandbox): detach local mounts before stopping the daemon
jiangkoumo Oct 26, 2026
3e427a3
Merge upstream main caebd268d (D8 three-layer split, #465) into PR #4…
jiangkoumo Oct 27, 2026
8b11778
style: rustfmt
jiangkoumo Oct 27, 2026
1931032
Merge upstream main caebd268d (D8 three-layer split, #465) into PR #4…
jiangkoumo Oct 27, 2026
eb21fbb
test(node): cover local Sandbox records through the built addon (#402)
jiangkoumo Oct 27, 2026
7fcfdfb
Merge port/d8-402 8b17038f2 (#402 node tests through the built addon)…
jiangkoumo Oct 27, 2026
8b17038
style: rustfmt
jiangkoumo Oct 27, 2026
1dcd1fb
test(node): native Sandbox records and the unmount lease regression a…
jiangkoumo Oct 27, 2026
a6b53a9
fix(tools): place layerfs-nfs in the engine layer of the boundary map…
jiangkoumo Oct 27, 2026
bf50a5f
docs(node): list native_local in the binding test guide (#402)
jiangkoumo Oct 27, 2026
befe2a9
Merge port/d8-402 bf50a5f70 (#402 binding test guide) into PR #427 (l…
jiangkoumo Oct 27, 2026
9478190
docs(node): native records in the binding test guide (#427)
jiangkoumo Oct 27, 2026
eb0d6d6
Merge upstream main 582bc786f into feat/macos-native-on-main (#427)
jiangkoumo Oct 28, 2026
d55acc1
refactor(node): native is the only local deployment; local is no long…
jiangkoumo Oct 28, 2026
3147916
Merge upstream main 35d36575c (#500 stalled unmount recovery) into PR…
jiangkoumo Oct 29, 2026
2518769
test(sandbox): wait for local daemon reaping (H29)
jiangkoumo Oct 29, 2026
5de8753
perf(nfs): suppress ignored macOS access-time updates
jiangkoumo Oct 30, 2026
2aef367
docs: explain SQLite WAL compatibility on macOS NFS
jiangkoumo Oct 30, 2026
8ab7be3
Merge macOS NFS noatime mount option
jiangkoumo Oct 30, 2026
fa02a1b
Merge SQLite WAL guidance for macOS NFS
jiangkoumo Oct 30, 2026
a32da57
feat(sandbox): define full Runtime/Sandbox interface and Docker provi…
jiangkoumo Oct 31, 2026
1dba37b
Merge PR #427 (macOS native loopback NFS) into the Runtime enum (Area…
jiangkoumo Oct 31, 2026
fccce8f
test(sandbox): leave Linux local_runtime tests to the Linux native br…
jiangkoumo Oct 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

48 changes: 44 additions & 4 deletions api/agent/src/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,16 +17,17 @@ export const BACKEND = /^(backend|runtime)\./;

/** Runs one operation once and returns its value or throws its refusal;
* `options` are the client's settings, whose `trace` selects its trace file;
* `transport` is the client's binding (default: the one a new client would use). */
export async function invoke(op, args, options, transport = binding()) {
* `transport` is the client's binding (default: the one a new client of
* `args.project` would use). */
export async function invoke(op, args, options, transport = binding(args.project)) {
return outcome(await traced(options, op, args, (scope) => {
if (BACKEND.test(op)) throw new LayerFSError("UnknownMethod", "backend and runtime methods are @layerfs/backend's and @layerfs/runtime's", null);
return transport.call(op, observed(args, scope));
}));
}

/** One call on the binding's backend table (`backend.*`, `runtime.*`). */
export async function invokeBackend(op, args, options, transport = binding()) {
export async function invokeBackend(op, args, options, transport = binding(args?.project)) {
return outcome(await traced(options, op, args, (scope) => transport.backendCall(op, observed(args, scope))));
}

Expand Down Expand Up @@ -60,9 +61,19 @@ export function sandboxArg(sandbox) {

/** Deployment settings from the client options, else the environment
* (LAYERFS_<NAME>). A Workspace's Sandbox needs
* every one but the container prefix and daemon host. */
* every one but the container prefix and daemon host. With `sandbox`
* (LAYERFS_SANDBOX) `native` (Linux or macOS), a Sandbox is a host daemon:
* Linux uses FUSE with a root daemon and nonroot commands (501:20);
* macOS uses loopback NFS and this user. It needs the daemon and
* `localDir` (LAYERFS_LOCAL_DIR) and no Docker setting. Unset or `docker` is
* Docker; any other value is refused. */
export function deployment(options = {}) {
const env = (name) => process.env[`LAYERFS_${name}`] || undefined;
const sandbox = options.sandbox ?? env("SANDBOX");
if (sandbox === "native") return nativeDeployment(options, env, sandbox);
if (sandbox !== undefined && sandbox !== "docker") {
throw new LayerFSError("InvalidInput", `deploy.sandbox (LAYERFS_SANDBOX) is docker or native (Linux or macOS), not ${sandbox}`, null);
}
const deploy = {
docker: options.docker ?? env("DOCKER") ?? "/var/run/docker.sock",
image: options.image ?? env("IMAGE"),
Expand All @@ -80,6 +91,35 @@ export function deployment(options = {}) {
return deploy;
}

/** A native Sandbox's settings: the daemon, the directory its Sandboxes
* live in, the optional name prefix, and the backend inside that directory
* (`sandboxBackend`, LAYERFS_SANDBOX_BACKEND: `native` when unset, or
* `namespaces` on Linux). The namespaces backend gives each Sandbox a root
* filesystem from an image, so it also needs `image` (LAYERFS_IMAGE, the
* same digest Docker deploys) and `imageArchive` (LAYERFS_IMAGE_ARCHIVE, the
* docker-archive the bundle carries). */
function nativeDeployment(options, env, sandbox) {
const deploy = {
sandbox,
daemon: options.daemon ?? env("DAEMON"),
localDir: options.localDir ?? env("LOCAL_DIR"),
containerPrefix: options.containerPrefix ?? env("CONTAINER_PREFIX"),
};
const backend = options.sandboxBackend ?? env("SANDBOX_BACKEND");
if (backend !== undefined) deploy.sandboxBackend = backend;
const required = [["daemon", "DAEMON"], ["localDir", "LOCAL_DIR"]];
if (backend === "namespaces") {
deploy.image = options.image ?? env("IMAGE");
deploy.imageArchive = options.imageArchive ?? env("IMAGE_ARCHIVE");
required.push(["image", "IMAGE"], ["imageArchive", "IMAGE_ARCHIVE"]);
}
for (const [name, setting] of required) {
if (!deploy[name]) throw new LayerFSError("InvalidInput", `deploy.${name} is not configured (LAYERFS_${setting})`, null);
}
if (!deploy.containerPrefix) delete deploy.containerPrefix;
return deploy;
}

/** Refuses every call on a closed client (or one no factory opened). */
export function live(client) {
if (client?.[STATE]?.closed !== false) {
Expand Down
19 changes: 16 additions & 3 deletions api/agent/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,15 @@ export class LayerFS {
* settings read LAYERFS_HOME, _DOCKER, _IMAGE, _VOLUME and _DAEMON; a
* Sandbox this client starts runs _IMAGE
* on _VOLUME. LAYERFS_CONTAINER_PREFIX (default `layerfs-`) names every
* container it starts `<prefix><project>-<n>`.
* container it starts `<prefix><project>-<n>`. On Linux or macOS,
* LAYERFS_SANDBOX=native starts each Sandbox as a host daemon in a directory
* under LAYERFS_LOCAL_DIR. Linux requires root for FUSE and runs commands
* as 501:20; macOS uses loopback NFS and the caller's identity. No Docker
* setting is read; native provides no isolation from the host.
* LAYERFS_SANDBOX_BACKEND picks the backend inside that directory: `native`
* (unset) or, on Linux, `namespaces` (namespaces, cgroups, nonroot commands).
* It is read for every call, so a Sandbox made with one backend is never
* reopened as another.
* LAYERFS_DAEMON_HOST is the host this process
* dials for a daemon's port, published on the Docker host's 127.0.0.1 only;
* set it when the client runs in a container (`host.docker.internal` on
Expand Down Expand Up @@ -129,13 +137,18 @@ export class LayerFS {
volume: env("VOLUME"),
daemon: env("DAEMON"),
...(env("CONTAINER_PREFIX") ? { containerPrefix: env("CONTAINER_PREFIX") } : {}),
...(env("SANDBOX") ? { sandbox: env("SANDBOX") } : {}),
...(env("SANDBOX_BACKEND") ? { sandboxBackend: env("SANDBOX_BACKEND") } : {}),
...(env("LOCAL_DIR") ? { localDir: env("LOCAL_DIR") } : {}),
...(env("IMAGE_ARCHIVE") ? { imageArchive: env("IMAGE_ARCHIVE") } : {}),
...(env("DAEMON_HOST") ? { daemonHost: env("DAEMON_HOST") } : {}),
},
storePolicy: env("STORE_POLICY"),
storePath: env("STORE_PATH"),
};
// not open until a factory has opened its Store; the binding is chosen now, once
this[STATE] = { closed: null, transport: binding() };
// not open until a factory has opened its Store; the binding is chosen now, once,
// for this client's project (a VM placement has one worker per project)
this[STATE] = { closed: null, transport: binding(this.projectName) };
this.fs = bind(this, fsMethods);
this.project = bind(this, { list_branches, list_sandboxes });
this.branch = bind(this, { list_commits, diff_commits, diff_branches, fork, merge, publish });
Expand Down
143 changes: 143 additions & 0 deletions api/agent/test/native_deployment.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
// Which runtime a Sandbox uses. No real addon, Store or Docker (the
// stand-in binding answers the client's calls):
// node api/agent/test/native_deployment.mjs
// LAYERFS_SANDBOX unset or `docker` gives exactly the Docker deployment it
// always gave; `native` (macOS) needs only the daemon and LAYERFS_LOCAL_DIR
// (no Docker setting); any other value, the former `local` included, is
// refused rather than taken as either.
import assert from "node:assert/strict";
import { createRequire } from "node:module";
import { fileURLToPath } from "node:url";
import { deployment } from "../src/client.js";

const FAKE = fileURLToPath(new URL("./fixture/fake-native.cjs", import.meta.url));
const native = createRequire(import.meta.url)(FAKE);

const clear = () => {
for (const name of Object.keys(process.env)) {
if (name.startsWith("LAYERFS_") || name.startsWith("DSH_LAYERFS_")) delete process.env[name];
}
process.env.LAYERFS_NATIVE = FAKE;
};
const docker = { docker: "/run/docker.sock", image: `sha256:${"a".repeat(64)}`, volume: "lfs-v", daemon: "/opt/layerfs-daemon" };
const setDocker = () => {
process.env.LAYERFS_DOCKER = docker.docker;
process.env.LAYERFS_IMAGE = docker.image;
process.env.LAYERFS_VOLUME = docker.volume;
process.env.LAYERFS_DAEMON = docker.daemon;
};
const store = () => {
process.env.LAYERFS_HOME = "/private/local-home";
process.env.LAYERFS_STORE_PATH = "/fake/store.sqlite";
};

// unset: the Docker deployment, field for field
clear();
setDocker();
assert.deepEqual(deployment(), docker);
// `docker` is the same deployment
process.env.LAYERFS_SANDBOX = "docker";
assert.deepEqual(deployment(), docker);
// the Docker settings are still required there
delete process.env.LAYERFS_IMAGE;
assert.throws(() => deployment(), (e) => e.code === "InvalidInput" && /LAYERFS_IMAGE/.test(e.message));

// native: daemon + localDir, no Docker setting read or required
clear();
process.env.LAYERFS_SANDBOX = "native";
process.env.LAYERFS_DAEMON = "/opt/layerfs-daemon";
process.env.LAYERFS_LOCAL_DIR = "/Users/me/lfs";
assert.deepEqual(deployment(), { sandbox: "native", daemon: "/opt/layerfs-daemon", localDir: "/Users/me/lfs" });
// an image (sandbox.create's option) does not change a native deployment
assert.deepEqual(deployment({ image: docker.image }), { sandbox: "native", daemon: "/opt/layerfs-daemon", localDir: "/Users/me/lfs" });
// the backend inside the directory is native unless named; namespaces is
// passed through (the binding refuses it off Linux, and an unknown value)
assert.equal(deployment().sandboxBackend, undefined);
const archive = `/bundle/images/${"a".repeat(64)}.tar`;
process.env.LAYERFS_SANDBOX_BACKEND = "namespaces";
process.env.LAYERFS_IMAGE = docker.image;
process.env.LAYERFS_IMAGE_ARCHIVE = archive;
// a namespaces Sandbox's root is an image: the same digest Docker deploys
// and the docker-archive the bundle carries
assert.deepEqual(deployment(), {
sandbox: "native",
daemon: "/opt/layerfs-daemon",
localDir: "/Users/me/lfs",
sandboxBackend: "namespaces",
image: docker.image,
imageArchive: archive,
});
// each of those is named when it is missing
delete process.env.LAYERFS_IMAGE;
assert.throws(() => deployment(), (e) => e.code === "InvalidInput" && /\(LAYERFS_IMAGE\)/.test(e.message));
process.env.LAYERFS_IMAGE = docker.image;
delete process.env.LAYERFS_IMAGE_ARCHIVE;
assert.throws(() => deployment(), (e) => e.code === "InvalidInput" && /\(LAYERFS_IMAGE_ARCHIVE\)/.test(e.message));
process.env.LAYERFS_IMAGE_ARCHIVE = archive;
// the native backend inside the same directory reads neither of them
assert.deepEqual(deployment({ sandboxBackend: "native" }), {
sandbox: "native",
daemon: "/opt/layerfs-daemon",
localDir: "/Users/me/lfs",
sandboxBackend: "native",
});
delete process.env.LAYERFS_SANDBOX_BACKEND;
delete process.env.LAYERFS_IMAGE;
delete process.env.LAYERFS_IMAGE_ARCHIVE;
// the container prefix names native Sandbox directories too
process.env.LAYERFS_CONTAINER_PREFIX = "ci-";
assert.equal(deployment().containerPrefix, "ci-");
delete process.env.LAYERFS_CONTAINER_PREFIX;
// each required setting is named when missing
delete process.env.LAYERFS_LOCAL_DIR;
assert.throws(() => deployment(), (e) => e.code === "InvalidInput" && /LAYERFS_LOCAL_DIR/.test(e.message));
process.env.LAYERFS_LOCAL_DIR = "/Users/me/lfs";
delete process.env.LAYERFS_DAEMON;
assert.throws(() => deployment(), (e) => e.code === "InvalidInput" && /LAYERFS_DAEMON/.test(e.message));
process.env.LAYERFS_DAEMON = "/opt/layerfs-daemon";
// a DSH_LAYERFS_NFS_ name is not mapped (an integration maps its own names)
process.env.DSH_LAYERFS_NFS_MOUNT = "/bin/true";
deployment();
assert.equal(process.env.LAYERFS_NFS_MOUNT, undefined);
// since #428 the SDK reads LAYERFS_ names only: a DSH_LAYERFS_ twin selects nothing
clear();
setDocker();
process.env.DSH_LAYERFS_SANDBOX = "native";
process.env.DSH_LAYERFS_LOCAL_DIR = "/l";
assert.deepEqual(deployment(), docker);

// anything else, the former `local` included, is refused, never silently Docker
for (const typo of ["local", "Native", "nativ", "NATIVE", "podman"]) {
clear();
setDocker();
process.env.LAYERFS_SANDBOX = typo;
assert.throws(() => deployment(), (e) => e.code === "InvalidInput" && e.message.includes(`not ${typo}`));
}

// a client reads LAYERFS_SANDBOX and LAYERFS_LOCAL_DIR once, and
// sandbox.create sends the native deployment (no Docker setting)
clear();
store();
process.env.LAYERFS_SANDBOX = "native";
process.env.LAYERFS_DAEMON = "/opt/layerfs-daemon";
process.env.LAYERFS_LOCAL_DIR = "/Users/me/lfs";
const { LayerFS } = await import("../src/index.js");
const client = await LayerFS.open("website");
assert.equal(client.options.deploy.sandbox, "native");
assert.equal(client.options.deploy.localDir, "/Users/me/lfs");
const before = native.deploys.length;
await client.sandbox.create();
assert.deepEqual(native.deploys.slice(before), [{ sandbox: "native", daemon: "/opt/layerfs-daemon", localDir: "/Users/me/lfs" }]);
// an image given to sandbox.create does not turn it into a Docker deployment
await client.sandbox.create({ image: docker.image });
assert.deepEqual(native.deploys.at(-1), { sandbox: "native", daemon: "/opt/layerfs-daemon", localDir: "/Users/me/lfs" });
await client.close();
// unset: the client carries no new key
clear();
store();
setDocker();
const plain = await LayerFS.open("website");
assert.equal("sandbox" in plain.options.deploy, false);
assert.equal("localDir" in plain.options.deploy, false);
await plain.close();
console.log("native_deployment: ok");
Loading