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:
59
docs/apps.md
59
docs/apps.md
@@ -1,12 +1,12 @@
|
||||
# Apps, manifests and the permission manager
|
||||
|
||||
How a program gets its authority. The kernel only knows capabilities; this
|
||||
is the userspace layer that decides who holds which, on the owner's behalf
|
||||
is the userspace layer that decides who holds which, on the user's behalf
|
||||
(see the vision's Capability Management and App Manifest notes).
|
||||
|
||||
## Apps
|
||||
|
||||
Every program the owner runs is an **app**: a manifest plus a binary.
|
||||
Every program a user runs is an **app**: a manifest plus a binary.
|
||||
An app is identified by its **id** (`kos.snake`), never by a file name or a
|
||||
process id. The id is what decisions, the app's data folder and (later) its
|
||||
encryption keys hang off.
|
||||
@@ -18,12 +18,12 @@ starts them from its own table, with exactly the capabilities listed there.
|
||||
|
||||
An app's capabilities have exactly two sources:
|
||||
|
||||
1. **Its manifest and the owner's consent.** Services the app declared
|
||||
1. **Its manifest and the user's consent.** Services the app declared
|
||||
(`display`, `storage`, …), granted only by the permission manager, only
|
||||
as far as the owner approved.
|
||||
as far as the user approved.
|
||||
2. **Its launcher.** Whoever starts an app may pass along what it already
|
||||
holds: the terminal as its output, a read-only grant to the current
|
||||
folder, a file the owner picked. A launcher can only give what it has.
|
||||
folder, a file the user picked. A launcher can only give what it has.
|
||||
|
||||
`cat` declares nothing and gets only what the shell passes it. Snake
|
||||
declares `display`. Nothing gets anything "just in case".
|
||||
@@ -31,22 +31,27 @@ declares `display`. Nothing gets anything "just in case".
|
||||
## Disk layout
|
||||
|
||||
```
|
||||
/home/ the owner's files; the shell's `/` (encrypted)
|
||||
/apps/<id>/ an installed app: manifest.toml and its binary
|
||||
/data/<id>/ the app's private folder (its `storage`) (encrypted, its own key)
|
||||
/system/permissions/ the owner's decisions, one file per app id (encrypted)
|
||||
/bin/ system servers and the shell (init starts these)
|
||||
/factory/ plaintext starter content the encrypted areas are
|
||||
seeded from with a new master key
|
||||
/apps/<id>/ a system app: manifest.toml and its binary, shared by every user
|
||||
/users/<user>/ one user's folder (encrypted, under their own key)
|
||||
home/ their files; the shell's `/`
|
||||
apps/<id>/ an app they installed
|
||||
data/<id>/ an app's private folder (its `storage`, its own key)
|
||||
permissions/<id> their decisions, one file per app id
|
||||
/bin/ system servers and the shell (init starts these)
|
||||
/factory/ plaintext starter content a user's folder is
|
||||
seeded from (home/, permissions/, data/)
|
||||
```
|
||||
|
||||
Only the permission manager reaches `/apps`, `/data` and `/system`. The
|
||||
shell's storage grant is `/home`, so neither it nor anything it runs can
|
||||
read another app's data or rewrite a decision.
|
||||
Users each decide for themselves, even about the system's apps, and what
|
||||
one installs the others do not see. An app id a system app has cannot be
|
||||
installed. Only the signed-in user's folder opens. Only the permission
|
||||
manager reaches their `apps/`, `data/` and `permissions/` (and `/apps`,
|
||||
read-only). The shell's storage grant is their `home/`, so neither it nor
|
||||
anything it runs can read an app's data or rewrite a decision.
|
||||
|
||||
## Manifest
|
||||
|
||||
`/apps/<id>/manifest.toml`, a strict subset of TOML:
|
||||
`apps/<id>/manifest.toml`, a strict subset of TOML:
|
||||
|
||||
```toml
|
||||
[app]
|
||||
@@ -60,7 +65,7 @@ icon = "icon.png" # optional: a file in the app's folder
|
||||
|
||||
[capabilities]
|
||||
required = ["display"] # cannot work without; denied = not installed
|
||||
optional = [] # works without; the owner decides each
|
||||
optional = [] # works without; the user decides each
|
||||
|
||||
[appearance]
|
||||
accent = "#e91e63" # optional: checked for format here, contrast by K-UI
|
||||
@@ -79,29 +84,29 @@ optional one is simply not granted.
|
||||
| Capability | What the app gets |
|
||||
|---|---|
|
||||
| `display` | the display server: the screen, and the keyboard while in focus |
|
||||
| `storage` | `/data/<id>/`, read-write (its own folder only) |
|
||||
| `storage` | the user's `data/<id>/`, read-write (its own folder only) |
|
||||
|
||||
## Decisions
|
||||
|
||||
`/system/permissions/<id>`, the same TOML subset:
|
||||
The user's `permissions/<id>`, the same TOML subset:
|
||||
|
||||
```toml
|
||||
[decision]
|
||||
granted = ["display"]
|
||||
```
|
||||
|
||||
Written by the permission manager when the owner decides; apps on the
|
||||
Written by the permission manager when the user decides; apps on the
|
||||
system image ship with their decisions already recorded under
|
||||
`factory/system/` (pre-approved by whoever built the image), and init
|
||||
seeds them into the encrypted `system/` on a fresh setup. Changing one later is the owner's call, through
|
||||
the permission manager.
|
||||
`factory/permissions/` (pre-approved by whoever built the image), and init
|
||||
seeds them into each new user's `permissions/`. Changing one later is that
|
||||
user's call, through the permission manager.
|
||||
|
||||
## Permission manager
|
||||
|
||||
A system server with no hardware. Its logic (checking manifests, deciding
|
||||
what an app gets, keeping decisions) is pure and generic over two ports:
|
||||
|
||||
- `Consent`: asks the owner. Today: a yes/no prompt on the terminal.
|
||||
- `Consent`: asks the user. Today: a yes/no prompt on the terminal.
|
||||
- `Store`: reads manifests and decisions, writes decisions, installs files.
|
||||
Today: the storage server.
|
||||
|
||||
@@ -113,9 +118,9 @@ From a folder the launcher passes as a read-only grant:
|
||||
|
||||
1. Read and check its `manifest.toml`; refuse if the id is installed.
|
||||
2. Required capabilities this device lacks: refuse.
|
||||
3. Ask the owner about the required ones together (no = not installed),
|
||||
3. Ask the user about the required ones together (no = not installed),
|
||||
then each optional one.
|
||||
4. Copy the binary and manifest to `/apps/<id>/`, create `/data/<id>/` if
|
||||
4. Copy the binary and manifest to the user's `apps/<id>/`, create `data/<id>/` if
|
||||
`storage` was granted, record the decision.
|
||||
|
||||
### Launch
|
||||
@@ -136,7 +141,7 @@ permission manager:
|
||||
|
||||
### Revoke
|
||||
|
||||
The owner withdraws a capability: the decision changes and the next launch
|
||||
The user withdraws a capability: the decision changes and the next launch
|
||||
gets less. A running app that held it is **stopped**: all its access dies
|
||||
at once. (Endpoint capabilities like `display` are copies the permission
|
||||
manager cannot take back from one app alone yet; once servers issue
|
||||
|
||||
49
docs/ipc.md
49
docs/ipc.md
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user