Trust boundaries
A box is divided into zones with different trust. Each arrow between zones is a place where something is checked, or where it is not. This page names every crossing so you can see what protects it.
The zones
Section titled “The zones”| Zone | What is in it | Trust |
|---|---|---|
| Outside | The internet, the model provider, paid APIs, outside services such as Jira | Untrusted. Reached on purpose, for specific jobs. |
| The box's own screen | The portal on the box's display, and anything else that calls the API from the box itself | Trusted like the owner |
| Your devices | Phones and browsers paired with a one-time code | Trusted to read, start tasks, undo and answer approvals. Owner's actions only for a device paired as the owner. Anyone without a token is refused. |
| The core, as root | The box service, the vault, every app's code, the action log, the task proxy, SpacetimeDB on 127.0.0.1 |
Fully trusted. Holds every key. |
| Each task | One Linux user, its own copy of a workspace, its worker (Droid or a test worker) | Untrusted. Assumed to be wrong or hostile. |
The core trusts apps completely: an app's tools, routes, triggers and log readers run inside the core as root.
Crossings
Section titled “Crossings”Into a task, and out of it
Section titled “Into a task, and out of it”| From | To | What crosses | Who checks | Logged as |
|---|---|---|---|---|
| Task | Its broker (Unix socket) | Steps, tool calls, questions, costs | The socket is owned by the task's user, mode 0600. The core checks the tool is in the task's tools, applies the budget and keys the call. |
task.step, tool.call, tool.result, approval.asked, cost |
| Task | The internet | Network connections | The firewall sends task users only to the proxy. The proxy checks the task's hosts, resolves the name once, and refuses the box's own addresses and ports. |
Refusals as net.blocked. Allowed connections are not logged. |
| Droid task | The model provider | Model calls with a placeholder key | The proxy relays only Droid tasks, only to Factory's hosts, and puts the vault's key on the request | Token counts and cost as cost |
| Task | The vault, the log, other tasks' files, the base files | Nothing | File permissions: all root-only or owned by another user | Not logged. The task gets a permission error. |
| Task | The API, SpacetimeDB, other local services | Nothing | The firewall rejects the packet; the proxy refuses the box's own ports | The kernel log counts firewall rejects (prefix box-block). Proxy refusals are net.blocked. |
| Task | Other tasks' processes | Nothing | ProtectProc=invisible, separate users |
Not logged |
Inside the core
Section titled “Inside the core”| From | To | What crosses | Who checks | Logged as |
|---|---|---|---|---|
| A tool or model job | The vault | One key at a time | The core returns only keys the tool or job lists in secrets |
Not logged |
| An app's route or trigger | The vault | One key at a time | The core returns only keys the app lists in secrets |
Not logged |
| A tool | Outside APIs | Whatever the tool sends | The tool's own code. Tools are outside the task's network limits. | The call and its result as tool.call and tool.result |
| An app's code | Anything on the box | Anything | Nothing. Apps run as root. | Only what the app writes |
From your devices and outside services
Section titled “From your devices and outside services”| From | To | What crosses | Who checks | Logged as |
|---|---|---|---|---|
| The box's own screen | The HTTP API (port 7700, or the Unix socket) | Everything | The request comes from the box itself with no proxy header | task.created with its source, approval.answered with the by it sends, note for settings, setup, vault and apps |
| A paired device | The HTTP API | Reads, new tasks, answers, undo | A valid device token, as a header or ?access_token=. Without one, 401. |
approval.answered with the device's name, whatever the request says |
| A device paired as the owner | The HTTP API | Also pairing, settings, setup, the vault, apps, the owner, folders, Keep | The token's device was paired as the owner from the box's own screen. Other devices get 403. |
note events, keep events. A vault key's name, never its value. |
| Anyone else | The HTTP API | Only the health check, redeeming a pairing code and the portal's pages | Every other request gets 401 |
Not logged |
| The box's own screen | SpacetimeDB (127.0.0.1:3000) |
Live tasks and approvals; answers through the answer reducer |
It listens on loopback, and the firewall never opens port 3000. Only the box writes tasks and approvals, and answer works only from a connection the box vouched for. |
approval.answered with the vouched name |
| Outside services | App routes /t/<app>/... |
Whatever the app accepts | A paired device's token, unless the app marks the route open (openRoutes). Open routes must check their own secret: the Jira app's routes require a shared secret and check the approval belongs to the task; the Mana app's status route returns only a public summary. |
What the app writes, such as approval.answered by jira:<name> |
| Outside callers | Webhooks /hook/<app>/<name> |
A JSON payload that usually starts a task | A paired device's token, then the app's handler | task.created |
| The chat agent (ASI:One) | The HTTP API | The same actions as the box's own screen | It runs on the box as nobody and calls the API on 127.0.0.1, so the API treats it as the box's own screen. Only paired chat owners can give it work. |
task.created with source asi1, answers by asi1 |
Into the system
Section titled “Into the system”| From | To | What crosses | Who checks | Logged as |
|---|---|---|---|---|
| An update file | The idle system slot | A whole system image | box-update verifies the signature against /etc/box-os/update.pub and the hash, then checks the image holds the box |
/data/update/last and the serial console |
| The new system | The default slot | Becoming the default | box-confirm.service waits up to 5 minutes for a healthy GET /health |
/data/update/last |
Reading the diagram
Section titled “Reading the diagram”- Every path from a task to anything else goes through the broker or the proxy. Both run in the core and both write to the log.
- The core is the one zone with the keys. Protecting the box means protecting root and choosing apps carefully.
- The API trusts the box itself and paired devices. Everyone else is refused. SpacetimeDB stays on
127.0.0.1. The API speaks plain HTTP, so reach it from other devices over a network you control. See the threat model.
Related
Section titled “Related”GurtOS is open source under the Apache License 2.0.Source code on GitHub.