# 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 (see the vision's Capability Management and App Manifest notes). ## Apps Every program the owner 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 owner's consent.** Services the app declared (`display`, `storage`, …), granted only by the permission manager, only as far as the owner 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. `cat` declares nothing and gets only what the shell passes it. Snake declares `display`. Nothing gets anything "just in case". ## Disk layout ``` /home/ the owner's files; the shell's `/` (encrypted) /apps// an installed app: manifest.toml and its binary /data// 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 ``` 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. ## Manifest `/apps//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 [capabilities] required = ["display"] # cannot work without; denied = not installed optional = [] # works without; the owner 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` | the display server: the screen, and the keyboard while in focus | | `storage` | `/data//`, read-write (its own folder only) | ## Decisions `/system/permissions/`, the same TOML subset: ```toml [decision] granted = ["display"] ``` Written by the permission manager when the owner 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. ## 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. - `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 owner about the required ones together (no = not installed), then each optional one. 4. Copy the binary and manifest to `/apps//`, create `/data//` 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. ### Revoke The owner 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 per-client badges, as storage does, revoking a single capability from a running app will not need to stop it.) ### 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.