Privacy and sandbox
PurposeOS makes a few strong promises. Each one is a property of the design, and each one comes with its limits written down.
What stays on the device
Inference
All embeddings and completions go through one daemon,
inferd. It supervises llama.cpp on private Unix sockets. There is no TCP in the path. The OS itself can reach the network, for example to download models. The inference engine cannot.Microphone audio
Held in memory only: a few seconds of features for the wake word, up to 120 seconds for dictation. Never written to disk, never logged.
Text you dictate
Passed to the app you were dictating into. Services never log entry text. Error messages are fixed strings, and a test greps the real binary's output and files for it.
File search
The names index holds file names and paths only, with no file content. The scan log line has counts, never names.
The activity log
The executor records every command: time, id, action, outcome and which runtime handled it. It never records the input or output, and it never sees keystrokes. A planned mood and activity feature reads this log on your device. Nothing is sent anywhere.
- One file per UTC day, one encrypted line per event. Encryption is age (X25519 with ChaCha20-Poly1305).
- The daemon holds only your public key. A compromised daemon or plugin can append events but cannot read history.
- The private key is a 0600 file in your config folder. Losing it means losing the logs. Rotate by adding keys. New events use the new key and old ones stay readable.
- Not covered yet: someone with write access could delete or reorder whole lines, because there is no hash chain.
purpose-activity keygen # once. Back up activity.key
purpose-activity list
purpose-activity cat 2026-10-11 How apps are confined
- Landlock confines each app to its own tree, data folder, config and state, plus any folders in permissions.files.
- Apps reach the executor only through their own socket. Identity comes from the socket that accepted the connection, never from a field in the request.
- TCP is denied. no_new_privs is set. The environment is scrubbed down to HOME, PATH, LANG, XDG_* and PURPOSE_*.
- Where this stops: socket isolation between apps needs Landlock ABI 9 (Linux 7.0 or newer), UDP is not filtered, and there is no seccomp yet.
Every app and every service starts through purpose-launch. If Landlock is unavailable it refuses to start the program. There is no silent fallback to running unsandboxed.
| Path | Access |
|---|---|
/usr /lib /bin | read, execute |
apps/<id> | read only (bin/ executes) |
apps/<id>/data, config, state | read, write |
folders in permissions.files | read (ro) or read, write (rw) |
| its own executor socket | connect |
everything else, including $HOME | denied |
What it does not stop yet
Isolation between apps' sockets needs Landlock ABI 9 (Linux 7.0 or newer). On older kernels a hostile app could connect to another app's sockets, so per-app identity is not hostile-proof there. purpose-launch --require-socket-isolation refuses to start instead of warning.
- UDP and other non-TCP traffic is not filtered by Landlock. A seccomp filter would close this and is not built.
- No seccomp: all system calls remain available to a sandboxed app.
- Bundles are checked for integrity, not authorship. Signing is not implemented.
- The files index is plain SQLite on disk. Use an encrypted home directory.