@ai-ecoverse/slicc-kernel 1.72.0 → 1.73.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -43,7 +43,7 @@ const { status, stdout, stderr } = await kernel.run(['bash', '-c', 'echo hi > he
43
43
  | `sealed` | none | `(req, { signal }) => Promise<object>`: answers requests written to [`/dev/sealed`](#devsealed). `createNodeKernel` takes it too |
44
44
  | `approve` | none | `(req, { signal }) => 'once' \| 'always' \| 'deny'` (or a promise of one): decides a `sudo` request that needs approval (see [Sudo](#sudo)); without it, such a request is denied. `createNodeKernel` takes it too |
45
45
  | `cdp` | none | `({ runtime }) => Promise<CdpConnection>`: a browser-level DevTools connection for programs that drive a browser (see [Browser automation](#browser-automation-cdp)) |
46
- | `protect` | none | `{ paths }`: directories no process may change, root included, in every `users` mode (see [Protected paths](#protected-paths)). `createNodeKernel` takes it too |
46
+ | `protect` | none | `{ paths, writers? }`: directories no process may change, root included, in every `users` mode, except processes with one of the `writers` tags (see [Protected paths](#protected-paths)). `createNodeKernel` takes it too |
47
47
 
48
48
  ### `kernel.run(argv, options?) → Promise<{ status, stdout, stderr }>`
49
49
 
@@ -632,10 +632,10 @@ A character device (`0666`, major 10, minor 250) through which a program talks t
632
632
  - `/` itself, which holds all of these.
633
633
  Paths in the layout the kernel stamps with root's owner at boot are skipped when protected.
634
634
  - **Symlinks as paths:** a path that is itself a symlink protects the directory it names too, by its real path, once the kernel has prepared.
635
- - **Mounts:** nothing can be mounted at, under or above a path, not even by the embedder (`kernel.mount`, `/etc/fstab`). So a path always lies on the root file system, where that check holds.
635
+ - **Mounts:** nothing can be mounted at, under or above a path, not even by the embedder (`kernel.mount`, `/etc/fstab`), and not by a writer. Above includes `/`: with any path protected, nothing can be mounted on `/`. So a path always lies on the root file system, where that check holds.
636
636
  - **Clients:** a client's (`kernel.connect`) file calls, mounts and unmounts see the paths read-only, as its processes do.
637
637
  - **Not affected:** the page's own writes to OPFS.
638
- - **Writers:** a `writer` that lets chosen processes change the paths needs the kernel-side process tag ([#405](https://github.com/ai-ecoverse/slicc-kernel/pull/405)). Until then, `protect.writer` is refused.
638
+ - **Writers:** `protect: { paths, writers: ['bios-installer'] }` names process tags whose processes may change the paths, such as the embedder's own installs (`kernel.run(['pnpm', 'install'], { tag: 'bios-installer' })`). A tag matches exactly, with no prefix or case folding. A process gets its tag only from the embedder, through `kernel.run`, `kernel.openTerminal` or `kernel.connect({ tag })`, and passes it to its children, forks and `exec`s; a program or a client can't choose one. Writers work on their own view of the root file system, so a file a writer changes is seen by other processes as between two tabs: close-to-open, at their next open after the writer closes it.
639
639
  - **Where it is a boundary:** it's one in a browser, where programs' own code can't reach OPFS around the kernel (see [JS programs](#js-programs)). Under the Node entry it isn't, until [#407](https://github.com/ai-ecoverse/slicc-kernel/issues/407), and there `kernel.writeFile` under a path fails too, so write those files into `root` before creating the kernel.
640
640
 
641
641
  ### `/proc`
package/dist/index.js CHANGED
@@ -288,7 +288,17 @@ async function loopbackFetch(dial, input, options) {
288
288
  }
289
289
 
290
290
  // src/fs/protect.ts
291
- var PROTECT_WRITER = "slicc-kernel: protect.writer needs the process tag (#405), which this kernel does not have yet";
291
+ function protectInit(protection) {
292
+ const writers = [...writerTags(protection)];
293
+ return { paths: [...protection.paths], ...writers.length > 0 ? { writers } : {} };
294
+ }
295
+ function writerTags(protection) {
296
+ const writers = protection?.writers ?? [];
297
+ if (!Array.isArray(writers) || writers.some((tag) => typeof tag !== "string" || tag === "")) {
298
+ throw new TypeError("protect.writers must be a list of process tags");
299
+ }
300
+ return new Set(writers);
301
+ }
292
302
 
293
303
  // src/kernel/net/loopback-names.ts
294
304
  var HOST_LOOPBACK = "host.slicc.internal";
@@ -1845,7 +1855,7 @@ function initCall(options, transport) {
1845
1855
  ...options.onDenial ? { denials: true } : {},
1846
1856
  ...options.sealed ? { sealed: true } : {},
1847
1857
  ...options.approve ? { approve: true } : {},
1848
- ...options.protect ? { protect: { paths: [...options.protect.paths] } } : {},
1858
+ ...options.protect ? { protect: protectInit(options.protect) } : {},
1849
1859
  ...options.processMounts !== void 0 ? {
1850
1860
  processMounts: typeof options.processMounts === "function" ? "ask" : options.processMounts
1851
1861
  } : {}
@@ -1962,7 +1972,7 @@ function checkOptions(options) {
1962
1972
  }
1963
1973
  async function createKernel(options = {}) {
1964
1974
  checkOptions(options);
1965
- if (options.protect?.writer) throw new Error(PROTECT_WRITER);
1975
+ writerTags(options.protect);
1966
1976
  const url = options.worker ?? new URL("./kernel-worker.js", import.meta.url);
1967
1977
  const worker = new Worker(url, { type: "module", name: "slicc-kernel" });
1968
1978
  const pending = /* @__PURE__ */ new Map();