@ai-ecoverse/slicc-kernel 1.59.1 → 1.60.1

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
@@ -90,10 +90,29 @@ The user database, `/etc/passwd` and `/etc/group` (see [Users](#users)):
90
90
  | | |
91
91
  | --- | --- |
92
92
  | `mode` | the `users` option the kernel was created with |
93
- | `add({ name, home?, groups?, umask?, homeGroup? })` | adds a user and resolves with `{ name, uid, gid, home, shell, groups, umask? }`. It's idempotent: an existing name resolves with its entry, keeps its ids, groups and home, and only remembers `umask`, creates a missing pnpm home or `config.yaml` (an existing pnpm home keeps its mode with `users: 'off'`), and, with owners on, gives the home to the user again. The new user gets the lowest free id from 1000 up, a private group of the same id and name, membership of `users` and of each group in `groups` (such as `sudo`), `home` (default `/home/<name>`, created), its pnpm home `~/.local/share/pnpm` (`0700`, `0750` for a `scoop-` user) and `~/.config/pnpm/config.yaml` with `minimumReleaseAge: 1440` (pnpm skips releases less than a day old), each only if missing, which an `add` of an existing user also does, and `/bin/bash`. `umask` is the umask its processes start with; the kernel keeps it in memory, so pass it again after a reload. With owners on, every `add`, of a new or an existing user, also gives a home under `/home/<dir>` to the user and sets its pnpm home to `0700` (`0750` for a `scoop-` user) again (see [File owners](#file-owners)). `homeGroup` names the group the home belongs to instead of the user's own, without making the user a member, and its mode is then `2750`. A name must match `[a-z_][a-z0-9_.-]*`; a group that doesn't exist, in `groups` or `homeGroup`, rejects with `ENOENT` |
93
+ | `add({ name, home?, groups?, umask?, homeGroup?, homeMode? })` | adds a user and resolves with `{ name, uid, gid, home, shell, groups, umask? }`. It's idempotent: an existing name resolves with its entry, keeps its ids and home, joins the groups in `groups` it isn't in yet (it never leaves one), remembers `umask`, creates a missing pnpm home or `config.yaml` (an existing pnpm home keeps its mode with `users: 'off'`), and, with owners on, gives the home to the user again. The new user gets the lowest free id from 1000 up, a private group of the same id and name, membership of `users` and of each group in `groups` (such as `sudo`), `home` (default `/home/<name>`, created), its pnpm home `~/.local/share/pnpm` (`0700`, `0750` for a `scoop-` user) and `~/.config/pnpm/config.yaml` with `minimumReleaseAge: 1440` (pnpm skips releases less than a day old), each only if missing, which an `add` of an existing user also does, and `/bin/bash`. `umask` is the umask its processes start with; the kernel keeps it in memory, so pass it again after a reload. With owners on, every `add`, of a new or an existing user, also gives a home under `/home/<dir>` to the user and sets its pnpm home to `0700` (`0750` for a `scoop-` user) again (see [File owners](#file-owners)). `homeGroup` names the group the home belongs to instead of the user's own, without making the user a member (`groups` does that), and its mode is then `2750`. `homeMode` sets the home's mode instead (setgid included, so `0o2750` makes new files in it take the home's group); like the owner, it is set again by every `add` with owners on, on the home itself only. A name must match `[a-z_][a-z0-9_.-]*`; a group that doesn't exist, in `groups` or `homeGroup`, rejects with `ENOENT` |
94
94
  | `remove(name, { keepHome }?)` | removes the user, its private group and its group memberships, and deletes its home unless `keepHome` is set. A home outside `/home/<dir>` is never deleted. Resolves `false` for an unknown name; system accounts (ids below 1000 or above 60000) reject with `EPERM` |
95
95
  | `list()` | every user, as `add` returns them |
96
96
 
97
+ ### `kernel.groups`
98
+
99
+ The groups of `/etc/group`, on the page and the Node entry (not on attached clients):
100
+
101
+ | | |
102
+ | --- | --- |
103
+ | `list()` | every group as `{ name, gid, members }`, `members` being the users listed in it (a user's own group lists nobody) |
104
+ | `addMember(group, user)` | adds `user` to `group`'s members and resolves with the group; a member already in it changes nothing |
105
+ | `removeMember(group, user)` | removes `user` from `group`'s members, the same way |
106
+
107
+ An unknown group or user rejects with `ENOENT`, and a user's own (primary) group with `EINVAL`. Every user has an own group with its name and its uid as gid, which is how a cone shares with its scoops: the scoops join the cone's group. As on Linux, a change counts from the user's next process (and the next client connected for it): processes already running keep the groups they started with, and so does a client already connected. The embedder's recipe, which bios and slicc-agent share, run at every boot:
108
+
109
+ ```js
110
+ await kernel.users.add({ name: 'cone', groups: ['sudo'], homeMode: 0o710, umask: 0o077 });
111
+ await kernel.users.add({ name: 'scoop-site', groups: ['cone'], homeGroup: 'cone', homeMode: 0o2750, umask: 0o027 });
112
+ ```
113
+
114
+ The cone's home lets its group in only to pass through (`0710`), and the cone opens what it shares with `chmod g+r`. A scoop's home belongs to the cone's group, so the cone reads it, and new files there take that group. Change `/etc/passwd` and `/etc/group` through `kernel.users` and `kernel.groups`, which keep the two consistent and serialize their writes.
115
+
97
116
  ### `kernel.setRoutes({ prefixes, exit }) → Promise<void>`
98
117
 
99
118
  Replaces the [uplink](#uplink)'s route table: `prefixes` are addresses or CIDR prefixes (`100.64.0.0/10`, `fd7a:115c:a1e0::/48`), and `exit: true` routes every address, as an exit node does. A prefix that does not parse rejects the whole table and keeps the old one.
@@ -365,7 +384,7 @@ The proxy answers `200` with `Content-Type: application/vnd.slicc.raw-fetch`: a
365
384
 
366
385
  ## Filesystem
367
386
 
368
- `/` is the OPFS root. Each process mounts the top-level directories that exist when it starts (plus `/usr` and `/bin`), so files in them are shared by all processes and visible through the OPFS API as soon as the process that wrote them has closed them; `run` resolves only after that. `createKernel` creates `/tmp` and `/home` in OPFS, so they are shared too. Files directly in `/`, and directories created there after a process started, are OPFS as well, the same for Emscripten and WASI programs; only `/dev` and `/proc` live in each process's memory. File contents are buffered per open file and written back on close, `fsync` and exit; metadata operations (`mkdir`, `rename`, `rm`, …) go straight to OPFS. The kernel worker is the only writer.
387
+ `/` is the OPFS root. Each process mounts the top-level directories that exist when it starts (plus `/usr` and `/bin`), so files in them are shared by all processes and visible through the OPFS API as soon as the process that wrote them has closed them; `run` resolves only after that. `createKernel` creates `/tmp` and `/home` in OPFS, so they are shared too. Files directly in `/`, and directories created there after a process started, are OPFS as well, the same for Emscripten and WASI programs; only `/dev` and `/proc` live in each process's memory. An OPFS or IndexedDB failure that has no POSIX counterpart is `EIO`; its message names the DOM error and the operation (`lookup`, `read`, `write`, `commit` or `sidecar`), and the kernel worker logs the first of each kind to its console. File contents are buffered per open file and written back on close, `fsync` and exit; metadata operations (`mkdir`, `rename`, `rm`, …) go straight to OPFS. The kernel worker is the only writer.
369
388
 
370
389
  Directories are renamed with `FileSystemHandle.move()` where available, else by copy and delete.
371
390
 
package/dist/index.js CHANGED
@@ -1824,6 +1824,17 @@ function cdpBridge(hook) {
1824
1824
  function failed(message, code) {
1825
1825
  return Object.assign(new Error(message), code === void 0 ? {} : { code });
1826
1826
  }
1827
+ function kernelGroups(call) {
1828
+ const member = (op) => async (group, user) => await call({ op, group, user });
1829
+ return {
1830
+ list: async () => await call({ op: "groups-list" }),
1831
+ addMember: member("groups-add-member"),
1832
+ removeMember: member("groups-remove-member")
1833
+ };
1834
+ }
1835
+ function accounts(call, mode) {
1836
+ return { users: kernelUsers(call, mode), groups: kernelGroups(call) };
1837
+ }
1827
1838
  function kernelUsers(call, mode = "off") {
1828
1839
  return {
1829
1840
  mode,
@@ -1977,7 +1988,7 @@ async function createKernel(options = {}) {
1977
1988
  },
1978
1989
  openTerminal,
1979
1990
  connect: async (connectOptions = {}) => await call({ op: "connect", ...connectOptions }),
1980
- users: kernelUsers(call, options.users),
1991
+ ...accounts(call, options.users),
1981
1992
  setRoutes: async (routes) => void await call({ op: "routes", routes }),
1982
1993
  dial,
1983
1994
  loopbackFetch: (input, fetchOptions) => loopbackFetch(dial, input, fetchOptions),