diff --git a/docs-site/concepts/connectors.mdx b/docs-site/concepts/connectors.mdx
index cabcced5c0..606bfd9707 100644
--- a/docs-site/concepts/connectors.mdx
+++ b/docs-site/concepts/connectors.mdx
@@ -7,25 +7,13 @@ Open **Connectors** in Commonly. Channels carry messages between a pod and an
outside chat. Tools let an agent read or act in another service after the
connection owner grants access.
-This live Connectors page shows a free account before any connection or tool
-grant. Slack and Telegram are under **Channels**; GitHub and Linear are under
-**Tools**. Other tools visible in the list are outside the walked guides here.
-
-
-

-
-
-
-

-
+Slack and Telegram are channels; GitHub and Linear are tools.
| Connector | Current path |
|---|---|
| [Telegram](/integrations/telegram) | Bind a private chat and choose which pods reach it. |
-| [Slack](/integrations/slack) | Authorize a workspace, then confirm the connection. |
-| [GitHub App](/integrations/github) | An instance administrator installs the app; the connection owner grants tools. On commonly.me this is limited to the instance's own repository. |
+| [Slack](/integrations/slack) | Authorize a workspace. |
+| [GitHub App](/integrations/github) | An instance administrator installs the app; the connection owner grants tools. |
| [Linear](/integrations/linear) | Connect your account and grant read access to an eligible pod. |
-The page also lists connectors that are unavailable on this instance. A listed
-row is not a working connection. The [grants guide](/concepts/grants) explains
-how a connection owner limits tool access and revokes it.
+The [grants guide](/concepts/grants) explains how a connection owner limits tool access and revokes it.
diff --git a/docs-site/concepts/grants.mdx b/docs-site/concepts/grants.mdx
index 5c8a863e0e..62ed2e2fe8 100644
--- a/docs-site/concepts/grants.mdx
+++ b/docs-site/concepts/grants.mdx
@@ -1,60 +1,58 @@
---
title: Grants
-description: Scope connector capabilities to a pod or seat and keep their use auditable.
+description: A grant is scoped to a pod and the members allowed to use it, and can expire or be revoked.
---
-A grant is a server-enforced capability record. It connects a connector
+A grant is scoped to a pod and the members allowed to use it, and can expire or
+be revoked. It is a server-enforced capability record: it connects a connector
installation to a target, the tools that target may use, and the audience that
-may redeem the capability. A grant is not a credential: it does not contain a
-secret, and revoking it does not replace the credentials held by the connector.
+may redeem the capability. It is not a credential. It does not contain a secret,
+and revoking it does not replace the credentials held by the connector.
## The grant lifecycle
1. A connected integration owns the connection and its connector capability.
-2. The connection owner creates a root grant for a pod or a seat.
+2. The connection owner creates a root grant for a pod.
3. The grant names the allowed tools, write mode, audience, budget, and expiry.
4. An installed agent may attenuate a usable grant into a narrower child grant.
5. The broker checks the grant before each tool call and records the outcome.
6. The connection owner can revoke a grant; revocation cascades to its children.
The root-grant route accepts connected GitHub App installations and supported
-hosted MCP connections, including Linear. Linear grants are read-only and work
-only in eligible private pods. The server derives the installation and broker
-from the connection, rather than trusting those values from the request body.
+hosted MCP connections, including Linear. Linear exposes only read tools. Every
+root grant, Linear included, works only in an eligible private pod. The server
+derives the installation and broker from the connection, rather than trusting
+those values from the request body.
## Targets and audience
-A grant targets either a pod or one seat:
+A grant targets a pod:
~~~json
{ "kind": "pod", "id": "" }
~~~
-~~~json
-{ "kind": "seat", "id": "" }
-~~~
-
Pod grants default to the target pod's current members. A caller can provide a
smaller audience, but every listed identity must still be a current member of
-the pod. A seat grant's audience is the target seat itself.
+the pod.
-The read boundary follows the target. Pod members can inspect a pod grant and
-its call trail. A seat grant is visible to the connection owner and the target
-seat. The API returns an effective audience rather than exposing a stale raw
-membership snapshot.
+The read boundary follows the pod. Anyone who can view the pod can inspect a
+grant and its call trail. Older seat-targeted grants may appear in the list;
+only the connection owner and that seat can read them. The API returns an
+effective audience rather than exposing a stale raw membership snapshot.
## Grant records
The projected grant record can include:
- the grant and parent/root identifiers;
-- the pod or seat target and effective audience;
+- the pod target and effective audience;
- allowed tools, write mode, and call budget;
- creation, expiry, and revocation state; and
- the installation identity and the connection owner who granted it.
-The projection deliberately omits connection and broker internals. A grant
-read is capability metadata, not a way to retrieve connector credentials.
+The projection omits the connection and broker identifiers. A grant read is
+capability metadata, not a way to retrieve connector credentials.
## API workflow
@@ -63,8 +61,7 @@ agent runtime token only for operations available to an installed agent.
### Inspect grants in a pod
-Members with access to the pod can list the grants targeting that pod or one of
-its seats:
+Anyone who can view the pod can list the grants targeting that pod:
~~~http
GET /api/pods/:podId/grants
@@ -88,17 +85,17 @@ Content-Type: application/json
"tools": ["github.list_issues"],
"writeMode": "read",
"budget": { "calls": 100, "windowMs": 3600000 },
- "expiresAt": "2026-10-01T00:00:00.000Z"
+ "expiresAt": "2027-12-31T00:00:00.000Z"
}
~~~
-The server verifies connection ownership, target membership, enabled tools,
-and audience scope before returning the new grant.
+`expiresAt` is required and must be a future time. The server verifies
+connection ownership, target membership, enabled tools, and audience scope
+before returning the new grant.
### Read a grant and its call trail
-The owner, an eligible pod member, or the target seat can read a grant. The
-same scope applies to its call trail:
+Anyone who can view the pod can read a grant and its call trail:
~~~http
GET /api/grants/:grantId
@@ -108,12 +105,14 @@ GET /api/grants/:grantId/calls?limit=100
Authorization: Bearer
~~~
-Call records include the tool, outcome, reason, approval identifier, timing,
-and an argument digest. They do not return the original tool arguments.
+Each connector call through a grant is recorded. The record keeps a digest of
+the arguments, not the arguments themselves. A call record includes the tool,
+outcome, reason, approval identifier, timing, and an argument digest.
### Attenuate or revoke
-An installed agent can delegate a narrower child grant. The child cannot widen
+An installed agent can delegate a narrower child grant under the same pod
+eligibility rule. The child cannot widen
the parent's tools, audience, write mode, budget, or expiry:
~~~http
@@ -137,11 +136,10 @@ These records have separate jobs:
| Record | Owns | Does not do |
|---|---|---|
-| Connector installation | Which external capability is connected and where it is projected | Grant a pod or seat access by itself |
-| Credential | Secret material and credential lineage | Describe which tools a caller may invoke |
+| Connector installation | Which external capability is connected and where it is projected | Grant access by itself |
+| Credential | A hashed agent token and its lineage | Describe which tools a caller may invoke |
| Grant | Target, tools, audience, limits, and revocation | Store or reveal the connector secret |
Keep actual secrets in the deployment's approved secret manager. Use
-[Connectors and Grants](/concepts/connectors) for the installation and
-credential model, and [Agent Tools](/agents/tools) for the runtime-facing tool
-surface.
+[Connectors](/concepts/connectors) to connect an integration, and
+[Agent Tools](/agents/tools) for the runtime-facing tool surface.
diff --git a/docs/agents/fleet-efficiency.md b/docs/agents/fleet-efficiency.md
index bfe793e947..39c350f9bb 100644
--- a/docs/agents/fleet-efficiency.md
+++ b/docs/agents/fleet-efficiency.md
@@ -100,8 +100,9 @@ Earned 2026-10-25: 139 ready rows had no owner, with a median wait of 69 h (p90
every week.
- **A public sentence names only what a non-author has walked on prod.** Credential, grant and record
wording follows what is walked, not what is planned. Until per-task grants are walked, a grant is
- "scoped to a pod and an agent, and can expire or be revoked". A record is "each connector call
- through a grant is recorded, never what the agent sent".
+ "scoped to a pod and the members allowed to use it, and can expire or be revoked". A record is
+ "each connector call through a grant is recorded. The record keeps a digest of the arguments, not
+ the arguments themselves".
- **About other products, only positive claims about ours.** No "first", "only" or "never".
## Where this comes from
diff --git a/docs/audits/claim-table.md b/docs/audits/claim-table.md
index 81840afd04..8dd8cb49d1 100644
--- a/docs/audits/claim-table.md
+++ b/docs/audits/claim-table.md
@@ -102,8 +102,76 @@ Owner: TASK-012. Source: `docs-site/bring-your-own/index.mdx`, `claude-code.mdx`
Owner: TASK-014. Source: `docs-site/concepts/connectors.mdx` and `docs-site/concepts/grants.mdx`.
+The connectors page is a first-touch page. A row marked `first-touch` in the Page column needs a walk on prod by someone who did not write the claim, with a 390 and a 1440 screenshot. A `file:line` does not meet that bar, so a marked row is TRUE on file:line only until its walk lands.
+
| Page | Claim | Verdict | Evidence | Date |
|---|---|---|---|---|
+| connectors (first-touch) | Open Connectors in Commonly | TRUE | `frontend/src/v2/components/V2NavRail.tsx:38` | 2026-10-26 |
+| connectors (first-touch) | Channels carry messages between a pod and an outside chat | TRUE | `backend/services/slackApi.ts:69, :82`; `backend/routes/webhooks/telegram.ts:173–177` | 2026-10-26 |
+| connectors (first-touch) | Tools let an agent read or act in another service after the connection owner grants access | TRUE | `backend/routes/grants.ts:320, :430` | 2026-10-27 |
+| connectors (first-touch) | Slack and Telegram are channels; GitHub and Linear are tools | TRUE | `frontend/src/v2/components/V2ConnectorsPage.tsx:175` | 2026-10-26 |
+| connectors (first-touch) | Telegram: bind a private chat and choose which pods reach it | TRUE | `backend/routes/webhooks/telegram.ts:173–177`; `frontend/src/v2/components/V2ConnectorsPage.tsx:616–641` | 2026-10-26 |
+| connectors (first-touch) | Slack: authorize a workspace | TRUE | `backend/services/slackOAuthService.ts:53, :66` | 2026-10-26 |
+| connectors | Slack: "then confirm the connection" | CUT | Not walked in time. This PR removes the sentence. | 2026-10-26 |
+| connectors (first-touch) | GitHub App: an instance administrator installs the app | TRUE | `backend/routes/integrations.ts:239–262` | 2026-10-26 |
+| connectors (first-touch) | GitHub App: the connection owner grants tools | TRUE | `backend/routes/grants.ts:320, :346` | 2026-10-26 |
+| connectors | GitHub App on commonly.me: "limited to the instance's own repository" | CUT | Not walked in time. This PR removes the sentence. | 2026-10-26 |
+| connectors (first-touch) | Linear: connect your account | TRUE | `backend/routes/hostedMcpConnect.ts:351, :697` | 2026-10-26 |
+| connectors (first-touch) | Linear: grant read access to an eligible pod | TRUE | `backend/integrations/hostedMcp/linear.ts:28–31, :64, :92–93`; `backend/services/outsiderGrantPodEligibility.ts:46–54` | 2026-10-26 |
+| connectors | Live page shows a free account before any connection or tool grant | CUT | Not walked in time. This PR removes the sentence. | 2026-10-26 |
+| connectors | "Other tools visible in the list" | CUT | Not walked in time. This PR removes the sentence. | 2026-10-26 |
+| connectors | Two screenshots and captions | CUT | Not walked in time. The images stay in `docs-site/images` and are no longer referenced. | 2026-10-26 |
+| connectors | The page lists unavailable connectors; a listed row is not a working connection | CUT | Not walked in time. This PR removes the paragraph. | 2026-10-26 |
+| connectors (first-touch) | The grants guide explains how a connection owner limits tool access and revokes it | TRUE | `backend/services/roomGrantService.ts:324–373`; `backend/routes/grants.ts:480–501` | 2026-10-27 |
+| grants | A grant is scoped to a pod and the members allowed to use it, and can expire or be revoked | TRUE | `backend/models/RoomGrant.ts:28, :32–34` (target, audience snapshot, expiry); `backend/routes/grants.ts:371–379` (audience defaults to current members; each listed id must be one of them); `backend/services/roomGrantService.ts:233–239` (effective audience is the snapshot ∩ current members), `:242–243, :255–257` (expiry required and future), `:431` (expiry enforced on use); revoke `backend/routes/grants.ts:480–501`, `backend/services/roomGrantService.ts:390–396`, `backend/models/RoomGrant.ts:106–136` | 2026-10-27 |
+| grants | A grant is a server-enforced capability record | TRUE | `backend/models/RoomGrant.ts:11–36` | 2026-10-26 |
+| grants | It is not a credential, and revoking it does not replace the connector's credentials | TRUE | `backend/models/RoomGrant.ts:24–45` (no credential field); `:128–134` (revoke writes only `revokedAt` and `revokedBy`); `backend/models/AgentCredential.ts:41–58` | 2026-10-27 |
+| grants | A connected integration's owner owns the connection | TRUE | `backend/routes/grants.ts:320` | 2026-10-26 |
+| grants | Main's lifecycle: "creates a root grant for a pod or a seat" | FALSE | Fixed by this PR. `backend/services/outsiderGrantPodEligibility.ts:46`; `backend/routes/grants.ts:367–369, :405–417` | 2026-10-27 |
+| grants | The connection owner creates a root grant for a pod | TRUE | `backend/routes/grants.ts:312, :320, :430` | 2026-10-27 |
+| grants | The grant names tools, write mode, audience, budget, and expiry | TRUE | `backend/models/RoomGrant.ts:30–34` | 2026-10-26 |
+| grants | An installed agent may attenuate a usable grant into a narrower child grant | TRUE | `backend/routes/grants.ts:438–473`; `backend/services/roomGrantService.ts:324–373` | 2026-10-27 |
+| grants | The broker checks the grant before each tool call and records the outcome | TRUE | `backend/services/toolBrokerService.ts:834, :1105, :1210, :1430` | 2026-10-26 |
+| grants | The connection owner can revoke; revocation cascades to children | TRUE | `backend/routes/grants.ts:480–501` (owner-or-admin check `:487–492`; routes `:500–501`); `backend/services/roomGrantService.ts:390–396`; `backend/models/RoomGrant.ts:106–136` | 2026-10-27 |
+| grants | The root-grant route accepts GitHub App and hosted MCP, including Linear | TRUE | `backend/routes/grants.ts:346`; `backend/services/installable/toolInstallables.ts:248` | 2026-10-26 |
+| grants | Linear exposes only read tools | TRUE | `backend/integrations/hostedMcp/linear.ts:28–31, :64, :92–93` | 2026-10-26 |
+| grants | Every root grant, Linear included, works only in an eligible private pod | TRUE | `backend/services/outsiderGrantPodEligibility.ts:46–54`; `backend/routes/grants.ts:405` (unconditional call), `:457` (attenuation) | 2026-10-27 |
+| grants | The server derives the installation and broker from the connection | TRUE | `backend/routes/grants.ts:321–322` (comment), `:350`, `:392–394` (caller's `brokerId` refused), `:428` (`brokerId` from the catalogue) | 2026-10-27 |
+| grants | A grant targets a pod: `{ "kind": "pod", "id": "" }` | TRUE | `backend/services/roomGrantService.ts:13` | 2026-10-26 |
+| grants | Main's seat target and seat audience | FALSE | Fixed by this PR. Create refuses seat targets at `backend/routes/grants.ts:367–369`, and the outsider gate refuses them too (`backend/services/outsiderGrantPodEligibility.ts:46`). The mint test asserts the 400 at `backend/__tests__/unit/routes/grants.mint.test.js:112–118` and the real gate at `:120–123`. | 2026-10-27 |
+| grants | Main's seat grant is visible to its owner and seat | FALSE | Fixed by this PR. The read branch at `backend/routes/grants.ts:523–528` exists, but POST refuses seats (`:367–369`), and attenuation copies the parent's target (`backend/services/roomGrantService.ts:379`). | 2026-10-27 |
+| grants | Pod grants default to the pod's current members | TRUE | `backend/routes/grants.ts:371–373` | 2026-10-26 |
+| grants | Each listed audience identity must be a current member of the pod | TRUE | `backend/routes/grants.ts:374–379` | 2026-10-27 |
+| grants | The read boundary follows the pod; anyone who can view it can inspect the grant and its call trail | TRUE | `backend/routes/grants.ts:514–533` (`gateGrantRead`; pod branch `:529–532`) | 2026-10-27 |
+| grants | Older seat-targeted grants may appear in the list; only the connection owner and that seat can read them | TRUE | `backend/routes/grants.ts:367–369` (create refuses seat targets, so a seat grant predates that guard or comes from attenuating one, `backend/services/roomGrantService.ts:379`); `:523–528` (`gateGrantRead` seat branch: granter or that seat only); `:613–618` (the pod list includes seat grants whose target is a current member) | 2026-10-27 |
+| grants | The API returns an effective audience, not a stale membership snapshot | TRUE | `backend/routes/grants.ts:100`; `backend/routes/grants.ts:586`, `:620–627` | 2026-10-27 |
+| grants | The projected grant record fields | TRUE | `backend/routes/grants.ts:85–110` | 2026-10-26 |
+| grants | The projection omits the connection and broker identifiers | TRUE | `backend/routes/grants.ts:94–107` (projected fields); `:578–579` (comment); `:588` (projection call) | 2026-10-27 |
+| grants | `grantedBy` is the connection owner | TRUE | `backend/routes/grants.ts:119` | 2026-10-26 |
+| grants | Use a human JWT for connection-owner and pod-management operations; use the agent runtime token only for operations available to an installed agent | TRUE | `backend/routes/grants.ts:312`, `:500–501`, `:600` (`auth`, which rejects agent tokens at `backend/middleware/auth.ts:44–45`); `:440–442` (attenuate, `agentRuntimeAuth`); `:540`, `:580` (reads, `dualAuth`) | 2026-10-27 |
+| grants | Anyone who can view the pod can list the grants targeting it | TRUE | `backend/routes/grants.ts:600–608` | 2026-10-27 |
+| grants | Example tool `github.list_issues` | TRUE | `backend/services/toolBrokerService.ts:198, :201` | 2026-10-26 |
+| grants | Example `expiresAt` of 2026-10-01 | FALSE | Fixed by this PR, to 2027-12-31. 2026-10-01 is past and is rejected (`backend/services/roomGrantService.ts:255–257`). | 2026-10-26 |
+| grants | `expiresAt` is required and must be in the future | TRUE | `backend/services/roomGrantService.ts:242–243, :255–257` | 2026-10-26 |
+| grants | Create verifies connection ownership, target membership, enabled tools, and audience scope | TRUE | `backend/routes/grants.ts:320` (owner), `:367–370` (pod target and `ensureTargetAccess`), `:374–379` (audience), `:395–403` (enabled tools), `:405–417` (outsider gate) | 2026-10-27 |
+| grants | An owner, an eligible pod member, or the target seat can read a grant | FALSE | Fixed by this PR. The pod branch has no owner exception (`backend/routes/grants.ts:529–532`). | 2026-10-27 |
+| grants | Anyone who can view the pod can read a grant and its call trail | TRUE | `backend/routes/grants.ts:529–532` (gate); `:540` (calls route); `:580` (grant route) | 2026-10-27 |
+| grants | Each connector call through a grant is recorded. The record keeps a digest of the arguments, not the arguments themselves | TRUE | `backend/services/toolBrokerService.ts:1078–1123` (`recordCall`, `argsDigest` at `:1113`); call sites `:1164, :1306, :1317, :1327, :1375, :1470`; `backend/models/ToolCall.ts:76, :99, :153–154`; the second writer `backend/services/approvalActionService.ts:608–633` also writes only `argsDigest`. Limit: a parked approval holds the raw arguments in `ApprovalAction.toolCall.canonicalArgs` from park (`backend/services/toolBrokerService.ts:1281`; `backend/models/ApprovalAction.ts:38–44`) until a terminal write unsets them (`backend/services/approvalActionService.ts:537, :857, :904, :1025`). These are call sites, not an exhaustive path audit. | 2026-10-27 |
+| grants | A call record includes the tool, outcome, reason, approval identifier, timing, and an argument digest | TRUE | `backend/services/toolBrokerService.ts:1105–1121` (`ToolCall.create`: tool `:1112`, `argsDigest` `:1113`, `at` `:1114`, outcome `:1115`, reason `:1116`, approvalId `:1119`, durationMs `:1120`). The second writer (`backend/services/approvalActionService.ts:620–631`) sets no `at` or `durationMs`; `ToolCall.create` then stamps the write time (`backend/models/ToolCall.ts:258`, `record.at || new Date()`) and stores `duration_ms` as null (`:262`). So an approval-decision record has a time and no duration. | 2026-10-27 |
+| grants | Attenuation is under the same pod eligibility rule | TRUE | `backend/routes/grants.ts:457` | 2026-10-27 |
+| grants | The child cannot widen tools, audience, write mode, budget, or expiry | TRUE | `backend/services/roomGrantService.ts:338–339, :346–347, :349–351, :369–373` | 2026-10-26 |
+| grants | Attenuation uses the agent runtime token | TRUE | `backend/routes/grants.ts:55–59, :440–442` | 2026-10-27 |
+| grants | The owner revokes by DELETE or the POST revoke action, and both cascade | TRUE | `backend/routes/grants.ts:500–501` (both routes share `revokeHandler`, `:480–498`); `backend/models/RoomGrant.ts:106–136` | 2026-10-27 |
+| grants | Connector installation does not grant access by itself | TRUE | `backend/routes/hostedMcpConnect.ts:36`; `backend/routes/integrations.ts:51`; `backend/services/installable/installableInstallationService.ts:21` (revoke only). The only grant creator is `backend/routes/grants.ts:430`. | 2026-10-27 |
+| grants | Connector installation sets which capability is connected and where it is projected | TRUE | `backend/routes/integrations.ts:261–262`; `backend/services/installable/installableInstallationService.ts:165` | 2026-10-26 |
+| grants | Credential owns "secret material and credential lineage" | FALSE | Fixed by this PR, reworded to "a hashed agent token and its lineage". Only a hash is stored (`backend/models/AgentCredential.ts:16, :43`); lineage is at `:27, :48, :87`. | 2026-10-26 |
+| grants | Credential does not describe which tools a caller may invoke | TRUE | `backend/models/AgentCredential.ts:50`; `backend/controllers/authController.ts:275`; `backend/middleware/daemonAuth.ts:51–52` | 2026-10-26 |
+| grants | Grant does not store or reveal the connector secret | TRUE | `backend/routes/grants.ts:85–110`; `backend/models/RoomGrant.ts:30–36` | 2026-10-26 |
+| grants | Grant owns target, tools, audience, limits, and revocation | TRUE | `backend/models/RoomGrant.ts:30–36`; `backend/routes/grants.ts:500–501` | 2026-10-27 |
+| grants | Link label "Connectors and Grants" points to `/concepts/connectors` | FALSE | Fixed by this PR. The label was at `docs-site/concepts/grants.mdx:145` on main; the page title is "Connectors" (`docs-site/concepts/connectors.mdx:2`). | 2026-10-26 |
+| grants | The connectors page covers the installation and credential model | FALSE | Fixed by this PR. The page never mentioned credentials. | 2026-10-26 |
+| grants | Agent Tools is the runtime-facing tool surface | TRUE | `docs-site/agents/tools.mdx:3` | 2026-10-26 |
+| grants | The API routes listed in the code blocks | TRUE | `backend/routes/grants.ts:312` (POST `/`), `:439` (attenuate), `:500`, `:501` (revoke POST and DELETE), `:540` (calls), `:580` (grant read), `:600` (pod grants list) | 2026-10-27 |
## Self-host