sysknife_audit_verify
Verify the integrity of the audit log by validating its Ed25519-signed hash chain. Returns status (intact/broken/cannot_verify), rows checked, and the first offending row if tampering is detected.
Instructions
Verify the tamper-evident Ed25519-signed hash chain over the audit log. Returns status (intact/broken/cannot_verify), rows_checked, and, on broken, the first offending row. Read-only and safe to call without prior sysknife_plan.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
| actual | No | The hex Ed25519 signature actually stored for the first broken row. | |
| reason | No | Human-readable explanation. Only set when `status == "cannot_verify"`. | |
| status | Yes | One of `"intact"`, `"broken"`, `"cannot_verify"`. | |
| backend | Yes | Backend label: a filesystem path for SQLite, the literal `"postgres"` for Postgres deployments. | |
| expected | No | What verification expected for the first broken row (the literal `"valid ed25519 signature"`). | |
| chain_status | Yes | The transaction chain's own verdict: `"intact"`, `"broken"` or `"cannot_verify"`. Reported separately because `status` is the worst of three checks, so a broken *approval-event* chain sets `status` to `"broken"` while this stays `"intact"`. Read this one, not `status`, to decide whether the attribution counts below are findings or claims: without it an agent had no way to recover the chain verdict and would discard sound attribution. | |
| rows_checked | Yes | Number of audit rows the verifier successfully checked. `0` for `cannot_verify` outcomes that fail before the first row is read. | |
| rows_censused | No | How many rows were censused for attribution: every row read, whether or not it verified. `null` when the store could not be read at all, along with every count below, so a database nobody could open never reads as one where nothing was found. A readable but empty store reports `0`. When `chain_status` is not `"intact"` this can exceed `rows_checked`, and the difference is the part of the trail that was counted but not proven. | |
| binding_status | Yes | `"consistent"` or `"missing_event"`: whether every event tip committed by a transaction row is still present in the event chain. | |
| events_checked | Yes | Number of approval events (grant / consume / revoke) checked in the second chain. | |
| attributed_rows | No | How many rows have a signed principal naming an account: a non-empty value under the `uid` or `token` scheme, which this build could read back as something the daemon itself could have written. Only a finding when `chain_status` is `"intact"`. Past a detected break the walk stopped checking, so those rows' principals are claims: some may be authentic, since deleting or reordering a row breaks the link while leaving later signatures valid, and this tool cannot say which. | |
| rows_unattested | No | How many rows have no principal any signature vouches for: the column is populated on an encoding that does not sign it, or holds a value this build cannot read back as one the daemon could have written, or the row declares an encoding this build does not know. This build writes none of those. The first two are out-of-band writes to investigate; the third means a newer SysKnife wrote the rows and the fix is to verify with a build at least that new. | |
| first_broken_seq | No | Sequence number of the first row that broke the chain. Only set when `status == "broken"`. | |
| unattributed_rows | No | How many rows record that the daemon could not name the caller. `chain_status: "intact"` with a non-zero count here means the chain is sound and the attribution is not: report both, never the first alone. Since 0.4.0 this counts only rows whose `chain_version = 3` principal is signed as `none:unattributed`. 0.3.0 matched the column on any encoding, which meant an unsigned column could land here; such rows are now `rows_unattested`. | |
| daemon_socket_caveat | No | Set when `SYSKNIFE_SOCKET` names a daemon that may not live on this machine, because verification reads a local store while every other tool travels over that socket. `None` for the local-daemon case. | |
| approval_events_status | Yes | Result of the approval-event chain walk: `"intact"`, `"broken"`, or `"cannot_verify"`. Reported separately from `status` so a clean authorisation trail can never paper over a tampered approval trail. | |
| rows_naming_no_account | No | How many rows name no account, for any reason. The complement of `attributed_rows` over `rows_censused`, provided so a reader does not have to add the three reasons and risk missing one. | |
| rows_without_principal | No | How many rows carry no principal the signature covers, normally because they were signed before the column existed. Reported next to `unattributed_rows` because zero attribution failures over a pre-v3 database would otherwise read as full attribution. The two have different remedies: this one cannot be fixed, since backfilling a principal would rewrite the bytes the signature covers. | |
| first_broken_transaction_id | No | Transaction ID of the first broken row. Only set when `status == "broken"`. |