lacs-project / lacs-project/sysknife
audit verify says OK over a wiped log, so an empty store and a deleted one read the same
- Dominant language
- Rust
- Stars
- 12
- Forks
- 19
- Avg merge
- 18h 57m
- Merged PRs (30d)
- 116
Description
`sysknife audit verify` reports success over a database whose rows have all been deleted.
```
$ sqlite3 daemon.sqlite 'DELETE FROM transactions; DELETE FROM audit_events;'
$ sysknife audit verify
OK: 0 row(s) verified in .../daemon.sqlite
NOTE: no independent checkpoint anchor is configured, so removal of the newest rows
would not be detectable — a truncated chain still verifies. Set SYSKNIFE_CHECKPOINT_DB
and run `sysknife audit checkpoint` periodically; see docs/the-audit-chain.md.
OK: 0 approval event(s) verified
OK: 0 row(s) still match the approval event they committed to
$ echo $?
0
```
Three previously signed actions were deleted. The verdict is `OK` and the exit code is 0.
## Why this is not simply a bug
A forward hash chain cannot detect the removal of its own tail without an external anchor. That is what checkpoint anchoring exists for, the tool says so in the NOTE, and `docs/the-audit-chain.md` explains it. Nothing here is hidden.
The problem is what an operator reads. Two very different states produce the same headline:
- a fresh install where nothing has run yet
- a host where the entire trail was deleted
Both print `OK` and exit 0. The one line that distinguishes them is a NOTE the reader has to interpret, below a verdict that already told them everything is fine.
## Scope
Make the verdict carry the distinction the tool already knows:
- **Zero rows and no anchor configured** is not `OK`. It is "nothing to verify, and this command cannot tell an empty log from a deleted one". A distinct exit code, or at minimum a different word than `OK`, gives a monitoring script something to alert on.
- **Zero rows with an anchor configured** genuinely is verifiable: the anchor either names a tip that should exist or it does not. That case can stay `OK` or become an explicit failure, and the anchor is what makes the difference.
- The `--json` shape should carry the same distinction, since that is what automation reads.
Worth considering alongside it: whether the daemon should anchor on a schedule by default rather than only when `SYSKNIFE_CHECKPOINT_DB` is set, so the mitigating control is not opt-in on a product whose subject is tamper evidence. That is a bigger decision and can be split out.
## Tests first
A test that writes rows, deletes them, and asserts the verdict is not the same one a fresh store produces. Then a second that configures an anchor and asserts the truncation is caught, so the fix is proved to add detection rather than only to change wording.
## Difficulty
`medium`. The verdict logic is small; deciding the exit-code contract is the part that needs care, and #335 is already open on exit codes generally.
## Getting started
[CONTRIBUTING.md](https://github.com/lacs-project/sysknife/blob/main/CONTRIBUTING.md) has the build and test commands, and [docs/the-audit-chain.md](https://github.com/lacs-project/sysknife/blob/main/docs/the-audit-chain.md) covers what the chain does and does not protect. No CLA and no copyright waiver. The project is MIT.
Contributor guide
Research direction
Read CONTRIBUTING.md and docs/the-audit-chain.md, then run the sqlite reproduction and locate the implementation of `sysknife audit verify`, including its JSON and exit-code handling. Done means a fresh empty store and a wiped store without an anchor produce distinct results, an anchored truncation is detected, and the same distinction appears in `--json` output.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust, sqlite
- Domain
- cli, databases, security
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 52/100