Repository navigation
registry: agents, their scopes, refusals and push kinds - #90
Conversation
An owner can let an assistant, Sourceful support for one ticket or the release check reach the box through FTW Cloud. The registry names what the box enforces for them: - roles agent, support and release, each the most a grant may carry; - scopes ftw.logs.read, ftw.config.read, ftw.diag.run, ftw.dispatch.hold, ftw.goals.request and ftw.update.request, so scopes now take the verbs run, hold and request as well as read and write; - an agent key on each op, the scope that opens it to an agent; - the capability access.agents; - eight refusals, from E_GRANT_EXPIRED to E_NOT_FOUND; - push kinds agent.added and agent.request. docs/agents.md in srcfl/ftw is the wire. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 01d9db64bc
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # To every owner, when an agent asks for a durable change. It waits for | ||
| # one of them to confirm it, and runs out after twelve hours. | ||
| title: Waiting for your answer | ||
| body: "An assistant asks to change a setting. Open FTW to confirm or decline." |
There was a problem hiding this comment.
Route request pushes to a confirmation screen
When a box emits agent.request, tapping the notification cannot reach the promised confirmation flow: src/sw.ts routes every non-charging push to /, and a repo-wide search finds no agent-request approval view or API. The request therefore remains pending until it expires even after the owner follows “Open FTW”; implement and deep-link the approval flow, or direct the owner to the box page where the request can actually be handled.
Useful? React with 👍 / 👎.
| # the release check access to the box. Access from outside the house is | ||
| # news every owner should hear, as with cloud.granted. | ||
| title: New access to your box | ||
| body: "You gave an assistant or Sourceful access. See who has access in the app." |
There was a problem hiding this comment.
Link access alerts to an agent roster
When agent.added is delivered, the advertised inspection path does not exist in this app: the current Access screen only reads paired phones from /api/app-link/devices, and a repo-wide search finds no use of access.agents or any agent roster. An owner following this security-sensitive alert therefore cannot see which agent was granted access; either add the agent list before shipping this copy or point the notification to the box page that has it.
Useful? React with 👍 / 👎.
| # the release check access to the box. Access from outside the house is | ||
| # news every owner should hear, as with cloud.granted. | ||
| title: New access to your box | ||
| body: "You gave an assistant or Sourceful access. See who has access in the app." |
There was a problem hiding this comment.
Avoid attributing every grant to its recipient
When one co-owner creates a grant, this event is sent to every owner as specified immediately above, so owners who did not perform the action are nevertheless told “You gave” the access. For a security-sensitive access alert this misidentifies the actor; use neutral wording such as “An assistant or Sourceful was given access,” or include the actual granting owner.
Useful? React with 👍 / 👎.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
ftw-webapp | 31a144b | Commit Preview URL Branch Preview URL |
Oct 15 2026, 06:09 PM |
… kinds From the review of the agent contract: - agent_args: what an agent may put in each op, with a label per argument for showing a request to the owner; - reads: each read an agent may send and its scope; - battery.hold and loadpoint.boost lose their agent scope until the box can bound them; - E_CLOCK_SKEW for an approval whose time is too far from the box's, and E_NOT_FOUND worded for any id; - ftw.update.request marked reserved; - push kinds agent.expiring, approval.key.set and owner.added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
What
Names for agents, edited here first as the registry rule says. An owner can let an assistant (ChatGPT, Claude, Grok through MCP at FTW Cloud), Sourceful support for one ticket, or the release check reach the box. The box enforces everything; these are the words both sides use.
agent,supportandrelease. Each is the most a grant of that kind may carry; a grant names a subset.ftw.logs.read,ftw.config.read,ftw.diag.run,ftw.dispatch.hold,ftw.goals.requestandftw.update.request. Scopes now take the verbsrun,holdandrequestas well asreadandwrite.agent, the scope that opens each to an agent. Holds (battery.hold,loadpoint.hold,loadpoint.boost) run under caps and end by themselves. Durable changes (site.mode.set,planner.prefs.set,loadpoint.soc.set,loadpoint.surplus_only.set) wait for an owner asagent.request.access.agents.E_GRANT_EXPIRED,E_OP_NOT_GRANTED,E_CAP_EXCEEDED,E_RATE_LIMITED(the one retryable),E_NEEDS_OWNER,E_BAD_SIGNATURE,E_NO_APPROVAL_KEY,E_NOT_FOUND.agent.addedandagent.request, to every owner.The wire, the approval signature and the box page are in
docs/agents.mdin srcfl/ftw (the paired PR below).In this app:
contract.ts, the retry table and the push toggles take the new names, and the bare-scope test accepts the new verbs. A toggle appears only when the box's rules list the kind, so nothing changes until a box sends them.Paired PRs
docs/agents.md, the message shapes and the approval-signature vectors ingo/protocol. Core enforcement is feat(agents): enforce agent and support access in Core ftw#1646, stacked on it.protocol/registry.yamland updatesContract.swift.Contract-pair: srcfl/ftw@claude/agents-contract
Tests
npx vitest run: 108 files, 1,102 tests pass.svelte-check: 0 errors.check-contract-drift.mjsagainst the box's copy: both files byte for byte.After the review (adversarial, at f557f6d)
Fixed before merge, the three blockers and everything that changes signed bytes or wire shapes:
tun: falseand no copy scopes; only FTW Cloud'skeyspart opens an agent's session, never/v1/app,/v1/app/onceor the relay (docs/protocol-cloud.md). Offers are signed (SignedOffer {o, kid, sig},protocol.OpenOffer) by a pinned offer key, live at most 10 minutes, carryclientHostandverified, leavehomeempty, and a support offer comes only fromSupportClientID.battery.holdandloadpoint.boostlose their agent scope. A newagent_argsblock allowlists each op's arguments, with labels (sovoltage,max_amps_per_phaseand the rest stay with owners). Holds carry their setter, an owner's hold wins (E_NEEDS_OWNER), an agent clears only its own,maxWis |W|, and revoke or expiry ends an agent's holds.agent.confirmsigns[reqId, agent, op, sha256(args)]; request ids are 128 bits; anatMsfrom before the box started is refused.agent_argsand the op's own checks.byisstaff:<id>orbot:<name>, refused from any session but support.agent.widensigns the row's key.Roster.noise); typed readslive.get,loadpoints.get,cars.get,drivers.getin a registryreadstable, cursors on logs and events, agent ids under the box's own key.approval.key.settakesprevSig, audit rows carrycount,agent.decided,E_CLOCK_SKEW, push kindsagent.expiring,approval.key.set,owner.added,ftw.update.requestmarked reserved,E_NOT_FOUNDworded for any id (the car contract can reuse it).Vectors regenerated: approval (widen and confirm bytes changed), a signed offer, and the roster (with
noiseand an agent row).🤖 Generated with Claude Code