youros

How it worksLetting others in

Access

A holder is the node that has what a call asks for. Every call reaches a holder through a session (network.md), so it arrives with a verified key, and the holder answers one question about it from its own copy of the log: may(key, class, resource, now), which says allow, ask or no. No server is asked, so it works offline (log/sync-before-serve).

Keys for everything that calls

The principals are the account key; your devices, Admin or Member (devices.md); and grantees, keys that hold grants but can never add, revoke or grant, or answer an ask: a service or an extension on a node, an MCP client, someone else's node. A grantee is never Added as a device, so no grant it holds can make it one.

A node makes a key for each service and each extension instance it runs, and for each MCP client added to it (mcp.md), and keeps them beside its own. A service never sees its key: it asks its node to call as itself, and the node signs. Inside the node, the local socket already says which service is calling. To another node, the call carries a chain from the calling node to the service's key, so the far holder checks the service, not the node it runs on. A crashed or broken-into service can call as itself only through its node, only within its grants, and can't take its key away.

Resources and classes

A resource is a key expression, <node key>/<service>/<path>, with @<version> where the service keeps versions, and a scope is one with * (one level) and ** (any depth): ab12…/files/home/sam/notes/**. A node's own state (its keys, its log, its settings) is not a resource of any service, so no grant can name it.

Every service ships a manifest: each verb, its class, which argument names the resource, and whether it can show a preview. The holder reads the manifest, not the call, to find the class and the resource, so a caller can't relabel a verb. A built-in service's manifest is built into the node; an extension's is the one shown when it was installed (sandbox/declared).

class for example
read list, stat, read a file changes nothing
append add a new file under a folder adds, never changes
write write, move, remove a file; merge a fork changes what's there
run open a terminal, start a process as much as the shell it gets
reach dial a host from a box leaves the node; the sandbox's gate (sandbox.md)

A path is checked twice. The holder checks it as written against the scope, and refuses early. The service then gets the call with the granted roots beside it and confines the path to them after following symlinks, as files confines to its one root now. The holder knows authority and not the filesystem; the service, the other way round, so both checks stay.

run is stated for what it is. A terminal as your user can read your keys, change anything and dial anywhere; no scope round it changes that. Your own devices may have one. A grantee gets a terminal only inside a box (sandbox.md), where its folders, its processes and its reach are what the box was made with.

Grants with fields an older holder can't read come in a new kind of log entry, not as extra bytes on an old one: an unknown kind is refused, and a refusal is safe where a partial reading is not.

Ask is in the grant

A grant is {subject, scope, classes, ask, expiry} in the log (D-12, D-30). ask says a person answers each call it covers. Changing your mind is an ungrant and a new grant, made from the same settings page. A key with no grant that covers a call gets no, so a client added for reading can't inherit a yes given to another.

A session keeps the grants it is relying on, and re-checks each when it expires and whenever the log changes under it, so an ungrant that arrives mid-session refuses the next call, not the next session.

An ask, from call to answer

  1. The holder parks the call and makes an ask: an id, the caller's chain, the verb, the hash of its exact arguments and a preview. The service makes the preview (a write's diff, a merge's changes), since it knows content and not authority.
  2. The ask is sealed to each Admin device's mailbox at the relay (D-18) and a closed browser is woken by Web Push (D-19). The caller hears pending and the ask's id.
  3. The shell shows the ask in the top layer, where no pane can draw (contain/asks-outside): who is asking, by its chain; what, and the preview.
  4. A person answers on any of your devices, which signs the answer with its key, bound to the ask's hash:
    • once: this call;
    • for this task: a chain from the device to the caller, scoped to what the ask showed, ending with the task or after an hour;
    • always: a grant in the log.
  5. The holder checks the answer against the log (an Admin device, not the caller, hash matching) and runs the call, or refuses it.

So an answer is a grant: one call long, a task long, or until taken back. Nothing new holds authority.

In person. A script in a tab can sign as the tab (core.md, Who sees what), so an answer that widens what a key may do (always, a new grant, pairing, a terminal outside a box) carries a passkey assertion over the ask's hash, with user verification. An agent driving a browser can't make one.

Fewer asks by shape. An ask every call is one nobody reads. Grants are narrow enough that most calls are plain allow; an agent's edits land on a fork and merging it is the one ask, with the whole diff (ai/fork-approved). A checker may sort asks, deny one or raise it, but never answers yes: only a person does.

Nodes and services calling each other

A machine joins as a Member, and a Member holds no grants until one is made, so a machine reaches another of yours only for what was granted, the same way any grantee does. An Admin device may do anything; that's what an Admin is, and why a machine isn't one.

A service calling another service on its node is a call from its key, checked by the node like a call from outside. A built-in service is granted what its job needs when it's installed, shown in settings; an extension, its manifest at most (sandbox/declared).

Nodes that face outward

A grantee's manifest says who it serves: nobody else, the node's own OS user (mcp.md), anyone signed in to it, or the internet. Nothing checks that past the node; the grant page shows it, so a grant reads as what it is. A node facing outward holds a copy of what it serves (files/hold-some) rather than calling through to a machine that may be asleep, and where it is hosted can read that copy. Its grants are allow or nothing: a stranger's request must never become an ask on your phone. Publishing is moving the ref it was granted, a write that can ask. A name it passes along for its caller (an OAuth user) is audited as its claim, never as a key. A node may instead give its callers keys of their own, by chains, when you want grants to tell them apart, as MCP does for each agent.

The audit

The holder writes each call it answered (allowed, asked, answered or refused) with the caller's chain, the verb and the resource. The audit is a doc of the node's, synced sealed to your devices like the sandbox's (sandbox/audit-reaches-you), so any device can read what any caller did anywhere. What a caller reads, it keeps; the audit says what it saw, and a revoke decides what happens next.

Where it breaks

  • A holder that's behind answers by the log it has. Short chains, log heads in every session and the mailbox drained first narrow that gap (devices.md, Revocation); log/freshness closes it at a price.
  • The holder is trusted for its own data. A broken-into machine can hand out what it holds; grants decide who else may ask it, not what it does with its own.
  • Same-user processes. A program running as the node's user can read the node's key file. Nothing on that machine is safe from it, and nothing here pretends otherwise (D-27).