Running Argon: requirements, configuration, storage, retention, and migration. Everything here reflects what the code does today.
-
MongoDB 6+ as a writable replica set. Write capture uses change streams with pre-images, which MongoDB serves only on replica sets. A single-node replica set is fine:
docker run -d --name argon-mongo -p 127.0.0.1:27017:27017 mongo:7 --replSet rs0 docker exec argon-mongo mongosh --quiet --eval \ 'rs.initiate({_id:"rs0", members:[{_id:0, host:"localhost:27017"}]})'
MongoDB 6 is the feature minimum, not a production version recommendation. Use a current supported MongoDB patch release; the
mongo:7example follows the latest available 7.x patch. -
Source builds require Go 1.26.11 or newer (1.27.4+ on the Go 1.27 line). Root, CLI and API modules declare the same minimum toolchain. CI scans all three modules with official
govulncheckagainst the current vulnerability database and fails on reachable vulnerabilities. Reproduce it withgo run golang.org/x/vuln/cmd/govulncheck@v1.9.0 ./...from each module. A clean reachable-symbol scan does not mean every unused transitive package is vulnerability-free; keep dependency and Go security patches current. -
Connection. Every Argon process (CLI, API server, MCP server, proxy) reads
MONGODB_URI(defaultmongodb://localhost:27017) and keeps its metadata inARGON_METADATA_DB(defaultargon_wal): the log (wal_log), branches, projects, LSN counters, snapshot manifests, merge plans, pins. Checked-out branches get physical databases namedargon_br_<branch-id>on the same deployment.
| Process | Run | Purpose |
|---|---|---|
argon watch -p P -b B |
one per checked-out branch you write to | captures direct writes into the WAL (resume tokens: it recovers writes made while it was down) |
(cd api && go run .) (or the built binary) |
one | REST control plane; supervises ingesters for the sandboxes it creates; HOST/PORT (default 127.0.0.1:8080) |
argon mcp |
per agent client | MCP server over stdio; supervises ingesters for its sandboxes |
argon proxy |
optional | stable project~branch connection strings on 127.0.0.1:27018 (see Exposure before listening elsewhere) |
argon sandbox sweep -p P |
schedule for CLI-only use | reap expired sandboxes (pinned ones are skipped loudly) |
argon gc -p P |
cron | reclaim covered, out-of-retention WAL entries |
argon import database copies ordinary documents with sequential cursor reads.
Stop all source application writes, jobs and DDL before starting, and keep them
stopped until completion. Pass --source-quiesced to acknowledge that contract;
the command cannot prove that an external writer is paused. --yes only skips
the target-creation confirmation. If writers resume during copying, consistency
is outside the contract: discard that result and repeat from a quiesced source.
This is not an online transactional clone or continuous source synchronization.
Indexes, validators and collection options are not copied.
argon import preview --uri "$SOURCE_MONGODB_URI" --database myapp
# Pause source writers and DDL now.
argon import database --uri "$SOURCE_MONGODB_URI" --database myapp --project myapp --source-quiesced --yes
# Source writers may resume after success.Preflight verifies readable ordinary collections before creating the target; views/time-series collections and an absent/no-collection source are rejected. The reserved target is hidden from normal project lookup/listing until every collection succeeds. A handled error or cancellation removes that unpublished project, its branches, WAL and counter transactionally, so retrying its name starts cleanly. Cleanup uses a separate context when the copy was canceled.
If the process is killed, or the metadata database was unavailable during cleanup, stop the original importer and recover explicitly:
argon import cleanup --project myapp --yesThis command accepts only an unfinished import; it refuses ordinary or completed projects. Do not run recovery concurrently with the original importer. Imports restart from the beginning; source snapshot/catch-up and resumable copies remain future capabilities. Destination metadata needs the normal writable replica set for transactional recovery, even when the quiesced source is standalone.
Snapshots are content-addressed, zstd-compressed chunks (~4 MB), deduplicated across branches and snapshots. Where chunks live is chosen by environment:
ARGON_SNAPSHOT_STORE |
Additional variables | Notes |
|---|---|---|
mongodb (default) |
— | chunks in argon_wal.wal_snapshot_chunks; zero setup |
s3 (cloud default) |
ARGON_S3_BUCKET (required), ARGON_S3_PREFIX (default argon/chunks), ARGON_S3_ENDPOINT (MinIO/R2/Ceph), plus standard AWS_* credentials |
recommended for cloud deployments |
filesystem |
ARGON_SNAPSHOT_DIR (required) |
self-hosted disks |
Snapshots happen automatically (roughly every 1,000 entries per branch,
plus immediately after imports); argon snapshot create forces one. GCS
is not yet a backend.
argon gc -p P --retention 168h deletes WAL entries that are all of:
at or below the newest snapshot that is older than the retention window and
that every future reader can use, below every live child's fork-point
coverage, and below every pin's coverage. Consequences, stated plainly:
- No snapshot → nothing is ever deleted, no matter how old.
- Reclaiming entries ends time-travel/audit/undo below the cutoff — that is what a retention window means; pick it accordingly (default 7 days). Every LSN inside the window stays readable: the cutoff is a snapshot at or below the window's start, never a point between two snapshots.
- An older position that can no longer be rebuilt is refused with
history reclaimed by garbage collection— by time-travel queries, and byrestore reset,restore branch,pin create --lsnandundobefore they change anything. It is not answered with partial data. (GC runs made by 2.1.5 and earlier are not recorded, so this protection starts with the first GC run after upgrading.) - Existing pins protect the history needed to materialize their referenced captured states. Deleting a pin releases that protection; pins do not replace independent backups.
- Deleting a branch reclaims its entries, snapshots and unshared chunks immediately (deletion is refused while the branch has live children or pins).
--dry-run reports what would be deleted, per branch and collection.
v1 logged updates as expressions and re-executed them on replay, which was not deterministic. The v2 materializer refuses v1 data entries with an error naming the fix:
argon migrate-wal --project myapp --dry-run
argon migrate-wal --project myappMigration rewrites entries in place (parents before children, LSNs preserved), is idempotent, and needs no downtime for readers of already- migrated branches.
argon status reports connectivity and system health; argon metrics
prints performance counters (operation rates, latencies, error rates). The
services log ingester lifecycle events and snapshot/GC warnings to stderr;
wal.Monitor runs periodic health checks inside every long-lived process.
Argon passes credentials through MONGODB_URI untouched. With the wire
proxy, clients must set authSource=admin explicitly (the URI database is
a branch alias, not a real database SCRAM can run against).
Run argon doctor before onboarding a deployment. It performs actual temporary
writes, a transaction across physical/metadata databases and an exact-image
change-stream probe; it removes its own probe collections afterward. Status is
read-only and reports the last persisted worker status. Neither command prints
the connection URI, which can contain credentials. Failed readiness returns a
nonzero exit code, including with --output json.
For an empty checkout, prepare collections before application writes:
argon collections prepare orders -p myapp -b experiment
argon watch -p myapp -b experiment --actor agent:run-42Driver alternative: create the collection with
changeStreamPreAndPostImages: {enabled: true}. From 2.3.1, capture reads back
the exact states of updates and deletes made before images were enabled, as long
as it processes them within MongoDB's snapshot history
(minSnapshotHistoryWindowInSeconds, five minutes by default); an update that
is followed by another change to the same document in the same transaction, in
that interval, cannot be read back.
A watcher retries temporary index-build
or topology errors; a post-image that can no longer be read back, unsupported
drop/rename or expired resume token marks capture degraded. Stop writers, inspect the reported cause
and preserve the physical data. Rebuild/import into a new branch from a trusted
state when a history gap cannot be repaired; do not clear a token to pretend
that missing history was captured.
argon console and API processes supervise live capture and sweep expired
sandboxes every minute. MCP does the same for its managed branches while the
process runs. Bare CLI checkout/sandbox commands do not create a background
worker: keep watch running and schedule sandbox sweep explicitly. Automatic
snapshots are triggered by native capture and programmatic writers; retention
GC still runs through its explicit command/service.
Stop native writers before release/reset. Release waits through a durable capture barrier; reset requires a released branch. Discard intentionally drops physical data and history, including degraded sandboxes, after pin/child checks. Use a single owner for destructive lifecycle operations; cross-process first checkout is not protected by a distributed lifecycle lease.
Snapshot publication and chunk garbage collection share persistent locks in
wal_snapshot_locks. These locks deliberately do not expire: a paused worker
must not resume uploading/publishing into chunks a second worker has deleted.
A crashed worker may therefore leave a lock behind. Stop all snapshot and GC
workers for that metadata database, verify that no former owner can resume,
inspect the lock record and remove only the confirmed abandoned lock. Restart
workers after that review. Do not attach a TTL index or automatically delete
old-looking locks; age alone cannot prove a worker is dead.
Console/API bind loopback by default. A non-loopback listener requires
ARGON_API_TOKEN unless the restricted demo mode is selected. CORS allows only
same-origin requests by default; configure trusted origins explicitly. The
hosted demo disables native checkout/sandbox credentials and is not a native
driver playground. Read-only mode is available for an inspection server.
A control plane without a token also checks the Host header: it answers to
localhost, *.localhost and loopback addresses only, and returns 403 for any
other name (/health excepted). This is what keeps a web page from reaching
your local console under its own hostname (DNS rebinding). If you reach the
console through a tunnel, forwarded port or reverse proxy that presents another
name, list it: ARGON_ALLOWED_HOSTS=console.internal,dev.example.com (hostnames
without ports; * turns the check off). Setting the variable also enforces the
list on token-protected servers.
Forwarding headers are ignored unless ARGON_TRUSTED_PROXIES names the proxies
that write them (IPs or CIDR ranges). The demo's per-client write budget is
keyed on the client address, so behind a load balancer set this to the
balancer's range; otherwise all visitors share the balancer's budget, and
without it no caller can mint a fresh budget by inventing X-Forwarded-For.
argon proxy listens on 127.0.0.1:27018 by default. It authenticates nobody
itself: every connection is forwarded to the upstream deployment, including
commands for databases that are not branch aliases. A non-loopback --listen
address is refused unless --allow-remote is passed, and should only be used
when the upstream MongoDB requires authentication — against an open
development mongod it would publish that database to the network.
Native URIs use the engine's MongoDB credentials. They are suitable only for
trusted clients sharing those database permissions. For mutually untrusted
agents, provision restricted users or separate deployments; API bearer auth
does not restrict a returned MongoDB credential. Changing the URI's database
preserves its original SCRAM authentication database via authSource.
Capture status may include last_captured_at, last_event_at, head_lsn and
last_event_lag_ms. Lag is the last event wall-clock-to-checkpoint sample, not
a continuously measured queue lag or latency guarantee.