Skip to content

Primitives

Six things. Everything else is metaphor.

Records

The only entity. Houses, rooms, agents, tools — all records. What differentiates them is type, content, and relational position (parent_id). A record becomes a scope when other records address it as their parent. Identity is emergent from usage, not declared by type. This is closer to speech act theory than database design: the meaning of a record is determined by how other records address it.

Streams

Append-only logs on a separate storage plane. Messages, events, anything that accumulates chronologically within a scope. Replayable from the beginning. The stream is what happened. Streams live outside Postgres — they’re HTTP-addressed byte logs with their own offset semantics, CDN-cacheable for historical reads. A room record points to its stream via durable_stream_id; the stream holds the content, the record holds the identity.

Streams are a primitive because they’re a fundamentally different storage plane, not a specialization of mutations. Mutations are transactional audit rows in Postgres. Streams are append-only byte logs outside Postgres entirely — different write semantics, different read patterns, different durability guarantees. They share the concept “append-only history” but share nothing else.

Agents

Records whose type grants them participation. Human or bot, irrelevant at this layer. An agent is a record with identity and permissions. An agent doesn’t “belong to” a scope. It has permission records linking it there.

The identity and authorization model is symmetric — humans and bots are both agents with rwx on scopes. The execution environment is not: a human’s runtime is a browser, a bot’s is a Durable Object with SQLite and an LLM. arbe equalizes participation, not embodiment.

Permissions

Unix got the formal layer right in 1973. Everything since has been cognitive translation.

rwx linking an agent to a scope. r reads, w writes, x manages structure and permissions within a scope. x means the same thing everywhere: execute a capability on the target. On a scope (house, room), that capability is managing structure and permissions. On a tool record, it’s invocation. Same symbol, different target, different risk surface. This keeps the bitmask at three bits while letting tool invocation be gated independently from scope administration — an agent can have x on a tool without having x on the room that contains it. Inherited down the room tree, overridable at any level. The permission model doesn’t distinguish humans from bots.

Mutations

The append-only audit log of structural changes. Every write to records or permissions fires a trigger that appends a mutation row. Mutations track structure; streams track content. These two histories never overlap.

Signals

The system witnessing its own operation. Latencies, retries, decisions, failures, activations, resource usage. Not content (nobody reads signals to understand a conversation) and not structure (nothing was created or moved). Every signal has an agent_id because every action has an author. Without a named primitive, this data is parasitic on the other five — shoved into streams where it pollutes content, or into mutations where it pollutes structural history, or into runtime-local storage where it becomes invisible to other surfaces. Signals are irreducible because the alternative is category errors in the model, not just worse observability.


Tools are records with type: 'tool', scoped to a house or room. x on the tool record gates invocation. The agent’s available tool set is computed at activation time from the intersection of what exists in the scope chain and what the agent has x on. Tools are a record type with a behavioral convention, not a seventh primitive.

A sandbox (container, VM, whatever runs code) is a capability an agent holds, not a primitive. An agent record may have a sandbox_id. The DO is the boundary — the sandbox does whatever it needs internally, but its only interface to the substrate is through the agent that owns it.

A session is a room with a short lifecycle. A thread is a child room. A workspace is a house. The names change. The primitives don’t.