Skip to content

Bases API

A base is the source a branch is cloned from. See the API overview for authentication, errors and operations. Calls for a base that lives on another node are passed on to that node automatically.

{
"name": "prod",
"description": "Masked production data, refreshed every Monday",
"runtime": "postgres@18.6",
"status": "ready",
"status_reason": null,
"node": "node1",
"port": 40100,
"created_at": "2026-11-01T09:00:00Z",
"latest_snapshot": "s20261109",
"snapshots": [
{ "name": "s20261109", "created_at": "2026-11-09T03:00:00Z", "public": false }
],
"replicas": { "s20261109": ["node2"] },
"replica_nodes": null,
"holders": [
{ "node": "node1", "state": "held", "origin": true },
{ "node": "node2", "state": "held", "origin": false }
],
"operation": null,
"can": { "base.snapshot": true, "base.delete": true }
}
Field Meaning
status provisioning, ready, maintenance or failed
node The node the base lives on. Its branches start from here or from a replica
snapshots[].public Whether anyone may create a branch from the snapshot without logging in
snapshots[].mask Present on a snapshot made by masking: how it was made
replicas For each snapshot, the other nodes that hold a copy
replica_nodes The setting: null keeps a copy on every node, a list names the other nodes that keep one
holders[].state held, pulling (copying now) or draining (giving up its copy)
can Which base actions the caller may do on this base. Used by the console to show buttons

Action base.read. Lists bases, sorted by name. GET /v1/bases/{name} (same action) returns one.

Action base.public. Lists the bases that have a public snapshot. Works without a login.

Action base.create. Creates a base and takes its first snapshot. Answers 202, operation kind base.create.

Field Type Meaning
name string, required Base name
runtime string, required engine@version, installed on the node
dump string A .sql or .sql.gz dump to load: an upload ID (upload:..., see below) or a path on the node. Omit for an empty base
description string Free text, up to 1000 characters
node string Node to create it on. The path in dump is read there. Default: the node you called
replica_nodes array or null null or absent: every node keeps a copy. []: only the base’s node. A list: those nodes as well

Errors: 409 already_exists, 404 runtime_not_installed.

Action base.describe. Changes the description. Answers 200 with the base.

{ "description": "Masked production data" }

Action base.delete. Answers 204. A base that branches still use returns 409 in_use.

Actions base.start and base.stop. Start or stop the base’s database server. Answer 204.

Action base.snapshot. Stops the base server cleanly, takes a new snapshot, starts it again, and copies the snapshot to the nodes that keep replicas. Answers 202, operation kind base.snapshot. If the server does not stop cleanly, the call fails with unclean_shutdown and no snapshot is taken.

PATCH /v1/bases/{name}/snapshots/{snapshot}

Section titled “PATCH /v1/bases/{name}/snapshots/{snapshot}”

Action base.publish. Makes a snapshot public or private. Answers 200 with the base.

{ "public": true }

DELETE /v1/bases/{name}/snapshots/{snapshot}

Section titled “DELETE /v1/bases/{name}/snapshots/{snapshot}”

Action base.snapshot_delete. Answers 204. The latest snapshot, and snapshots that branches use, cannot be deleted (409 conflict for the latest, 409 in_use for the others). Older unused snapshots are also pruned automatically when the Prune old snapshots setting is on.

Action base.mask. Writes a masked copy of another base into this (empty) base and snapshots it. The masking is done by a masking tool (a module such as Greenmask), which must be installed. Answers 202, operation kind base.mask.

Field Type Meaning
source string, required Base to copy from
snapshot string Snapshot of the source. Default: its latest
masker string, required Name of the masking tool module, for example greenmask
rules string Masking rules, in the format the masking tool expects

Action base.sql. Runs SQL as the administrator on the base server (started if it is stopped) to change data before the next snapshot. Answers 204. A failing statement returns 422 query_failed.

{ "sql": "INSERT INTO shop.products (name) VALUES ('new item');" }

Action base.load. Loads a dump on top of the base’s current data. Answers 202, operation kind base.load.

Field Type Meaning
dump string, required An upload ID or a path on the base’s node
snapshot boolean Take a snapshot after loading

Existing branches are not affected.

Dumps can be sent over the API instead of copying them to a node.

Action base.upload. The request body is the file itself (not JSON). It needs a Content-Length header and the query parameters name (the file name) and, optionally, node. The upload is stored on that node, and the ID refers to it until it is used by POST /v1/bases or /load.

Terminal window
curl -sk -H "$AUTH" -H 'Content-Type: application/gzip' \
--data-binary @prod.sql.gz "$DBB/uploads?name=prod.sql.gz"
# {"id":"upload:01J...","name":"prod.sql.gz","size":104857600,"node":"node1"}

Errors: 411 length_required, 413 too_large (above the Largest dump upload setting), 507 no_space (not enough room on the node’s ZFS pool).

Action base.upload. Returns how large an upload this node accepts now: {"node": "node1", "max_bytes": 21474836480, "reason": "limit"}. reason is limit (the setting) or pool (free space).

A base can be refreshed from a source database on a schedule: dump the source, load it and take a snapshot. every must be at least 1h.

Action base.refresh.read. Returns the schedule. 404 not_found if none is set. The source password is never returned.

{
"base": "prod",
"engine": "postgres",
"source": { "host": "db.internal", "port": 5432, "user": "reader", "password_set": true, "databases": ["app"] },
"every": "24h0m0s",
"updated_at": "2026-11-09T12:00:00Z",
"last_run": { "started_at": "2026-11-10T03:00:00Z", "finished_at": "2026-11-10T03:12:00Z", "ok": true, "error": null },
"next_run": "2026-11-11T03:00:00Z",
"operation": null
}

Action base.refresh. Sets or changes the schedule. Answers 200 with the schedule above. Without password, the stored password is kept.

{
"source": { "host": "db.internal", "port": 5432, "user": "reader", "password": "...", "databases": ["app", "billing"] },
"every": "24h"
}

Action base.refresh. Turns the schedule off. Answers 204.

Action base.refresh. Refreshes now. Answers 202, operation kind base.refresh.

Action base.replicas. Chooses which nodes keep a copy of the base. The body must contain replica_nodes: null for every node, [] for the base’s own node only, or a list of node names. Answers 200 with the base. Nodes that are dropped give up their copy once no branch on them uses the base.

{ "replica_nodes": ["node2"] }