# 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 tokens
```

**A 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](record-types.md) (resource / tool / secret-binding shapes), [thinking/activation](activation.md), [system/secrets](../system/access/secrets.md).
