OMGDB DOCS
// AI-native

Agent-Safe Mutations

Plan, apply, and roll back data changes safely — a dry-run produces a reviewable token, apply commits in one transaction, and rollback restores the recorded before-state.


Letting an autonomous agent mutate a database directly is risky: a bad filter or a wrong operator can rewrite documents with no easy way back. OMGDB makes mutations reviewable and reversible by splitting a change into three explicit steps — plan, apply, and rollback — instead of mutating blindly.

A plan is a dry run that writes nothing: it computes which documents would change and a before/after sample, then persists a legible plan file keyed by an opaque token. A human or a supervising agent inspects that plan, applies it by token in a single transaction, and can later restore the recorded before-state. Because the op-log already records every operation, this audit/undo layer is a thin, natural addition rather than a bolt-on.

The workflow

The three steps map to three CLI subcommands and three MCP tools. Each plan is identified by a single opaque token (an ObjectId hex string) that is produced by plan-update and reused unchanged as the change id by apply.

StepCommandWrites data?Reversible
Planomgdb plan-update <path> <collection> <filter> <update>No (dry run)n/a
Applyomgdb apply <path> <token>Yes, one transactionYes, via rollback
Rollbackomgdb rollback <path> <change_id>Yes, one transactionRestores recorded before-state

The update document uses the same update operators as everywhere else in OMGDB — $set, $unset, $inc, $mul, $min, $max, $rename, and the array operators $push/$addToSet/$pull/$pop. The filter uses the standard query operators.

1. Plan a change (dry run)

omgdb plan-update app.omgdb <collection> '<filter>' '<update>'

plan-update compiles the filter, scans the collection, and for each matching document computes the resulting after document without writing anything to the store. It persists the plan as one NDJSON record per matched document under app.omgdb/pending/<token>.ndjson, then prints a summary as canonical JSON:

{"token":"<token>","matched":3,"sampleBeforeAfter":[{"before":{...},"after":{...}}]}
  • token — the opaque identifier you pass to apply.
  • matched — the full count of documents the change would affect.
  • sampleBeforeAfter — up to three {before, after} pairs so a reviewer can see the exact effect, even when more documents match.

The plan file itself is plain NDJSON you can cat: each line is a canonical-JSON object {ns, id, before, after}, holding both the original and the proposed document.

Note: During planning, the original _id is re-inserted into every after document after the update runs. No update operator can ever change a document’s identifier.

2. Apply the plan

omgdb apply app.omgdb <token>

apply reads app.omgdb/pending/<token>.ndjson and replaces every recorded document inside a single store transaction (replace_one per record). The operation is all-or-nothing: if any replacement fails, none are committed. On success the plan file is moved from pending/ to app.omgdb/changes/<token>.ndjson, where it becomes a cat-able audit-and-undo record (it holds both before and after for every affected document). The command prints the result as canonical JSON:

{"changeId":"<token>","applied":3}

changeId is the same value as the original token — they are not distinct identifiers. An unknown token fails with a not-found error.

3. Roll back an applied change

omgdb rollback app.omgdb <change_id>

rollback reads app.omgdb/changes/<change_id>.ndjson and, in a single transaction, restores each document’s recorded before state. It prints the number of documents restored:

rolled back 3 document(s)

Note: Rollback leaves the change file in place and is not itself recorded as a new change. Re-running it simply restores the same before-state again; there is no forward “redo” of a rolled-back change.

Worked example

Insert a document, plan a change, apply it, then roll it back. This is the end-to-end flow exercised by the engine’s plan_apply_rollback_round_trip test.

# Create a store and insert a document.
omgdb create app.omgdb
omgdb insert app.omgdb u '{"name":"ada","age":30}'

# 1. Plan: set role=admin and increment age — nothing is written yet.
omgdb plan-update app.omgdb u '{"name":"ada"}' '{"$set":{"role":"admin"},"$inc":{"age":1}}'
# -> {"token":"<token>","matched":1,"sampleBeforeAfter":[
#      {"before":{"_id":...,"name":"ada","age":30},
#       "after":{"_id":...,"name":"ada","age":31,"role":"admin"}}]}

# The store is still unchanged: age is 30 and role is absent.
omgdb get app.omgdb u '{"$oid":"..."}'

# 2. Apply the plan by its token, in one transaction.
omgdb apply app.omgdb <token>
# -> {"changeId":"<token>","applied":1}

# Now age == 31 and role == "admin".
omgdb get app.omgdb u '{"$oid":"..."}'

# 3. Roll back by the change id (same value as the token).
omgdb rollback app.omgdb <token>
# -> rolled back 1 document(s)

# Restored: age == 30, role absent again.
omgdb get app.omgdb u '{"$oid":"..."}'

Audit trail

Applied changes accumulate as NDJSON files under app.omgdb/changes/, one per change id. Each file records both the before and after document for every row the change touched, so the directory is a legible, append-only audit log you can inspect with ordinary tools. These sidecar directories (pending/ and changes/) are bundled into single-file archives by omgdb pack alongside the op-log, so a packed store carries its pending plans and undo history with it.

This layer builds on the op-log, which already records every committed operation as the storage durability foundation — the plan/apply/rollback files add the before/after capture needed for human-reviewable, undoable agent edits.

MCP equivalents

The same workflow is exposed to agents over the Model Context Protocol. The omgdb mcp server advertises three matching tools:

MCP toolArgumentsRequired scope
plan_updatepath, collection, update, optional filterread-write
applypath, tokenread-write
rollbackpath, change_idread-write

All three require read-write scope. A server started with --scope read does not advertise them in tools/list and refuses them at call time, so an untrusted agent can be handed a database it is structurally unable to mutate. plan_update returns the same {token, matched, sampleBeforeAfter} summary as the CLI and writes no data; apply commits a planned change by token; rollback reverses an applied change by id. See MCP integration for scope details and the full tool catalog.

Tip: A safe agent pattern is to run plan_update, surface the matched count and sampleBeforeAfter to a human (or a higher-trust reviewer), and only then call apply with the returned token — keeping rollback available if the applied change turns out to be wrong.

Edit this page on GitHub →