Command reference
Every command, with the flags it actually accepts. safegrd <command>
--help prints the same thing on the host, and is the authority if this page
and the binary ever disagree.
Global flags
| Flag | Meaning |
|---|---|
| --config | Config file. Default ~/.safegrd/config.yaml. |
| --server-url | Control plane URL. Default https://safegrd.dev, or $SAFEGRD_SERVER_URL. |
| -v, --version | Print the version and exit. |
Setup
init
Generates an Age keypair if this host has none and writes local configuration. Contacts no SafeGrd server; with --storage s3 it asks the bucket whether Object Lock is on.
| Flag | Meaning |
|---|---|
| --node-name | Name this host is shown under. Default: the hostname. |
| --database-url | PostgreSQL connection URL to protect. |
| --storage | local, s3 or hosted. Default local. |
| --local-path | Directory for the local sink. |
| --s3-bucket | Bucket for WORM storage. |
| --s3-prefix | Key prefix in the bucket. Default safegrd/snapshots. |
| --s3-region | Bucket region. Default us-east-1. |
| --s3-endpoint | Custom endpoint, for MinIO, R2 or Wasabi. |
| --retention-days | WORM retention in days. Default 14. |
| --worm-mode | Object Lock mode for --storage s3: COMPLIANCE (default), GOVERNANCE, or NONE for a bucket with no Object Lock, such as DigitalOcean Spaces. |
| --force | Overwrite an existing config. This discards its settings and generates a new keypair, after which snapshots taken under the old key can only be read with the old key. Back it up first. |
enroll
Registers this host with the control plane, running local setup first if needed. A personal access
token enrols new hosts only. A host that is already enrolled keeps its node token; if that token is
lost, rotate it under Nodes in the console and run safegrd enroll --token <node token>
--node-id <id> on the host.
| Flag | Meaning |
|---|---|
| --token | Pre-issued node token, or a personal access token (which registers a new node). |
| --node-id | The node a node token belongs to. The console shows it beside the token. Not needed with a personal access token. |
| --api-key | Organization or admin API key, for provisioning many hosts. |
| --node-name | Name this host is shown under. Default: the hostname. |
| --org | Organization to enrol into. Only needed when the credential can see more than one. |
| --project | Project id or slug. Defaults to the organization's default project. |
| --key-custody | safegrd (default) escrows the generated private key; local keeps it on this host and sends only the public half. Ignored when you supplied your own key. |
| --storage | Where this host's backups go when its project has no bucket: hosted uses SafeGrd's hosted storage where your plan includes it; local keeps them in a directory on this host. |
| --allow-unconfigured | Enroll even if neither the host config nor its project specifies a storage destination. By default, enrollment requires a configured storage target so the host can immediately begin backups. |
claim
Adds to an enrolled host's config the surfaces named for it in the console (Add surface on the
host's row under Nodes). Nothing changes on the host until it runs, and it only adds: a surface the config
already has is left as it is, the previous config is kept as config.yaml.bak, and running it again
adds nothing. The daemon reads its config when it starts, so restart it afterwards with
safegrd daemon restart. A new host enrolls with safegrd enroll --claim <code> instead.
login
Authenticates your user account with SafeGrd Cloud.
| Flag | Meaning |
|---|---|
| --token | Personal access token (sg_pat_...) for CI and headless use. |
| --no-browser | Print the URL rather than opening a browser. |
whoami
Prints the authenticated user and the active server session.
Protect
backup
Streams a surface, compresses it with zstandard, encrypts it to your Age public key and writes ciphertext to WORM storage. Plaintext never touches disk, except a SQLite surface's private temporary copy on the same host (why).
| Flag | Meaning |
|---|---|
| --database-url | Database connection string: postgres://… selects the PostgreSQL surface, mysql://… (or mariadb://…) the MySQL one, mongodb://… the MongoDB one, sqlite:///path/to/file.db the SQLite one. |
| --surface | The id of one surface under surfaces: in the config. Backs it up now, whatever its schedule, the way the daemon would: its own storage, retention and credential. Take one before a migration. Used alone, without the flags that describe a surface. |
| --files | Directory tree to back up. Selects the files surface. |
| Run an IMAP mailbox backup. Selects the email surface. | |
| --exclude | Glob patterns to exclude from a file backup, e.g. '*.tmp,node_modules/*'. |
| --format | How --files or a PostgreSQL or SQLite database is stored: repo, incremental, each run uploading only what changed (the default), or tar, one archive per backup. MySQL and MongoDB are one archive. See incremental file backups and databases. |
| --change-log | For a PostgreSQL backup in repo format: skip reading tables nothing wrote since the last run, from a trigger it puts on each table. See the change log. |
| --roles-without-passwords | PostgreSQL: leave role password hashes out of the backup. By default the roles the schema names travel with them, sealed like everything else. See roles, owners and grants. |
| --new-epoch | Start a new monthly epoch now, uploading everything once. For --format repo, or with --surface. |
| --rescan | Read every file, not only those whose size or times changed. For --format repo, or with --surface. |
| --one-filesystem | Stay on the root's filesystem. The default when the root is /. |
| --email-host | IMAP hostname. |
| --email-port | IMAP TLS port. Default 993. |
| --email-user | Mailbox username or address. |
| --email-password | Mailbox password. Prefer SAFEGRD_EMAIL_PASSWORD: a flag is visible in the process table. |
| --email-folders | Specific folders. Default is all except spam and trash. |
| --email-ca-file | PEM bundle of extra CAs to trust for the IMAP server, added to the system roots, never instead of them. Also SAFEGRD_EMAIL_CA_FILE. |
| --retention-days | WORM immutability period for this snapshot. |
| --s3-bucket | Write to this bucket instead of the project's or the config's. |
| --s3-prefix | Key prefix in the bucket. |
| --s3-endpoint | S3 endpoint, for MinIO, R2 or another S3-compatible store. |
| --s3-region | S3 region. |
| --s3-access-key | S3 access key id. |
| --s3-secret-key | S3 secret access key, as env:VAR or file:/path. |
| --tag | Custom snapshot tag prefix. |
| --json | Emit the result as JSON. |
guard
Backs up one surface, waits until the snapshot is uploaded and locked, then runs the command. If the backup fails or the snapshot is not locked, the command does not run and guard exits 3. See Agent guard.
| Flag | Meaning |
|---|---|
| --surface | The surface to back up, by its id in the config. |
| --allow-unlocked | Run the command after a backup that is not locked: local storage, or worm_mode: NONE. |
| --list | Print the destructive command rules. |
| --matches | Check a command against the rules and take no backup: exit 0 if it matches, 1 if not. |
| --hook | Answer a pre-command hook on stdin: claude-code, cursor or codex. |
Recover and prove
restore
Downloads ciphertext, decrypts it on this machine and restores it to a database or a directory.
With --from it reads a copy made by safegrd export --to-dir instead of the
storage in the config (Runbook 4).
| Flag | Meaning |
|---|---|
| --snapshot | Snapshot id to restore. Leave it out with one --path or one --table to restore its newest kept version, or with --from naming a .safegrd file. |
| --from | An export to restore from: the directory export --to-dir wrote, with --snapshot, or one .safegrd file in it. |
| --target | Target database URL, postgres://… or mysql://… to match the snapshot. It must be an empty database. |
| --target-dir | Target directory, for file and email snapshots. |
| --path | For an incremental files snapshot: restore only this path, relative to /. Repeatable; *, ? and ** match. |
| --version | With one --path or --table: restore that version, as safegrd find numbers them, in place of --snapshot. |
| --surface | With --version: the surface whose repository holds the file, when more than one does. |
| --schema | PostgreSQL only: restore this schema's objects and rows into a database that already has other schemas, such as a Supabase project. Repeatable. The chosen schema's objects have to be absent from the target. |
| --data-only-schema | PostgreSQL only: load this schema's rows into the tables the target already has, and create nothing. Repeatable; combine with --schema. |
| --no-owner | PostgreSQL only: restore without owners, grants and default privileges, so every object belongs to the restoring role. The roles the schema names are still created, because a policy cannot be restored without them. See roles, owners and grants. |
| --to-sql | A new directory to write a PostgreSQL snapshot into as files psql loads: the schema as SQL, each table's rows as binary COPY, and load.sql. Run psql "$URL" -f load.sql from that directory. The files are unencrypted; the directory is created 0700. |
| --key-path | Path to the Age private identity file. |
| --private-key | The Age private identity inline (AGE-SECRET-KEY-1...). |
verify
Runs a Fire Drill: pulls the snapshot, decrypts it and asserts what came back. The default is an in-memory dry restore needing no database at all.
| Flag | Meaning |
|---|---|
| --snapshot | Snapshot id to verify. Required. |
| --dry-run | In-memory dry restore, with no target database. |
| --sandbox-target | An empty database to restore into for a full drill. verify never empties it afterwards. When the drill fails, it prints where the sandbox is. |
| --keep | When a sandbox drill fails, record that the sandbox is kept for a person to look at. |
| --show-url | When a sandbox drill fails, print the sandbox URL with its password. |
| --key-path | Path to the Age private identity file. |
| --private-key | The Age private identity inline. |
| --s3-bucket | Read the snapshot from this bucket instead of the configured storage. |
| --s3-prefix | Key prefix in the bucket. |
| --s3-endpoint | S3 endpoint, for MinIO, R2 or another S3-compatible store. |
| --s3-region | S3 region. |
| --s3-access-key | S3 access key id. |
| --s3-secret-key | S3 secret access key, as env:VAR or file:/path. |
find
Lists every kept version of the matching files in incremental backups: when each was first and last seen, its size and SHA-256, and how many snapshots hold it. Patterns are paths relative to /; a directory selects everything below it. Runs where the private key is.
| Flag | Meaning |
|---|---|
| --surface | Search this surface only. Without it, a host searches the surfaces it has the key for and says how many others it did not search; naming another host's surface fetches its key when SafeGrd holds it. |
| --deleted | List only files the newest snapshot no longer holds. |
| --json | Print the histories as JSON. |
| --key-path | Path to the Age private identity file. |
| --private-key | The Age private identity inline. |
check
Checks incremental backups without restoring them: every pack a snapshot names is in storage and agrees with its index, every directory listing decodes, the content digest recomputed from them matches the one recorded, and the catalog agrees. Exits non-zero and names what is wrong.
| Flag | Meaning |
|---|---|
| --read-data | Also open every chunk and check its SHA-256. Downloads the snapshot's data. |
| --key-path | Path to the Age private identity file. |
| --private-key | The Age private identity inline. |
history
Checks a node's attestation chain and the Ed25519 signature of each record under the key it names. Aliased as verify history.
| Flag | Meaning |
|---|---|
| --node | Node id whose history to verify. |
| --file | Local JSON file of verifications, for an offline audit. |
| --save | Also write the records fetched from SafeGrd to this file, for a later --file check. |
| --key | One hex-encoded Ed25519 public key, checked against every record. |
| --key-file | File containing that one public key. |
| --keys-file | The JSON of /api/v1/attestations/public-key, saved while online: the active key and the retired ones, for a chain that spans a key rotation. |
| --json | Emit the verification result as JSON, with the keys that signed the chain. |
Storage
list
Lists every snapshot in the bucket, with the node it was filed under, what it holds and
its retention state. restore and verify find a snapshot under
whichever node it is filed under, so a machine rebuilding a lost host does not need the
old node id (Recovery runbooks). Incremental file backups are
listed in a second table: the month (epoch) each snapshot belongs to, whether its run
opened the month or followed it, the size of the tree it holds and the bytes its run
uploaded.
| Flag | Meaning |
|---|---|
| --json | Print a JSON array on stdout, one object per snapshot with snapshot_id, created_at, status, locked and locked_until. Every other line goes to stderr. |
undelete
Removes the delete markers hiding snapshots in a versioned bucket. It only ever deletes delete markers, addressed by version id, and cannot remove a version holding data. With --all it rolls the bucket back to before the deletion, every snapshot and sidecar, and re-uploads nothing (how a deletion is undone).
| Flag | Meaning |
|---|---|
| --snapshot | Make one snapshot visible again. |
| --all | Clear delete markers from every hidden snapshot. |
export
Copies every snapshot and its metadata, still encrypted, to a directory or to a bucket
you own. --snapshot and --node narrow it. No key is needed. An
incremental files backup is copied a whole month (epoch) at a time, since a snapshot needs
every object of its month, and --snapshot copies the month that holds it. Each copy is checked against the digest recorded at backup
time, a snapshot keeps its lock in the destination bucket, and one already there is
skipped, so an interrupted export can be run again. Use it to leave hosted storage or
to keep an offline copy. Credentials for --to-bucket come from the
standard AWS environment. safegrd restore --from restores from a directory export.
| Flag | Meaning |
|---|---|
| --to-dir | Export into this local directory. |
| --to-bucket | Export into this S3 bucket. |
| --to-endpoint | S3 endpoint of the bucket (MinIO, B2, R2, ...). |
| --to-region | Region of the bucket. Default us-east-1. |
| --to-prefix | Key prefix in the bucket. Default safegrd/snapshots. |
| --to-worm-mode | COMPLIANCE (default), GOVERNANCE or NONE. |
| --snapshot | Export only this snapshot. Repeat it or give a comma-separated list. An id that is not in storage is an error. |
| --node | Export only this node's snapshots. Repeatable. |
estimate
Measures a PostgreSQL database and prints the egress a month for weekly, daily and hourly backups at its provider, after the provider plan's included egress. Every backup reads every row, uncompressed, so one backup reads about the size of the tables without their indexes. The provider is read from the host name (Supabase, Neon, Amazon RDS); the prices were read from their pricing pages on 2026-10-05. A warning on stderr names each schedule whose egress costs more a month than the SafeGrd plan you compare it with, at the price the server publishes. Your application's own traffic uses the same allowance, so pass what is left with --included-gb. For a database SafeGrd backs up itself, the console shows the same estimate for the surface's schedule when its connection string is saved.
| Flag | Meaning |
|---|---|
| --database-url | The database to measure. Default: the first PostgreSQL surface in the config. |
| --surface | Measure this surface's database. |
| --provider | supabase, neon, rds or other. Default: read from the host name. |
| --provider-plan | Whose included egress applies: Supabase free, pro (default) or team; Neon free, launch (default) or scale. |
| --egress-price-per-gb | The price in US dollars per GB, for --provider other or to replace the one built in. |
| --included-gb | Egress a month that is free and not used by your application. |
| --schedule | Price this schedule as well, as the config writes it. |
| --plan | The SafeGrd plan to compare with: starter, growth (default) or scale. |
| --json | Print the estimate as JSON. |
keep
Extends one snapshot's lock, its backup file, metadata and recovery document together, to the end of --until (UTC). A lock can be extended and never shortened, by anyone, so the date can be at most 13 months from today and the command needs --yes. In your own bucket the host extends it, which needs s3:PutObjectRetention and s3:GetObjectRetention, and tells the server. On hosted storage the server extends it. An incremental snapshot is kept as long as its month and cannot be kept on its own (keep a snapshot longer).
| Flag | Meaning |
|---|---|
| --snapshot | The snapshot to keep longer. |
| --until | The new lock end: a date, kept to the end of that day in UTC, or an RFC 3339 time. |
| --yes | Extend it. Without this the command says what it would lock and stops. |
prune
Deletes snapshots from your S3 bucket whose Object Lock retention expired more than a day ago,
evaluated against the bucket's timestamp and retention status. SafeGrd keeps the newest
--min-keep snapshots of a surface (three by default) whatever their locks say, and preserves the last known-good snapshot whenever Threat Shield reports an open
anomaly. Snapshot versions are removed before delete markers and metadata. Requires
s3:DeleteObjectVersion and s3:GetObjectRetention permissions.
Set storage.expire_after_lock: true to enable automated daily pruning. An
incremental files backup is pruned by month: a month's expired objects are deleted once
every snapshot that could use them is past its retention, and never from the month holding
the surface's newest snapshot.
| Flag | Meaning |
|---|---|
| --min-keep | How many of each surface's newest snapshots are never deleted, whatever their locks say. At least 1; default 3. |
| --dry-run | List what would be deleted, and delete nothing. |
| --grace | How long after a lock ends a snapshot is kept anyway. Default 24h. |
Unattended
daemon run
Runs the resident daemon: evaluates due surfaces, takes per-surface single-flight locks and backs them up.
| Flag | Meaning |
|---|---|
| --once | One reconciliation pass, then exit. For cron and Kubernetes Jobs. |
| --interval | Poll interval. Default 5m. |
| --state-dir | State and locks directory. |
daemon status
Live status of every configured surface and the daemon itself.
| Flag | Meaning |
|---|---|
| --json | Emit status as JSON. |
| --state-dir | State directory to read. |
daemon install
Generates or installs a systemd unit or launchd job. daemon restart and daemon uninstall manage it from there.
| Flag | Meaning |
|---|---|
| --user | Install as a user agent rather than a system service. |
| Print the service definition to stdout and install nothing. |
Diagnose
doctor
Deep preflight: config permissions (0600 on POSIX), keypair integrity, WORM sink connectivity and real Object Lock support, control plane reachability, host clock skew, and that every surface opens with the credential its backup will use: a query on each PostgreSQL, MySQL, MongoDB and SQLite database, a login to each mailbox, and each files root. For each database it finds a pg_dump or mysqldump the server can be dumped with. For each PostgreSQL surface it warns when the role can write and prints the read-only role to use, and names the tables row-level security hides rows of (read-only role). On an enrolled host the results are reported to the console, which shows the host as checked.
| Flag | Meaning |
|---|---|
| --json | Emit the diagnostic report as JSON. |
| --agent-proof | Check instead whether an AI agent on this host could destroy the backups, and print the fix for each failure. See Agent guard. |
status
Quick check of connectivity, storage immutability and encryption keys.
config validate
Validates configuration syntax, permissions and surface definitions without running anything.
Organization
org
org list prints your organization profile, plan and quota status; org members lists the team. Aliased as orgs.
projects
Lists the projects inside an organization.
version
Prints the CLI version, commit and build date. Include it in any bug report.
Environment variables
| Variable | Meaning |
|---|---|
| SAFEGRD_SERVER_URL | Control plane URL, the same as --server-url. |
| SAFEGRD_PRIVATE_KEY | The Age private identity, for restores and drills on a host without the key file. |
| SAFEGRD_EMAIL_PASSWORD | Mailbox password. Prefer this to the flag. |
| SAFEGRD_EMAIL_CA_FILE | PEM bundle of extra CAs to trust for IMAP. |
Token lifetime
Personal access tokens expire 90 days after creation, and the console displays the expiration date. For automated environments and CI pipelines, plan regular token rotation to prevent build interruptions, and scope each token to its specific project to limit exposure. Tokens issued before this expiration policy remain valid indefinitely.