Authority Should Be Borrow-Checked

In Cathedral, authority is borrow-checked. The right to do one specific thing to one specific object is a value the compiler tracks like memory: moved, borrowed, dropped, never forged, never conjured from nothing. That value is a capability.

On a normal OS, authority is ambient and largely invisible. A program you launch can read every file you can, because power is keyed to your identity, and the system does not track which program is exercising which slice of it. So “what can reach the camera right now?” has no precise answer, and revoking an app’s access after the fact is awkward and easy to get incompletely. Treating authority as a tracked, owned value addresses both: every grant is a thing some principal holds, with a recorded source, so those questions become things the system can answer by construction.

Holding it is the permission

A capability grants one named power over one named object, and holding the value is the permission. There is no access-control list to consult, no check against who you are. Hold the key, use the door. Lack it, and the door does not exist for you.

In Cathedral they read as types over a stable object and a domain:

Capability<File::Write("/users/zach/docs/report")>
Capability<Network::Connect("api.stripe.com:443")>
Capability<Clock::Read>
Capability<Spawn<ComponentX>>

This is why nothing is ambient. On a traditional OS a process acts because of who it is, its user id, and that power hangs around it whether it uses it or not. A Cathedral component acts only while it grips a value that permits it, and it grips nothing it was never handed. The filesystem root, a clock read, a socket, the right to spawn a child: each is a value that came from somewhere visible.

Because every grant is a value with recorded provenance, the OS can keep a live authority graph: which principals hold which capabilities, and where each came from. “What can reach the camera?” is a graph query. “What breaks if I revoke this?” is a graph query. The information lives in one place because the model put it there.

Four verbs, and one rule

The algebra is small. A capability is a value you can hold, attenuate, delegate, and revoke.

Over all of this sits one rule that makes the system safe by construction: monotonicity. You cannot give what you do not hold. Every operation either keeps authority the same or shrinks it. Delegation passes along what you have, attenuation passes along less, and nothing in the algebra manufactures authority out of thin air. Fresh authority enters the system only through a small set of trusted brokers explicitly allowed to mint it (a picker, a loader, a login surface, an OS broker). Ordinary code can only accept, narrow, use, and release. It can never conjure.

The same model runs through five things that looked unrelated.

One: the picker mints one

You pick report.pdf in a file dialog. The picker is a trusted broker, so it may mint. It mints Capability<File::Read("report.pdf")> and delegates it to the app, attenuated down from the broker’s broader authority over your realm to a single file. The gesture was the mint.

Two: the shell turns a typed path into one

You run cat ./report.pdf. The shell runs as your session principal, so it already holds broad authority over your realm. It does not pass that wholesale. It resolves the path, attenuates its realm authority down to Capability<File::Read("report.pdf")> for that one object, and delegates it to cat. The argument was the grant. rm a.txt b.txt is two attenuations and two delegations, the same algebra expressed in text instead of a dialog. Monotonicity is why rm a.txt cannot also read your photos: the shell handed down only the slice you named, and rm holds nothing else.

Three: inter-process communication is a region you hold a capability to

Two processes want to talk. In Cathedral there is exactly one inter-process communication (IPC) primitive: a capability-scoped shared memory region. The OS maps one chunk of physical memory into both processes, gated on a capability to the endpoint. After that, talking is plain reads and writes to shared memory, no kernel in the hot path. Pipes, sockets, queues, pub/sub, remote procedure calls are all libraries built over that one region.

Access to the channel is holding the capability to the region, not “the kernel checks your user id on every message.” You hold a value that names the region, or you do not have the region. Passing a capability to a peer (handing it the right to call your service, say) is just a delegation over that same shared region, and the graph records the edge. The region’s lifetime is a lease, a capability that fails safe: when the last holder drops it, or a revoke-capable holder tears it down, the OS unmaps it and reclaims the memory. A crashed peer cannot leak the region, because the region’s life was never the peer’s to leak. Communication stopped being a special OS concept and became “two principals holding capabilities to the same value.”

Four: a network connection is the same shape

Capability<Network::Connect("api.stripe.com:443")> is a value like any other. A component that holds it can reach exactly that endpoint and no other. It can attenuate (a connect capability scoped to one host is already an attenuation of broader network authority) and delegate the connection to a child. The payoff: because the typed IPC layer lowers to the same shared-region primitive whether the peer is on this machine or across the network, a service can move from local to remote without the caller rewriting its security model. The authority you hold to call it is the same value either way. Local and remote are two lowerings of one algebra.

Five: a login session is a capability too

Logging in looks like leaving the world of files and sockets for a separate authentication subsystem. It is not.

A session is a short-lived principal carrying delegated authority. Authentication is an authority-minting event: a correct credential binds a new session principal to your user principal and produces a bounded, revocable bundle of capabilities, chiefly the root capability to your realm. Before login that realm is sealed, encrypted with no principal holding its root, so your data is genuinely unreadable rather than merely access-denied. Logging in releases the realm key and mints the session. Logging out revokes the session and re-seals the realm.

Everything your apps hold is delegated down from that session. The session is a lease, so it fails safe: idle too long and it expires, taking the cascade of delegations with it. The login surface itself is a broker with no user authority of its own: it can start an authentication but cannot read your data, the same discipline as the picker. A login is a mint. A session is a held, leased, delegatable, revocable capability bundle. The same four verbs, one rule.

Being fair to Unix

It would be easy to read all this as a dunk on Unix. It is not.

The user-and-permission-bits model was a genuinely good design for its era and its hardware: a handful of users, a shared machine, a small kernel, tight memory. Authority keyed to a user id with read/write/execute bits per file is simple, fast, and comprehensible, and it ran the computing world for fifty years for a reason. Given the constraints, it was the right call.

What grew on top of it is where the strain shows. Over the decades, each new authority need got its own mechanism bolted alongside the last: groups, then access-control lists, then sudo, then setuid, then POSIX capabilities, then seccomp, then AppArmor and SELinux, then container namespaces and cgroups, then secrets stuffed in files and environment variables. Each is reasonable in isolation. Together they are a dozen uncoordinated authority models, and no single component can honestly answer “who can do what, why, through which path, and can I revoke it safely?” The information was never modeled in one place, because there was never one model.

That is the honest delta. Unix was right for its machine. The improvement is making authority a first-class value carried by the language and the OS together, so the dozen mechanisms collapse back into one. Omega, the language Cathedral is written in, treats a capability as an ordinary value the type system already understands: ownership tells you borrowed from owned from stored, domains express attenuation (Folder::Writable narrowing to File::Writable), and the compiler infers an authority-flow report for every component (what it accepts, uses, derives, stores, acquires, returns, releases). The OS supplies the runtime graph of who actually holds what. Language and OS describe the same authority with the same vocabulary, which is the only reason the single graph can exist at all.

The one-sentence version

The picker, the shell argument, the shared region, the connection, the session: all of them are the same value you hold, narrow, hand off, and take back, governed by the one rule that you cannot give what you do not hold.