<!-- Operations runbook: observe, shadow, enforce, export evidence — Attenu. Source: https://github.com/attenu-io/attenu-derive/blob/main/docs/OPS-RUNBOOK.md · Rendered: https://attenu.io/docs/operations/ · Updated 2026-08-25 -->

# Attenu ops runbook

*Install → shadow → curate → enforce → verify, for an operator running Attenu on their own agent app. All
paths assume the packaged `attenu` CLI (installs from a wheel, no source tree).*

## Install

    pip install attenu-derive
    # (brings attenu-guard, the enforcement library, as a dependency)
    attenu --version

## 1. Shadow first (zero risk)

Run your app with the attenu-guard adapter for your framework in **observe** mode (see
`attenu-guard` adapter docs). It records every delegation + tool call to the audit ledger, redacted at
capture (names, scope classes, quantity buckets, salted hashes — never values). Nothing is blocked.

## 2. Day-0 coverage + a draft pack

    attenu onboard <recorded-traces>.jsonl --domain my-app --scaffold my-app.yaml

Prints what the shipped kit resolves (reads granted heuristically; money/mail/delete/exec **withheld**;
unknowns fail-closed) and writes `my-app.yaml` — a **draft** domain pack with one entry per uncurated tool
(heuristic guess + a `_review` note; tier-2 auto-marked `requires_grant`). **Edit every entry**: confirm the
scope, drop `requires_grant` only for tools you deliberately enable. This is the ≤1h step.

## 3. Verify the pack

    attenu coverage <traces>.jsonl --domain my-app     # expect curated_share high, unresolved 0

Re-run until unresolved is 0 and the tier-2 tools show as `requires_grant` (held), not withheld/unresolved.

## 4. Enforce

Install the shim in **enforce** mode with the derived authority (see `run_adk_enforce` for the wiring:
deriver + pack + `operator_grants` → `meet` → shim). Grant the tier-2 scopes the app legitimately needs
(`operator_grants={"payments.transfer"}`); leave the rest held. A call outside the granted authority is
denied before the tool body runs, the machine-readable denial goes back to the model, and a `deny` lands on
the ledger.

## 5. Export + verify evidence (offline)

    # the app exports a bundle from its audit log (attenu_guard.evidence.export_bundle)
    attenu verify bundle.json --pubkey <hex>           # --hs256-key is the offline TEST signer only — never the auditor path

Returns `{integrity, monotonicity, containment}`. An auditor runs this against the bundle **with no access
to the engine** — a rewritten-and-re-signed log still fails, because the invariants are checked against the
bundle's own contents, not a hash.

## Operational controls

- **Revocation:** `guard.revoke()` / `guard.revoke_agent(agent_id)` cuts a node or a principal chain-wide.
- **Strike policy (optional):** `Guard.issue(strikes=StrikePolicy(n=3, mode="same_scope"))` auto-revokes a
  node after N same-scope denials; off by default; per-installation config.
- **Key rotation (ledger anchor):** rotate the Ed25519 signing key on your schedule; anchors are per-run, so
  a rotation does not invalidate past anchors (each verifies against the key that signed it).
- **TTL:** authorities carry a TTL; expired authority denies (`TTL_EXPIRED`).

## Failure modes

- **A benign call is denied** → the pack is missing/too tight for that tool. `attenu coverage` to find it,
  add/loosen the curated entry (never below the correct tier), re-verify, redeploy. Fail-closed by design.
- **A `requires_grant` tool is blocked** → expected until the operator enables its scope (`operator_grants`).
- **`attenu verify` fails integrity** → the bundle was altered or the anchor key is wrong. Do not trust the log.
- **`attenu verify` fails monotonicity/containment** → the ledger claims a delegation/action outside authority;
  treat as a tampered or buggy producer, not an Attenu enforcement result.

## Data custody — what does and does not leave the premises

Attenu collects **no background telemetry.** The only data that ever leaves is a bundle **you export and send**,
via `attenu_cloud.flywheel.export_for_flywheel` (or `attenu verify` locally, which sends nothing). That bundle
is enforced-redacted and refuses to emit anything unvetted (`EvidenceLeakError`):

**Leaves the premises (redacted):** the hash-chained ledger structure — event types, node/agent ids, scopes,
decisions (allow/deny), reasons, the derived authorities per node — plus tool-call context restricted to
**redacted features only** (argument *shapes*, quantity *buckets*, salted *hashes*; ceiling context the guard
sets). Free-text prompts are replaced by a length+hash marker before the bundle is signed.

**Never leaves:** raw tool-argument values, raw prompt/task text, tool outputs, customer records. These are
caught by an allow-list check that fails the export rather than shipping an unknown field or a raw context value.

The bundle remains offline-verifiable after redaction — `attenu verify` still confirms integrity, monotonicity
and containment, because redaction only removes and the anchor is computed over the redacted form.
