Reference
The complete Snap surface: CLI commands, REST endpoints, the in-session SQL meta-commands, and the proxy’s own environment variables for self-hosting.
All Snap commands live under dst snap. They act in your active workspace — set it once with dst workspace switch <name>. Install with:
curl -fsSL https://get.destesi.io/install.sh | shRemotes
Section titled “Remotes”dst snap remote postgres <dsn> — register a Postgres database as a remote.
| Flag | Description |
|---|---|
--name |
Optional display name |
--tunnel-mode |
none (default) or ssh |
--tunnel-bastion-host |
SSH bastion hostname |
--tunnel-bastion-user |
SSH bastion user |
--tunnel-remote-host |
Database host reachable from the bastion |
--tunnel-remote-port |
Database port reachable from the bastion |
--tunnel-key-path |
Local path to an SSH private key (.pem) |
--tunnel-key-secret-ref |
Reference to a pre-uploaded SSH key (secret://…) |
--output |
text (default) or json |
dst snap remote s3 <bucket> — register an S3-compatible bucket as a remote.
| Flag | Description |
|---|---|
--name |
Optional display name |
--region |
S3 region (defaults to us-east-1, or $AWS_REGION) |
--endpoint |
Custom endpoint for S3-compatible storage (MinIO, R2) |
--access-key-id |
Access key (or $AWS_ACCESS_KEY_ID) |
--secret-access-key |
Secret key (or $AWS_SECRET_ACCESS_KEY) |
--path-style |
Use path-style addressing (for MinIO) |
--output |
text (default) or json |
dst snap remote list — list remotes in the active workspace.
dst snap remote show <id> — show one remote’s details.
dst snap remote remove <id> — remove a remote from the workspace.
Branches
Section titled “Branches”dst snap try <postgres-url> — one-shot: register a remote, create a branch, wait for it, and print the DSN.
| Flag | Description |
|---|---|
--as |
Name for the new branch (default: auto-generated) |
--wait-timeout |
Max time to wait for provisioning (default 5m) |
--quiet |
Print only the final DSN (for eval $(…)) |
dst snap create <name> — create a branch by cloning from a remote or forking from a branch.
| Flag | Description |
|---|---|
--connector-id |
Remote to clone from (required unless --from-snap) |
--from-snap |
Fork from an existing branch instead (inherits its remote) |
--max-lag |
Maximum acceptable source sync lag, in seconds |
--at-checkpoint |
Pin to a specific checkpoint ID |
--override |
Column override table.column=sql_expr, repeatable |
--overrides-file |
Path to a JSON file with the full override spec |
dst snap list — list branches in the active workspace.
dst snap status <id> — show a branch’s status and endpoint DSN.
dst snap switch <id> — set the active branch and save its DSN to local config.
dst snap delete <id> — delete a branch and release its resources.
dst snap trace <snap-id> — print recent SQL trace events from a branch’s proxy. Requires SNAP_TRACE_SQL=1 on the branch; uses psql if present, otherwise prints the command to run. --print only prints the command.
Fixtures
Section titled “Fixtures”dst snap fixture save <snap-id> — pin a ready branch as a named fixture.
| Flag | Description |
|---|---|
--name |
Fixture name, unique within the workspace (required) |
--desc |
Optional description |
dst snap fixture list — list fixtures in the active workspace.
dst snap fixture show <id-or-name> — show a fixture by ID or name.
dst snap fixture restore <id-or-name> — fork a new branch from a fixture.
| Flag | Description |
|---|---|
--as |
Name for the new branch (required) |
--max-lag |
Maximum acceptable source sync lag, in seconds |
dst snap fixture delete <id-or-name> — delete a fixture. The source branch is unaffected.
Override rules
Section titled “Override rules”Override rules rewrite specific columns as rows are copied from the source — for PII masking, flipping feature flags, or filtering to one tenant. Rules are attached at branch-create time and baked into the sandbox; they cannot be changed later.
dst snap create agent/safe-sandbox \ --connector-id conn_a1b2c3d4 \ --override "users.email='me@example.com'" \ --override "users.is_admin=false"dst snap create agent/safe-sandbox \ --connector-id conn_a1b2c3d4 \ --overrides-file ./overrides.jsonoverrides.json:
{ "tables": [ { "table": "users", "set": { "email": "'me@example.com'", "is_admin": "false" }, "where": "role = 'customer'" } ]}Each set value is a raw SQL expression evaluated against the source row — quote string literals ('...'), use false / NULL, or reference other columns (first_name || '@example.com'). The optional where predicate scopes which rows get masked; every source row is still copied, and non-matching rows keep their original values. --override flags merge with --overrides-file, and flags win on conflict.
Local config
Section titled “Local config”The CLI stores config under ~/.destesi/: the API URL and auth token, the active workspace, and the active branch DSN (set by dst snap switch).
REST API
Section titled “REST API”All endpoints are JSON over HTTPS at https://api.destesi.io. Protected endpoints require a Bearer token:
Authorization: Bearer <your-token>List endpoints are scoped by an identity_workspace_id query parameter.
Connectors (remotes)
Section titled “Connectors (remotes)”| Method | Path | Description |
|---|---|---|
POST |
/v1/connectors |
Register a connector (Postgres or S3) |
GET |
/v1/connectors?identity_workspace_id=… |
List connectors in a workspace |
GET |
/v1/connectors/{id} |
Get connector details |
DELETE |
/v1/connectors/{id}?identity_workspace_id=… |
Remove the connector from a workspace |
Snaps (branches)
Section titled “Snaps (branches)”| Method | Path | Description |
|---|---|---|
POST |
/v1/snaps |
Create a branch (clone or fork) |
GET |
/v1/snaps?identity_workspace_id=… |
List branches in a workspace |
GET |
/v1/snaps/{id} |
Get a branch’s details and DSN |
DELETE |
/v1/snaps/{id} |
Delete a branch |
POST |
/v1/snaps/{id}/switch |
Mark a branch active |
POST |
/v1/snaps/{id}/promote |
Pin a ready branch as a fixture |
Fixtures
Section titled “Fixtures”| Method | Path | Description |
|---|---|---|
GET |
/v1/fixtures?identity_workspace_id=… |
List fixtures in a workspace |
GET |
/v1/fixtures/{id} |
Get fixture details |
DELETE |
/v1/fixtures/{id} |
Delete a fixture |
POST |
/v1/fixtures/{id}/instantiate |
Fork a new branch from a fixture |
Checkpoints & operations
Section titled “Checkpoints & operations”| Method | Path | Description |
|---|---|---|
GET |
/v1/checkpoints?connector_id=… |
List checkpoints for a connector |
GET |
/v1/checkpoints/{id} |
Get checkpoint details |
GET |
/v1/operations/{id} |
Poll an operation (pending, running, completed, failed) |
Example: create a branch
Section titled “Example: create a branch”// POST /v1/snaps{ "identity_workspace_id": "ws_abc123", "connector_id": "conn_a1b2c3d4", "name": "agent/migration-test"}
// 201 Created{ "id": "snap_e5f6g7h8", "status": "provisioning", "operation_id": "op_aabbcc"}To fork from another branch, send parent_snap_id instead of (or alongside) connector_id — the remote is inherited from the parent. Pass overrides, max_lag, or checkpoint_id to apply masking, a freshness bound, or a pinned checkpoint.
Override rules on create
Section titled “Override rules on create”// POST /v1/snaps{ "identity_workspace_id": "ws_abc123", "connector_id": "conn_a1b2c3d4", "name": "agent/safe-sandbox", "overrides": { "tables": [ { "table": "users", "set": { "email": "'me@example.com'", "is_admin": "false" }, "where": "role = 'customer'" } ] }}Overrides are immutable once the branch is created; branch again to change them.
Errors
Section titled “Errors”Errors return a JSON body with an error.code and error.message and the matching HTTP status.
| Status | Meaning |
|---|---|
400 |
Validation error — invalid or missing parameters |
403 |
Forbidden — caller is not a member of the target workspace |
404 |
Not found |
409 |
Conflict — duplicate name, or a state conflict (e.g. source not healthy) |
429 |
OPERATIONAL_CAPACITY — too many branches provisioning at once (five per workspace by default), or quota_exceeded — the workspace used up the branch allowance its plan enforces |
500 |
Internal error |
503 |
quota_policy_unavailable — the plan allowance could not be read, so the request was refused rather than admitted blindly |
A quota_exceeded body carries the numbers behind the refusal:
{ "error": { "code": "quota_exceeded", "message": "Snap branch limit reached", "details": { "capability": "max_branches", "used": 5, "limit": 5, "remaining": 0 } }}A 403 on create means the workspace itself is barred (workspace_suspended or entitlement_required), not that you lack access to the connector.
Schemas on a branch
Section titled “Schemas on a branch”A branch mirrors every user schema in the source, not just public. Postgres’ own catalogs and Snap’s internal _snap_* schemas are excluded, and each mirrored schema gets its own private holding area so same-named tables in different schemas never collide.
Two rules when you query a branch:
- Qualify tables outside
public. Snap resolves an unqualified table name aspublic, soSET search_pathalone will not redirect it — writebilling.invoices. _snap_*is reserved. Any statement referencing those schemas is rejected, including asearch_paththat names one.
SQL meta-commands
Section titled “SQL meta-commands”Run these from any Postgres client connected to a branch:
| Command | Description |
|---|---|
SNAP STATUS |
Summary of your mutations (table, operation, count) |
SNAP DIFF |
Full audit log with before/after row data |
SNAP RESET |
Reverse all tracked changes, restoring the sandbox to source state |
SNAP TRACE |
Recent SQL trace events (requires SNAP_TRACE_SQL=1 on the branch) |
Self-hosting the proxy
Section titled “Self-hosting the proxy”The Snap proxy is configured entirely through environment variables (or a .env file in its working directory). These apply when running the proxy yourself; the hosted service manages them for you.
Proxy listener
Section titled “Proxy listener”| Variable | Default | Description |
|---|---|---|
SNAP_LISTEN_ADDR |
0.0.0.0 |
Proxy listen address |
SNAP_LISTEN_PORT |
15432 |
Proxy listen port |
Postgres source
Section titled “Postgres source”| Variable | Default | Description |
|---|---|---|
SNAP_POSTGRES_SOURCE_HOST |
localhost |
Source host |
SNAP_POSTGRES_SOURCE_PORT |
5432 |
Source port |
SNAP_POSTGRES_SOURCE_USER |
postgres |
Source user |
SNAP_POSTGRES_SOURCE_PASSWORD |
(empty) | Source password |
SNAP_POSTGRES_SOURCE_DB |
postgres |
Source database |
SNAP_POSTGRES_SOURCE_SSLMODE |
prefer |
SSL mode for the source connection |
SNAP_POSTGRES_PORT |
15433 |
Host port for the auto-managed sandbox container |
SNAP_POSTGRES_CONTAINER_NAME |
snap_pg_auto |
Sandbox container name |
SNAP_POSTGRES_IMAGE |
postgres:16-alpine |
Sandbox Postgres image |
SNAP_POSTGRES_DB_USER |
(source user) | Client-facing username |
SNAP_POSTGRES_DB_PASSWORD |
(source password) | Client-facing password |
SNAP_POSTGRES_DB_NAME |
(source database) | Client-facing database name |
SNAP_READ_MATERIALIZE_ASYNC |
true |
Hydrate non-RLS first reads in the background instead of blocking |
SNAP_READ_MATERIALIZE_MAX_CONCURRENT |
2 |
Max concurrent background hydrations per session |
SNAP_OVERRIDES_JSON |
(empty) | JSON override rules applied at materialization |
S3 source
Section titled “S3 source”| Variable | Default | Description |
|---|---|---|
SNAP_S3_SOURCE_ENDPOINT |
(empty) | S3-compatible endpoint URL |
SNAP_S3_SOURCE_REGION |
us-east-1 |
AWS region |
SNAP_S3_SOURCE_BUCKET |
(empty) | Source bucket |
SNAP_S3_SOURCE_ACCESS_KEY |
(empty) | Source access key |
SNAP_S3_SOURCE_SECRET_KEY |
(empty) | Source secret key |
SNAP_S3_SOURCE_USE_SSL |
true |
Use SSL for the source connection |
SNAP_S3_SOURCE_PATH_STYLE |
false |
Use path-style addressing (for MinIO) |
SNAP_S3_ACCESS_KEY |
(random) | Client-facing access key; a random key is generated when unset |
SNAP_S3_SECRET_KEY |
(random) | Client-facing secret key; a random key is generated when unset |
SNAP_S3_OVERLAY_DIR |
/tmp/snap-s3-overlay |
Local overlay storage directory |
SSH tunnel (Postgres)
Section titled “SSH tunnel (Postgres)”Set these to have the proxy own an ssh -N -L … forward when the source is reachable only through a bastion, then point SNAP_POSTGRES_SOURCE_PORT at the forwarded local port.
| Variable | Default | Description |
|---|---|---|
SNAP_POSTGRES_SOURCE_SSH_ENABLE |
false |
Start and manage an SSH tunnel before connecting |
SNAP_POSTGRES_SOURCE_SSH_LOCAL_PORT |
(source port) | Local port bound by ssh -L |
SNAP_POSTGRES_SOURCE_SSH_REMOTE_HOST |
(empty) | Database host reachable from the bastion |
SNAP_POSTGRES_SOURCE_SSH_REMOTE_PORT |
5432 |
Database port reachable from the bastion |
SNAP_POSTGRES_SOURCE_SSH_BASTION_USER |
(empty) | SSH user on the bastion |
SNAP_POSTGRES_SOURCE_SSH_BASTION_HOST |
(empty) | Bastion hostname or IP |
SNAP_POSTGRES_SOURCE_SSH_KEY_PATH |
(empty) | Path to the private key (ssh -i) |
SNAP_POSTGRES_SOURCE_SSH_STRICT_HOST_KEY_CHECKING |
accept-new |
Passed to ssh -o StrictHostKeyChecking=… |