fix: let agent keys send hooks without project:read

Merged
MF
3d ago
+435-33

DNO-1412

Why

An agent key holding org:hooks_ingest on a project got 403 when sending hooks there. Resolving Gram-Project also ran the generic project:read gate, which org:hooks_ingest does not imply. Sending hooks should depend only on org:hooks_ingest bounded to the project. Pairs with #8015, which gives new agents org:hooks_ingest: together, an agent key with only that grant can send hooks.

What changed

Before: every hooks route resolved its project through Authorize, so an agent needed project:read and then org:hooks_ingest.

After:

  • authorizeHooksProject resolves the project for hooks ingestion. For an agent actor (an agent key, or a workload acting for an agent) it uses AuthorizeWithHandlerProjectAccess, which keeps tenant scoping and key project binding, then requires project-bounded org:hooks_ingest. Other callers keep Authorize and its project:read gate.
  • Cursor, codex, OTEL, the optional-auth plugin endpoints, AHP and workloads use it. An agent on any other hooks method is refused before its project resolves.
  • project:read stays required everywhere else.
  • Route-level tests with real agent keys: admitted for project P with only the hooks grant, refused for project Q, refused without the grant.

Platform MCP

No tool change: this corrects runtime hooks ingestion by agent credentials on a selected project. Existing management tools do not send hook events or require a contract change; route-level tests cover project bounds and missing grants.

Narrated before/after demonstration

Real local integration results using the same agent-key tests against the parent and PR implementations. Before: hooks-only keys are refused. After: all eight tests pass, including wrong-project and missing-grant refusal. The recording presents captured Go test output; it does not demonstrate a dashboard or OpenClaw interaction.

https://github.com/user-attachments/assets/9c847044-8f63-46f2-a1a7-3113f9fd14cb