feat(users): per-user sealed folders users/<id>/{home,apps,data,permissions}; storage SET_USER/LOCK/PURGE; init sign-in/sign-out loop (purge fresh/removed/guest, seed from factory, session start/stop); shell logout; pm user+system app shelves; terminal SIGNED_OUT; per-program stack (gatekeeper 12p); 128K load scratch

This commit is contained in:
2026-10-28 01:08:50 +01:00
parent 29a25bd8c2
commit 91f7d28834
38 changed files with 1156 additions and 378 deletions

View File

@@ -91,22 +91,37 @@ and budget listed there. Storage, started before the log server, receives
its log endpoint from init later (`SET_LOG`, accepted only from the
unbadged holder of its endpoint).
Nothing that touches user data starts before the owner is authenticated.
Nothing that touches a user's files starts before someone has signed in.
Init starts the log, input, display and terminal servers, then the
gatekeeper (`types::gatekeeper`), handing it only the terminal, the keystore
(a small disk of its own, told from the data disk by its size) and the
virtio-rng device. Init calls `UNLOCK`, which returns once the owner is in,
then `KEY`, whose reply is the master key; init passes it to storage
(`SET_KEY`, from the unbadged holder only, each half once), and a fresh
32-byte seed (`SEED` → `SET_SEED`) for storage's randomness this boot.
Storage's encrypted areas (`home/`, `system/`, each app's `data/<id>/`;
crate `filecrypt`, layer `storage_server::encrypted`) open once both are
in. With a new key (a fresh device, or one just wiped: `UNLOCK`'s word 1)
init empties those areas and seeds them from the plaintext `factory/`;
an area that is missing is seeded too. Only then does init start the
permission manager and the desktop. If the gatekeeper cannot
unlock, init stops there. The gatekeeper exits, and its capabilities go
with it.
virtio-rng device. Init calls `UNLOCK`, which returns once someone is in:
`[OK, flags, user id, role]`, a guest (`GUEST`) or a new keystore
(`FRESH`) among the flags. Then `KEY` (their master key), `SEED` (32 fresh
random bytes for storage this sign-in), `NAME`, and `REMOVED` until `DONE`
(users the owner removed just now). The gatekeeper exits.
Each user's files are in `users/<id>/` (`types::user`: the id in hex, or
`guest`): `home/`, `permissions/`, and per app `apps/<id>/` and
`data/<id>/`, each sealed under its own key derived from that user's
master key (crate `filecrypt`, layer `storage_server::encrypted`); the rest
of the disk is plaintext. While storage is still locked, init removes what
must go (`PURGE`, init only, only in `users/`, only while locked): every
user's folder on a new keystore, a removed user's, the last guest's. Then
it tells storage who is in (`SET_USER`) and hands over the key and seed
(`SET_KEY`, `SET_SEED`, a half per call, each once a sign-in; init only).
Only the signed-in user's folder opens; anyone else's is refused. Init
seeds whatever area of theirs is missing from the plaintext `factory/`,
then starts the session: the permission manager (the user's folder, and
`apps/` read-only) and the desktop. If the gatekeeper cannot unlock, init
stops there.
Signing out (`types::spawn::SIGN_OUT`, sent one way through the copy of
init's endpoint the shell holds) stops everything the session ran (the
shell, the permission manager, every app), tells the terminal
(`SIGNED_OUT`, init only), locks storage (`LOCK`, init only: every grant
taken back, every file closed, the key forgotten), removes a guest's
files, and runs the gatekeeper again.
A program's `uses` endpoints arrive as badged copies with `Execute` and
`Delegate`: the badge (unique per copy) tells the server which client is
@@ -135,12 +150,12 @@ log endpoint is the terminal prints there with `println!`: standard output
is whatever text endpoint a program was given. `READ` (`types::terminal`)
blocks until a line is typed and returns it in chunks, the last ending in
`\n`. Ctrl+C there drops the line. Keys typed while no line is read are
kept for the next one, except Ctrl+C, which signals the notification its
first `ON_INTERRUPT` caller gave. `REDRAW` repaints the screen and takes
kept for the next one, except Ctrl+C, which signals the notification the
session's first `ON_INTERRUPT` caller (its shell) gave. `REDRAW` repaints the screen and takes
input focus back.
The desktop is the shell, given the terminal, the permission manager and
`/home` (its `/`). Everything the owner runs is an app (docs/apps.md): a
The desktop is the shell, given the terminal, the permission manager,
the user's `home/` (its `/`) and signing out. Everything the owner runs is an app (docs/apps.md): a
command that is not a built-in asks the permission manager (`types::apps`,
client `libkos::apps`) to run it, passing the terminal as its output and a
read-only grant to the current folder (`cd`, `pwd`). The permission manager