# Board API Key Audit Boundary Board API key allow decisions authorize a request only when the matching security disposition is durable. PostgreSQL mutations and non-database effects have different atomicity constraints, so this document states the boundary explicitly. ## PostgreSQL guarantee `server/src/security/board-key-audit-coupling.ts` stages an allow disposition in the request context. - A mutation inside `db.transaction` writes the allow disposition on that transaction before the first domain mutation. Both commit or both roll back. - Direct `db.insert`, `db.update`, `db.delete`, and mutating `db.execute` calls are replayed lazily inside a transaction with the staged disposition. An audit insert failure therefore rolls back the direct domain mutation. - Direct mutation-builder operations that cannot be replayed safely fail before issuing SQL and must be moved into an explicit `db.transaction`. - There is no successful untransacted fallback. Response settlement persists only successful no-mutation dispositions; it cannot convert an uncoupled mutation into success. This boundary deliberately classifies unknown raw SQL as mutating. Read-only raw SQL is limited to statements beginning with `SELECT`, `SHOW`, `EXPLAIN`, `TABLE`, or `VALUES`. ## Non-database side-effect review The runtime route inventory denies undeclared board-key routes. Explicit denials also cover the MCP surface, tool calls and sessions, plugin action/bridge/data and webhook surfaces, skill-test execution, agent instruction-file mutation, and auth/claim/invite surfaces. Board keys can still reach authorized management routes whose handlers may combine PostgreSQL state with effects that PostgreSQL cannot roll back. | Reachable route family | Effect outside PostgreSQL | Residual failure mode | | --- | --- | --- | | Artifacts, attachments, company import/export, skills | Object storage or local filesystem writes/removals | An audit or domain rollback can leave an orphaned object/materialized tree; compensation can also fail. | | Agents, heartbeat/runtime, workspaces, environments | Process start/cancel, git/worktree changes, or provider calls | A crash can occur between durable intent and execution/acknowledgement, causing a missed action or a retry. | | Plugin and tool management | Plugin lifecycle, OAuth/provider, or network activity | Remote state cannot join the database transaction and may be applied despite a later local failure. | | Activity publication, plugin events, assignee wakeups | In-process publication or adapter dispatch | A post-commit crash can miss delivery; retry after an ambiguous acknowledgement can duplicate delivery. | The database audit coupling does not claim atomic rollback for these effects. Side-effecting handlers must use the following pattern when the effect matters to correctness or security: 1. Persist the domain change, board-key allow disposition, and an intent/outbox row in one transaction. 2. Execute the external effect only from the committed intent. 3. Give the effect a stable idempotency key derived from the intent, not from a delivery attempt. 4. Persist completion or failure so a worker can retry ambiguous outcomes. 5. Use compensating cleanup for object/file/provider state and treat cleanup as retryable, not guaranteed rollback. Existing durable wake requests and idempotency keys reduce duplicate/lost-work risk where they are used. Live UI/plugin publications remain best effort. A successful no-mutation request is audited during response settlement; if that audit write fails there is no PostgreSQL domain commit to roll back. A handler that performs only an external effect must therefore not rely on no-mutation settlement: it needs a durable intent/outbox boundary before the effect. When a route becomes board-key reachable, security review must identify every filesystem, object-store, process, adapter, plugin, and outbound-network effect and either prove an existing outbox/idempotency boundary or record the residual risk and remediation owner.