Capabilities
A capability is the right to cause a class of side effects — where tools meet external resources. Agents already reason and speak; the gap is credentialed external actions: posting to Slack, commenting on GitHub, sending a webhook. The model wants both: tool records (what operation is possible) and resource records (what external thing it operates on). A tool without a target is abstract; a target without a tool is inert.
A capability is not a record type. It’s the derived answer to “can this agent use this tool against this resource?” — computed from the intersection of x on the tool record and x on the resource record. The existing permission system is the single authority. No separate capability grant table, no second ACL that can disagree.
At activation time, build the agent’s tool registry by pre-filtering: the spawned agent literally cannot see tools it shouldn’t use. Maps cleanly onto the permission model — no runtime checks during the agent loop. resolveCapabilities(agentId, db) → { tools, resources } returns the resolved permission intersection; secrets resolve via a runtime-injected callback. If we later need narrowing (rate limits, time windows, action subsets), those become constraint records attached to the agent-tool or agent-resource permission edge — but start without them; the permission system is already expressive enough for the hourly Discord test.
Resources have identity in the substrate so activation policies and audit trails reference them by record ID rather than opaque config blob — Discord channels, GitHub repositories, Linear workspaces, PagerDuty services, code-repo checkouts, durable streams, sandbox runtimes. Scoped to houses (the right permission boundary — a house admin controls which external targets are available within their scope). Resources do not carry secret references — many resources may share one credential, one resource may need several; credential binding is a separate concern.
Secrets are the one place “everything is a record” breaks down if taken too literally. A capability without secret management is theatre. Credentials should be referenced from records, not stored in plaintext in shared records. The vault holds values keyed by stable record IDs; binding records say which credential goes where and are auditable — you can answer “which agents have access to which credentials” without touching the vault. Many resources sharing one credential is the common case. One resource needing multiple credentials (OAuth + webhook secret) means multiple bindings pointing at the same resource.
Connections: the first resource that exists
The forge case makes the abstract model concrete, and the current code shows what happens without it. A house GITHUB_TOKEN secret already works end to end: buildBoxEnv special-cases the name, derives GH_TOKEN, and hand-wires a git credential helper scoped to github.com. That is a connection hiding inside the KV secret resolver — a resource (the forge) and a credential binding, expressed as a magic env-var name a user has to know. environments.default_repo and allowed_repos are the other half of the same missed shape: schema-only columns nothing reads, enforces, or clones from.
A connection is the house-level idea that replaces both: this house is connected to GitHub, with this credential. The user story is one sentence — connect a house to a forge once, by pasting a token (today) or installing an app (later), and from then on git and gh are authenticated in every box in the house for the repos that credential covers. Nothing more: how an agent clones, branches, or opens a PR is prompt territory, and binding repos onto environments is scope creep dressed as convenience.
Today a connection is sugar over a secret, not a record type. The only kind that exists is a token, and a token connection is a house secret plus the knowledge that it is the GitHub one. So the product surface — a “Connect GitHub” card in house settings, arbe connection in the CLI — reads and writes the GITHUB_TOKEN secret, and the env-var name stops being something a user has to know. Keeping it a secret is what keeps the rest of the system honest: the environment’s secrets policy still narrows it by name, any member can create it, an owner or its author rotates or deletes it, and arbe env diagnose already reports it missing. A separate table would break every one of those for no gain until a second kind exists.
The record shape below is where this goes when a GitHub App installation is real. That kind is the reason a connection would stop being a secret: the backstage mints a one-hour installation token per run, so the long-lived credential never enters a sandbox, repos are picked in GitHub’s own UI, and revocation happens on GitHub. Until then it is a disabled placeholder in the connect form, and the schema stays in this document.
connection parent: house {provider: github | ..., # the seam — GitLab is a second provider, not a second system kind: token | app_installation, # per provider; `token` ships first as a tagged secret host, # the credential helper is scoped to exactly this host secret_id?, # `token` kind: the Vault-backed value it resolves installation_id?} # `app_installation` kind: GitHub mints per-run tokensA connection is a house asset, like a shared deploy key. Any member’s agent uses it; one per provider per house, so a house with a personal and a work GitHub holds one of them — the same limit a single GITHUB_TOKEN has today. There are no per-user credentials because agents are shared and the principal is ambiguous the moment two humans share a thread or a bot triggers a bot. “Act as me” (a user-to-server token on the same app) is a later kind that resolves only for the one human who authored the triggering entry — never the default. A member’s own machine user is the token kind with zero extra code: the escape hatch, not the product path.
Git identity is the one thing the box lacks. Today it sets none, so commits come out as nobody; that is undesigned, not decided. buildBoxEnv should export the agent as author — its handle and a non-routable house-agent email; there is no domain for agents and GitHub does not require the email to resolve, so the commit shows an unlinked author and the token’s owner as pusher. A Co-authored-by trailer naming the human whose entry triggered the run is honest attribution, but a trailer is commit-message text the agent writes, not something env can enforce — it belongs in the sandbox tool guidance, alongside the one line saying which forges the house is connected to.
This settles the open question below for the credential class that matters first: one credential per provider, house-wide, and the environment’s secrets policy stays the narrowing knob. Tracked as task arbe-e29c (“Connect a house to GitHub so git and gh just work in every box”).
Open question: should secret-binding records enumerate resource_ids and agent_ids explicitly, or use the permission system (agent has r on the secret-binding record → can resolve it)? The permission approach is more consistent but means secret access is governed by the same chain-walking as everything else — possibly too permissive for credentials. Explicit enumeration is safer but means a second access-control path. Leaning explicit — credentials deserve a tighter default.
See: thinking/record-types (resource / tool / secret-binding shapes), thinking/activation, system/secrets.