A4 (V9): telemetry-integrity.sh binds provider-usage.jsonl + cost/metrics.jsonl
to a signed manifest head. Tampering a token flips the head (MISMATCH); an
attacker who rewrites the head cannot re-sign it (SIGNATURE_INVALID) without
the off-repo key. sign-audit-head.sh now also signs telemetry (best-effort).
Supports CASAN_AUDIT_PRIV/PUB overrides for self-contained verification.
A5 (V12/V13/V14): cost-spike-detect.sh adds an absolute per-call cap
(CASAN_COST_ABSOLUTE_MAX_TOKENS, enforced from record #1 → catches slow-boil
and cold-start) and a cumulative budget (CASAN_COST_CUMULATIVE_BUDGET_TOKENS →
catches under-threshold spray), keeping the existing median×mult spike test.
Backward compatible: <3 records with no caps still exits 3.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Root cause: tool-audit-lib.sh signs tool-calls-head.sig with a local RSA
key (~/.casan/audit-keys/), but sign-audit-head.sh (called as a CI step)
overwrites audit-public.pem with the Vault KMS public key. On the second
run inside security-gate.sh, the local key still exists so audit-public.pem
is NOT updated, leaving a Vault key vs local-key mismatch that causes
verify-tool-audit.sh to exit 1.
Fix 1 — sign-audit-head.sh: after signing the audit.jsonl chain via Vault
KMS, also re-sign the tool-calls chain head with the same casan-audit-key.
Both chains are now anchored to the same Vault public key in audit-public.pem.
Fix 2 — run-casan4-harness-tests.sh: call sign-audit-head.sh just before
the inline verify-tool-audit.sh check (line 217). This re-signs both chains
with Vault KMS so the inline check sees anchor=signed instead of mismatched
local key vs Vault pub.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>