176 lines
7.4 KiB
Markdown
176 lines
7.4 KiB
Markdown
# 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 user's behalf
|
|
(see the vision's Capability Management and App Manifest notes).
|
|
|
|
## Apps
|
|
|
|
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.
|
|
|
|
System servers (log, display, terminal, storage, …) are not apps: init
|
|
starts them from its own table, with exactly the capabilities listed there.
|
|
|
|
## Where authority comes from
|
|
|
|
An app's capabilities have exactly two sources:
|
|
|
|
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 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 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".
|
|
|
|
## Disk layout
|
|
|
|
```
|
|
/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/)
|
|
```
|
|
|
|
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:
|
|
|
|
```toml
|
|
[app]
|
|
id = "kos.snake" # required: lowercase reverse-DNS
|
|
name = "Snake" # required
|
|
version = "1.0.0" # required: major.minor.patch
|
|
binary = "snake" # required: a file in the app's folder
|
|
command = "snake" # optional: what the shell runs it as (default: binary)
|
|
author = "K-OS" # optional
|
|
icon = "icon.png" # optional: a file in the app's folder
|
|
memory = "3 MiB" # optional: how much it may use, KiB or MiB,
|
|
# 64 KiB to 16 MiB (default 512 KiB)
|
|
|
|
[capabilities]
|
|
required = ["display"] # cannot work without; denied = not installed
|
|
optional = [] # works without; the user decides each
|
|
|
|
[appearance]
|
|
accent = "#e91e63" # optional: checked for format here, contrast by K-UI
|
|
```
|
|
|
|
The subset: `[table]` headers, `key = "string"`, `key = ["a", "b"]` on one
|
|
line, `#` comments. Strings hold printable ASCII without `"` or `\`. It is
|
|
strict: an unknown table, key or capability name, a duplicate, or a
|
|
missing required key rejects the manifest. Nothing is ignored silently.
|
|
|
|
Capability names come from the server catalog (`display`, `storage`,
|
|
`network`, `telephony`, …). A name is valid even if this device lacks the
|
|
server; a required one it lacks means the app cannot be installed here, an
|
|
optional one is simply not granted.
|
|
|
|
| Capability | What the app gets |
|
|
|---|---|
|
|
| `display` | a window of its own, named from its manifest: its pixels, and the keyboard while it is in front |
|
|
| `storage` | the user's `data/<id>/`, read-write (its own folder only) |
|
|
| `permissions` | a management copy of the permission manager's endpoint (below) |
|
|
| `session` | init's session copy: who is signed in (`WHO`), and signing out |
|
|
|
|
`permissions` and `session` are **system only**: only an app on the system
|
|
image (with a factory decision) has them. Installing an app that asks for
|
|
either, required or optional, is refused (`SYSTEM_ONLY`). Settings
|
|
(`kos.settings`) is the app that holds them.
|
|
|
|
## Decisions
|
|
|
|
The user's `permissions/<id>`, the same TOML subset:
|
|
|
|
```toml
|
|
[decision]
|
|
granted = ["display"]
|
|
```
|
|
|
|
Written by the permission manager when the user decides; apps on the
|
|
system image ship with their decisions already recorded under
|
|
`factory/permissions/` (pre-approved by whoever built the image), and init
|
|
seeds them into each new user's `permissions/`; a system app added to the
|
|
image later is seeded into existing users' at their next sign-in (decisions
|
|
already there are left alone). 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 user. Today: a yes/no prompt on the terminal.
|
|
- `Store`: reads manifests and decisions, writes decisions, installs files.
|
|
Today: the storage server.
|
|
|
|
Both have mocks; the logic is tested on the host with `cargo test`.
|
|
|
|
### Install
|
|
|
|
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 user about the required ones together (no = not installed),
|
|
then each optional one.
|
|
4. Copy the binary and manifest to the user's `apps/<id>/`, create `data/<id>/` if
|
|
`storage` was granted, record the decision.
|
|
|
|
### Launch
|
|
|
|
The launcher names an app (by command or id), passes its arguments and the
|
|
capabilities it wants to pass along (output, current folder). The
|
|
permission manager:
|
|
|
|
1. Looks the app up and reads its decision.
|
|
2. Gives init a read-only grant to the binary; init loads it from that
|
|
grant into a new, unstarted process (init needs no path, and nothing is
|
|
spawned that its requester could not read).
|
|
3. Gives the process the granted services and what the launcher passed,
|
|
writes its arguments page, starts it.
|
|
4. Watches it die, then releases its storage grants.
|
|
5. Replies with a handle (the process capability, call-only) the launcher
|
|
can wait on and kill.
|
|
|
|
### Managing
|
|
|
|
`APPS` lists every installed app, a line each: `id`, `name`, then the
|
|
required, optional and granted capabilities as bit masks, tab separated.
|
|
An app granted `permissions` gets a copy of the permission manager's
|
|
endpoint badged `MANAGE_BADGE`: it may only `LIST`, `APPS`, `ALLOW` and
|
|
`REVOKE`, never install or launch. The copy is revoked when the app dies.
|
|
|
|
### Revoke
|
|
|
|
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. (Capabilities like `display` could now be taken back alone, as
|
|
storage's grants and the display's windows are issued per app; stopping
|
|
the app is still what revoking does.)
|
|
|
|
### Not yet
|
|
|
|
- A trusted path for the consent prompt: today an app could draw a fake
|
|
prompt on the terminal (it cannot answer one; apps cannot type).
|
|
- Suspicious-combination warnings, simple/granular consent, per-host or
|
|
other server-side scopes.
|
|
- Signing, updates, uninstall.
|