Skip to content
BasaltDBBasaltDB
Star

The ledger

View as Markdown

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.

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, HOLD or REFUSE, 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.

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.

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.

  • Old values: 30 days. This is how long an undo can restore a row.
  • Everything else about each action: 1 year.

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.

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.

BasaltDB · Apache-2.0