Skip to content

docs(config): define the silent-mode contract -- logs quiet, evidence complete - #286

Merged
imran-siddique merged 2 commits into
fix/egress-context-workflowfrom
fix/silent-mode-contract
Jun 11, 2026
Merged

docs(config): define the silent-mode contract -- logs quiet, evidence complete#286
imran-siddique merged 2 commits into
fix/egress-context-workflowfrom
fix/silent-mode-contract

Conversation

@imran-siddique

Copy link
Copy Markdown
Member

Summary

Closes #284. The configuration docs said silent mode "does not log denies", which reads as "no evidence at all" -- a compliance problem if true. The actual behavior is the defensible one: the hash-chained audit log records every would-have-denied decision as advisory_deny even in silent mode; only operational log lines are suppressed. Logs quiet, evidence complete.

  • docs/configuration.md: enforcement-mode table and inline comment now state the contract explicitly.
  • tests/unit/test_silent_mode_contract.py: 4 regression tests -- silent flags would_have_denied, emits no deny log lines (advisory does), and the audit chain records advisory_deny end-to-end through the proxy.

Also the resolution direction for agentrust-io/trace-spec#19: silent is log-suppression, never evidence-suppression.

Test plan

  • 4 new tests
  • Full suite: 673 passed, 1 skipped

Stacked on #285.

Generated with Claude Code

… complete

The docs claimed silent mode "does not log denies", implying no record at
all. The actual (and correct) behavior: the hash-chained audit log records
every would-have-denied decision as advisory_deny in silent mode; only
operational log lines are suppressed. State that contract explicitly and
pin it with regression tests so it cannot change silently.

Closes #284.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(cli): add cmcp verify command for TRACE claims and audit bundles

The verifier existed only as a library (cmcp_verify), so there was no
obvious reproducible path for a third party to check a claim. New command:

  cmcp verify claim.json [--policy-hash sha256:..] [--catalog-hash sha256:..]
                          [--trusted-key HEX] [--max-age N]
                          [--audit-bundle bundle.json]

- wraps verify_trace_claim (signature, schema, key binding, freshness,
  chain consistency, pinned hashes); unpinned hashes are reported as
  "not pinned", never as verified
- new verify_audit_bundle(): recomputes every entry hash, checks the
  genesis->tip linkage, cross-checks root/tip/length against the claim,
  and verifies bundle_signature with the claim confirmation key
- exit 0 only when everything checks out

End-to-end tests generate a real claim via session close, verify it, then
tamper with one field of the claim and one audit entry and prove both fail.

Closes #283.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(verify): dev-mode platform honesty + fail-closed hardware verification (#290)

Batch of review findings, all in the trust-decision path:

- Dev-mode claims advertised platform "tpm2" (distinguished only by a
  firmware sentinel); any consumer keying trust on runtime.platform treated
  non-attested records as TPM-backed. software-only is now a first-class
  platform value (requires agentrust-trace with trace-spec#23). The
  verifier accepts both the new value and legacy tpm2+sentinel records.

- The SEV-SNP dispatch compared against the provider name ("sev-snp")
  instead of the platform name ("amd-sev-snp"), so SNP hardware
  verification NEVER ran -- claims fell through to UNSUPPORTED_PROVIDER.
  Fixing the dispatch exposed the next bug:

- All four platform verifiers (tpm, sev_snp, tdx, opaque) returned
  verified=True when raw_evidence was None -- string-format checks alone
  "verified" hardware attestation. All now fail closed with
  failure_reason="no_raw_evidence". verify_trace_claim additionally sets
  the new HARDWARE_ATTESTATION_FAILED failure when any platform verifier
  fails, which previously left overall status VERIFIED.

- Server: malformed _cmcp param (string/list/number) no longer 500s the
  call path; session_reset audit entry now records the pre-reset
  sensitivity instead of the post-reset value (forensic detail).

Tests asserting the old fail-open behavior updated to the fail-closed
contract.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@imran-siddique
imran-siddique merged commit 84423f1 into fix/egress-context-workflow Jun 11, 2026
imran-siddique added a commit that referenced this pull request Jun 11, 2026
* fix(policy): carry workflow_id into the egress evaluation context

The egress check re-evaluates the same Cedar bundle with a context that
lacked workflow_id, so a default-deny bundle (workflow-scoped permits, no
catch-all) denied every response at egress even when ingress allowed the
call. Found making the example policies workflow-scoped.

authorize_egress() now accepts workflow_id and includes it in the egress
context; the proxy passes the call's workflow through. Egress-specific
rules can still distinguish direction via context.egress.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* docs(config): define the silent-mode contract -- logs quiet, evidence complete (#286)

* docs(config): define the silent-mode contract -- logs quiet, evidence complete

The docs claimed silent mode "does not log denies", implying no record at
all. The actual (and correct) behavior: the hash-chained audit log records
every would-have-denied decision as advisory_deny in silent mode; only
operational log lines are suppressed. State that contract explicitly and
pin it with regression tests so it cannot change silently.

Closes #284.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(cli): cmcp verify command for TRACE claims and audit bundles (#287)

* feat(cli): add cmcp verify command for TRACE claims and audit bundles

The verifier existed only as a library (cmcp_verify), so there was no
obvious reproducible path for a third party to check a claim. New command:

  cmcp verify claim.json [--policy-hash sha256:..] [--catalog-hash sha256:..]
                          [--trusted-key HEX] [--max-age N]
                          [--audit-bundle bundle.json]

- wraps verify_trace_claim (signature, schema, key binding, freshness,
  chain consistency, pinned hashes); unpinned hashes are reported as
  "not pinned", never as verified
- new verify_audit_bundle(): recomputes every entry hash, checks the
  genesis->tip linkage, cross-checks root/tip/length against the claim,
  and verifies bundle_signature with the claim confirmation key
- exit 0 only when everything checks out

End-to-end tests generate a real claim via session close, verify it, then
tamper with one field of the claim and one audit entry and prove both fail.

Closes #283.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(verify): dev-mode platform honesty + fail-closed hardware verification (#290)

Batch of review findings, all in the trust-decision path:

- Dev-mode claims advertised platform "tpm2" (distinguished only by a
  firmware sentinel); any consumer keying trust on runtime.platform treated
  non-attested records as TPM-backed. software-only is now a first-class
  platform value (requires agentrust-trace with trace-spec#23). The
  verifier accepts both the new value and legacy tpm2+sentinel records.

- The SEV-SNP dispatch compared against the provider name ("sev-snp")
  instead of the platform name ("amd-sev-snp"), so SNP hardware
  verification NEVER ran -- claims fell through to UNSUPPORTED_PROVIDER.
  Fixing the dispatch exposed the next bug:

- All four platform verifiers (tpm, sev_snp, tdx, opaque) returned
  verified=True when raw_evidence was None -- string-format checks alone
  "verified" hardware attestation. All now fail closed with
  failure_reason="no_raw_evidence". verify_trace_claim additionally sets
  the new HARDWARE_ATTESTATION_FAILED failure when any platform verifier
  fails, which previously left overall status VERIFIED.

- Server: malformed _cmcp param (string/list/number) no longer 500s the
  call path; session_reset audit entry now records the pre-reset
  sensitivity instead of the post-reset value (forensic detail).

Tests asserting the old fail-open behavior updated to the fail-closed
contract.

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@imran-siddique
imran-siddique deleted the fix/silent-mode-contract branch July 29, 2026 23:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant