The ledger is the record of every action an agent asks for. It is what lets BasaltDB preview a change, stop one, ask you, and undo it.
What one record holds
Section titled “What one record holds”One record per action, whether it was applied, held or refused:
- Who. The agent, its run and its task.
- Why. The intent the agent gave.
- What. The statement and its parameters, the kind of action, the rows touched and how long it took.
- Before and after. For each row written, the old values and the new ones, cascaded rows included.
- The decision.
ALLOW,HOLDorREFUSE, and the rule behind it. - What it saw. For updates and deletes, the version of the row the run had read.
Reads through BasaltDB are recorded too. Each read keeps the rows returned and the version of each, so the ledger knows what the agent was looking at when it wrote.
Statuses
Section titled “Statuses”| Status | Meaning |
|---|---|
APPLIED |
The change ran. |
APPROVAL_PENDING |
The change is held and waits for you. |
REFUSED |
The change did not run. The record has the reason. |
APPROVAL_EXPIRED |
The hold ran out after 24 hours. |
REVERTED |
Every row of it was undone. |
The full shape is on Change record.
Where it lives
Section titled “Where it lives”In the basalt_meta schema inside your own Postgres. BasaltDB has no server of its own that holds your data. DROP SCHEMA basalt_meta CASCADE removes it. See Remove BasaltDB.
How long it is kept
Section titled “How long it is kept”- Old values: 30 days. This is how long an undo can restore a row.
- Everything else about each action: 1 year.
Tamper evidence
Section titled “Tamper evidence”Each ledger row is chained to the one before it with a hash. basalt log checks the chain and warns if it is broken. The ledger is append-only: an undo is a new record and never edits an old one.
Reading it
Section titled “Reading it”basalt log --run RUN_ID
One line per action, with the rows it wrote indented below:
2026-11-15T07:20:47Z APPLIED UPDATE basalt_demo.leads 3 rows 9ms 7c3e4f0a-5b1d-4c8e-9a62-0d1f2e3a4b5c "Qualify leads 1 to 3"
UPDATE basalt_demo.leads (blind write) {"id":1} score 37 -> 47, status NEW -> QUALIFIED
UPDATE basalt_demo.leads (blind write) {"id":2} score 74 -> 84, status NEW -> QUALIFIED
UPDATE basalt_demo.leads (blind write) {"id":3} score 11 -> 21, status NEW -> QUALIFIED
Consecutive reads collapse into one line, such as 3 reads, 42 rows: leads (2), lead_notes (1). Add --full for whole before and after rows. --json gives the raw records. An agent reads its own runs with run_history and list_runs.
For a table the agent cannot read, the record shows values hidden instead of the rows. After 30 days it shows values pruned.
- See what it did:
basalt login detail. - Runs: how actions are grouped.
- Undo: what the old values are for.
BasaltDB · Apache-2.0