feat: durable persistence + verifiable audit log + operator CLI (#126, #127, #147, #124)#148
Open
dgenio wants to merge 2 commits into
Open
feat: durable persistence + verifiable audit log + operator CLI (#126, #127, #147, #124)#148dgenio wants to merge 2 commits into
dgenio wants to merge 2 commits into
Conversation
…#127, #147, #124) Add a coherent "durable, verifiable, operable audit trail" feature set: - #126 Pluggable persistence: new weaver_kernel.stores package with TraceStoreProtocol / RevocationStoreProtocol / HandleStoreProtocol and stdlib-only durable backends (SQLiteTraceStore, JsonlTraceStore, SQLiteRevocationStore). In-memory stores remain the defaults; backends are injected via constructor. Revocation now lives behind a protocol on HMACTokenProvider, so a revoked token stays revoked across a restart when a SQLiteRevocationStore is used. - #127 Hash-chained verifiable audit log: prev_hash/record_hash envelope (HMAC-SHA256, keyed by WEAVER_KERNEL_SECRET), verify_chain() detecting mutation/insertion/deletion/reorder, and SQLiteTraceStore.prune() that keeps the retained suffix verifiable via a checkpoint. Tamper-evidence, not non-repudiation (documented). - #147 + #124 weaver-kernel CLI: `audit list|show|verify|export` over a persisted store (redaction-safe, --json everywhere, non-zero exit on tamper) and `doctor` preflight checks with token/audit self-test vectors. argparse, stdlib-only, registered via [project.scripts]. Supporting changes: - Extract HMAC secret loading to weaver_kernel._secrets (shared by token signing and audit-chain hashing) to avoid an import cycle. - Type Kernel(trace_store=...) against TraceStoreProtocol. - Fix a latent grant-cap bypass in HandleStore.expand surfaced by the existing property test: a negative limit/offset is now rejected with INVALID_CONSTRAINT instead of slicing past max_rows. - Docs (architecture, security integrity model, new docs/cli.md), CHANGELOG (0.11.0), and an offline examples/persistent_audit_demo.py wired into `make example`. make ci: 639 passed, 1 skipped; fmt-check, lint, mypy --strict, examples green. https://claude.ai/code/session_014vcsXSCqnFprxCkMiBQGtY
There was a problem hiding this comment.
Pull request overview
This PR delivers a cohesive “durable → verifiable → operable” audit trail feature set: pluggable persistence backends for traces/revocation, a hash-chained audit envelope with verification/pruning, and an operator-facing weaver-kernel CLI (audit + doctor). It also includes a handle-store constraint hardening fix (negative pagination) plus docs/examples/releases updates to ship the new functionality.
Changes:
- Add
weaver_kernel.storeswith protocol seams and stdlib-only durable backends (SQLite + JSONL), plus durable token revocation. - Implement hash-chained trace records with
verify_chain()and SQLite pruning checkpoints; expose chain verification via CLI. - Add
weaver-kernelCLI (audit list|show|verify|export,doctor) and update docs/examples/changelog/versioning.
Reviewed changes
Copilot reviewed 31 out of 31 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_tokens.py | Adjusts dev-secret warning test to the new shared secret loader module. |
| tests/test_stores_sqlite.py | Adds SQLite store tests: round-trip, reopen persistence, chain verification, pruning, revocation durability. |
| tests/test_stores_jsonl.py | Adds JSONL trace store tests including tamper detection and blank-line handling. |
| tests/test_handles.py | Adds regression tests rejecting negative offset/limit in handle expansion. |
| tests/test_cli_doctor.py | Adds tests for weaver-kernel doctor text and JSON modes. |
| tests/test_cli_audit.py | Adds tests for weaver-kernel audit list/show/verify/export behaviors and tamper exit codes. |
| tests/test_audit_chain.py | Adds unit tests for record hashing, chain verification, and pruning checkpoint logic. |
| src/weaver_kernel/tokens.py | Refactors secret loading + extracts revocation behind a protocol with durable backend support. |
| src/weaver_kernel/stores/sqlite.py | Implements SQLiteTraceStore + SQLiteRevocationStore including chain and pruning checkpoint. |
| src/weaver_kernel/stores/memory.py | Adds InMemoryRevocationStore (default revocation backend for tokens). |
| src/weaver_kernel/stores/jsonl.py | Implements append-only JSONL trace store with chain support. |
| src/weaver_kernel/stores/audit_chain.py | Adds canonical hashing, TraceRecord, and verify_chain() implementation. |
| src/weaver_kernel/stores/_trace_codec.py | Implements encode/decode between ActionTrace and the persisted/export payload shape. |
| src/weaver_kernel/stores/_protocols.py | Defines TraceStoreProtocol, RevocationStoreProtocol, HandleStoreProtocol. |
| src/weaver_kernel/stores/init.py | Public export surface for store protocols, backends, and chain helpers. |
| src/weaver_kernel/kernel/_invoke.py | Retypes trace recording helpers to accept TraceStoreProtocol. |
| src/weaver_kernel/kernel/init.py | Allows injecting any TraceStoreProtocol into Kernel. |
| src/weaver_kernel/handles.py | Rejects negative pagination inputs to prevent grant-cap bypass. |
| src/weaver_kernel/cli/_doctor.py | Implements weaver-kernel doctor preflight checks and self-test vectors. |
| src/weaver_kernel/cli/_audit.py | Implements weaver-kernel audit command group for listing/showing/verifying/exporting traces. |
| src/weaver_kernel/cli/init.py | Adds CLI entry point, parsing, and kernel-error handling. |
| src/weaver_kernel/_secrets.py | Centralizes secret resolution with one-time dev fallback warning. |
| src/weaver_kernel/init.py | Re-exports new stores/protocols/verification helpers from the top-level package. |
| pyproject.toml | Bumps version to 0.11.0 and registers weaver-kernel console script. |
| Makefile | Adds the new persistent audit demo to make example. |
| examples/persistent_audit_demo.py | Adds an offline demo of durable audit + tamper detection + durable revocation. |
| docs/security.md | Documents the hash-chain integrity model and its limits (tamper-evidence, not non-repudiation). |
| docs/cli.md | Adds CLI documentation for audit and doctor. |
| docs/architecture.md | Documents protocol-based persistence seams and backend selection guidance. |
| CHANGELOG.md | Adds 0.11.0 release notes covering persistence, chain verification, CLI, and related changes. |
| AGENTS.md | Updates repo layout map and doc index to include stores/ and cli/. |
…ne tz, missing-store guard Resolve all 7 review comments on PR #148: - Remap stdlib parsing/JSON errors to AgentKernelError so tampered or corrupt store data surfaces as a typed, CLI-renderable error instead of a bare ValueError/JSONDecodeError traceback: - stores/_trace_codec.decode_trace: catch ValueError/TypeError (bad invoked_at / sensitivity) in addition to KeyError. - stores/sqlite: shared _loads_payload() helper used by get(), list_all(), and list_records(); also rejects non-object JSON. get() names the action_id. - stores/jsonl._iter_records: remap with file:line context. - stores/sqlite.prune(): normalise `before` to UTC (naive assumed UTC) so the lexicographic comparison against stored UTC ISO-8601 timestamps is correct. - cli/_audit.open_trace_store(): error if the --store path does not exist (except ":memory:") so `audit verify` can't falsely report OK on a freshly created empty store from a mistyped path. Adds regression tests (test_trace_codec.py + corruption/prune/missing-store cases). make ci: 647 passed, 1 skipped; lint, mypy --strict, examples green. https://claude.ai/code/session_014vcsXSCqnFprxCkMiBQGtY
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What changed
A single coherent feature set — "make the audit trail durable, then provable, then operable" — implementing the four issues from the triage's recommended group. The four are mutually dependent (#127 builds on #126; the CLI reads #126/#127;
doctorandauditshare one CLI skeleton), so they belong in one PR.#126 — Pluggable persistence for the stateful stores
weaver_kernel.storespackage withTraceStoreProtocol,RevocationStoreProtocol,HandleStoreProtocol(mirroring the existingDriver/PolicyEngineprotocol seams).sqlite3/json, no new runtime dep):SQLiteTraceStore,JsonlTraceStore(append-only),SQLiteRevocationStore.TraceStore/HMACTokenProviderremain the defaults — opt in via constructor injection.RevocationStoreProtocolonHMACTokenProvider; a token revoked throughSQLiteRevocationStorestays revoked after a process restart.#127 — Hash-chained, verifiable audit log
prev_hash/record_hashenvelope (HMAC-SHA256 keyed byWEAVER_KERNEL_SECRET), separate fromActionTracesemantics; the wrapped payload is the redaction-safeexport_action_traceshape, so chaining adds no field the trace didn't already hold (I-01 preserved).verify_chain()detects mutation, insertion, deletion, and reordering, reporting the first divergent record.SQLiteTraceStore.prune(before=...)enforces retention while keeping the retained suffix verifiable via a checkpoint.docs/security.md: tamper-evidence, not non-repudiation.#147 + #124 —
weaver-kerneloperator CLIweaver-kernel audit list|show|verify|exportover a persisted store: filters (--principal/--capability/--status/--since/--until/--limit),--jsonon every subcommand, redaction-safe output by construction, non-zero exit on tamper.weaver-kernel doctor: environment + optional-extras checks plus token sign/verify and audit-chain self-test vectors; exits non-zero only on a real error, warns (not fails) on a missing secret.[project.scripts]. Documented in newdocs/cli.md.Supporting changes
weaver_kernel._secrets(shared by token signing and audit hashing; avoids atokens↔storesimport cycle).Kernel(trace_store=...)typed againstTraceStoreProtocol.test_handle_expand_never_exceeds_grantproperty test surfaced a latent grant-cap bypass inHandleStore.expand— a negativelimit(withmax_rows=0) slicedrows[0:-1]and returned a row. Negativeoffset/limitare now rejected withINVALID_CONSTRAINT, plus a deterministic regression test. Unrelated to the persistence work but fixed here because it blocked CI and is a real grant-enforcement bug.architecture.mdpersistence section,security.mdintegrity model, newdocs/cli.md, AGENTS.md map),CHANGELOG.md(0.11.0), and an offlineexamples/persistent_audit_demo.pywired intomake example.Why
These four issues form the triage's recommended combined group: a durable, verifiable, operable audit trail. They share the same modules (
trace.py/newstores/,tokens.pyrevocation, a new CLI) and a hard dependency chain, so shipping them together avoids building-then-re-touching the same seams.How verified
make ci(the single authoritative command) — green end-to-end:fmt-check(ruff format) — cleanlint(ruff) —All checks passed!type(mypy--strict, 54 source files) —Success: no issues foundtest— 639 passed, 1 skipped (≈46 new tests across chain, SQLite, JSONL, CLI, doctor, handle regression)example— all examples run offline, including the newpersistent_audit_demo.pyNew tests prove the headline acceptance criteria: SQLite/JSONL round-trip + persistence across reopen; chain detects mutation/insertion/deletion/reorder/wrong-secret; prune-then-verify; revoked token stays revoked for a fresh provider; CLI list/show/verify(tamper→exit 1)/export; doctor self-test vectors.
Scope notes / caveats
--status(not--decision allow|deny|ask) is the audit filter because denials are never traced (policy gates before invocation, per I-02) — code-true to the data model; documented indocs/cli.md.Closes #126, #127, #147, #124.
https://claude.ai/code/session_014vcsXSCqnFprxCkMiBQGtY
Generated by Claude Code