---
id: w4/m10/t004
title: Acceptance — one event per write verb, denials recorded, no value leakage
worker: worker4
status: done
estimate: 25m
depends_on: [w4/m10/t003]
---

## Objective

Prove coverage and hygiene: enumerate every write verb (the same inventory `TestAuthzGuardsEveryVerb` guards) and assert each emits exactly one audit event; a cross-tenant 403 produces a denial event; env-var and connection-info flows never leak values into events; the read surface shows it all.

## Context

- `TestAuthzGuardsEveryVerb` (w4/m4) already maintains the verb inventory — reuse it so a future verb that joins authz automatically joins the audit assertion, keeping the two lists from drifting.
- Live check on the mock cluster with `BEX_CP_DB_URI` set: suspend/resume/env-var write → query t003's Render-shaped audit-logs endpoint. Note the mock likely needs recreating first (`scripts/mock-cluster.sh`; CAPD rots across host restarts).
- _2026-07-11, optional prod smoke:_ the rebuild made end-to-end prod verification cheap — OpenBao and the tenant env-vars API are live, and the CP store (bex-db CNPG) is live and restored on the platform pool. After the gitops deploy, run one suspend + one env-var write + one cross-tenant 403 against prod bex-api and confirm the audit read surface shows all three with no values. Keep the mock-cluster path as the pre-merge check.

## Steps

1. Extend the verb-inventory test: every write verb → exactly one success event with correct caller/resource; denial path → one denied event.
2. Grep-style assertion over serialized events: no env-var values, no connection strings, no secrets.
3. Live smoke on the mock cluster end-to-end.

## Files

- `lego/backend/internal/api/*_test.go` (inventory extension)
- feature test files as needed

## Acceptance criteria

- [x] Inventory test fails if a write verb emits zero or two events.
- [x] Denials recorded (`TestAuditCoversEveryWriteVerbExactlyOnce`'s deny-run assertions, `TestAuditRecordsDenialOnCrossTenantAuthorizeOn`); payload hygiene assertions pass (`TestAuditNeverCarriesSecretValues`).
- [ ] Live smoke on the mock cluster — **not run this session** (no mock cluster available in this environment; `scripts/mock-cluster.sh` wasn't invoked). Automated coverage instead: the store-layer assertions ran against a real throwaway Postgres (docker), and the full REST/GraphQL surface was exercised via `httptest` over the real composition root (`internal/api`), which is the same rigor `TestAuthzGuardsEveryVerb` itself relies on (no live cluster either). Recommended follow-up before a prod deploy: run the mock-cluster smoke described above once, or the optional prod smoke.

## Out of scope

- Perf/volume testing.
