CLI reference
snoutdata is one bundled file with no dependencies, so npx snoutdata is a download rather than
an install. It needs Node 20 or newer.
Published on npm as snoutdata. Nothing to install:
npx snoutdata <command> fetches it and runs it.
The rules every command follows
--json | Accepted everywhere. Exactly one JSON value on stdout and nothing else, so a pipe into jq needs no filtering. Progress and warnings go to stderr. |
| No prompts | Nothing asks a question when stdin is not a terminal. A command that would have to ask says which flag to pass, and exits 2. |
| Unknown flags | An error, never ignored. |
Exit codes
| Code | Meaning |
|---|---|
0 | Fine. |
1 | The operation failed. |
2 | The command was wrong (a bad flag, a missing argument, no project). |
3 | The credential is no good: never signed in, expired, or revoked. The message says which. |
4 | Not ready yet. Retrying is reasonable. |
127 | A program this command needs is not installed (psql, or pg_restore for an archive). |
Where things come from
A token, in this order:
SNOUTDATA_ACCESS_TOKEN~/.snoutdata/auth.json, written bysnoutdata login
The environment wins deliberately, so a script does not behave differently depending on who is logged in on the machine running it.
A project, in this order:
--refSNOUTDATA_PROJECT.snoutdata/project.jsonin this folder or any parent, walked upward the way git finds.git, so the CLI works from a subdirectory.
A password: never typed. Commands that run psql or pg_restore fetch the project's
credentials because you are signed in and pass the password to the child process in its
environment, never on a command line.
Global flags
| Flag | Does |
|---|---|
--json | JSON on stdout, nothing else. |
--help | Print the usage summary. |
--version | Print the version. |
Every command
init
snoutdata init [--name NAME] [--region REGION] [--env] [--ref REF]
From nothing to a working DATABASE_URL. Creates a project (named after the current folder
unless --name says otherwise), waits until it is serving, writes .snoutdata/project.json, and
with --env writes DATABASE_URL into .env.
Idempotent: a folder that is already linked prints that project's URL instead of creating a
second one. The .env line is rewritten in place rather than appended, so you never end up with
two DATABASE_URLs.
login, logout, whoami
snoutdata login [--provider github]
snoutdata logout
snoutdata whoami
login opens a browser and completes a PKCE flow against a loopback callback. The provider
defaults to google. The session it writes lasts an hour and is refreshed automatically.
whoami says who the credential belongs to and which door it came in by, a session or an access
token.
tokens
snoutdata tokens list
snoutdata tokens create --name NAME [--expires DAYS]
snoutdata tokens revoke <id|sdt_prefix>
tokens create prints the token on stdout, alone, once. Only its hash is stored, so there is no
second call that returns it. Without --expires it does not expire.
A token cannot create another token. A token can revoke itself, or any other.
projects
snoutdata projects list
snoutdata projects create --name NAME [--region REGION] [--team NAME|ID] [--no-wait]
snoutdata projects pause [--ref REF]
snoutdata projects resume [--ref REF]
snoutdata projects delete [--ref REF]
create waits for the project to be ready unless you pass --no-wait, because a connection
string handed over before the database exists is a string that does not work yet. It prints the
connection URL once; snoutdata db url prints it again any time.
--team takes a team name or a team id. A name that matches more than one team is refused rather
than guessed at.
A projects list row shows read-only rather than ready when the project is over its storage
limit, because that is the state you need to know about.
teams
snoutdata teams
The teams you can share a project into. A team you can see but cannot share into is listed with the reason, rather than hidden.
link
snoutdata link --ref REF
Writes .snoutdata/project.json in the current folder, so later commands here need no --ref.
db url
snoutdata db url [--ref REF]
Prints the postgres:// connection string on stdout, one line, nothing else. It contains a live
password.
If the project is over its storage limit, a warning goes to stderr so it cannot end up inside
a .env line.
db psql
snoutdata db psql [--ref REF] [-- PSQL ARGS...]
Opens psql against the project. Everything after -- is passed to psql verbatim.
Needs psql on the machine. If it is missing, the error says so and points at db url.
db reset-password
snoutdata db reset-password [--ref REF]
Prints a new password for the project's role. It applies when the project restarts with it, which the command says out loud rather than implying the change is instant.
db export
snoutdata db export [--ref REF] [--out FILE]
snoutdata db export --status [--ref REF]
Asks the control plane for a pg_dump, waits for it, and either prints a signed download link or,
with --out, streams the dump to that file.
- Asking twice is one export. A second request while one is running is treated as the same
request, so a script that retries does not queue a second
pg_dumpagainst a production database. --statuslooks without asking, which is what you want when the alternative is running a dump against somebody's live database.- A missing link is not a failed export. Links are signed with credentials that rotate every few hours, so an aged-out one is dropped rather than handed over dead. The dump is still there; asking again signs a fresh link against the same file.
db push
snoutdata db push [--dir DIR] [--dry-run] [--out-of-order]
Runs every .sql file in the folder (migrations by default), in byte-wise name order, once
each, recording what ran in a _snoutdata_migrations table in your own database.
Order is ls | sort, so zero-pad your numbers (001-, 002-) or 10- sorts before 9-.
The ledger row is written in the same transaction as the migration, so a file that fails half
way leaves nothing behind and there is no state where the database believes something ran that did
not. A file that cannot be wrapped in a transaction says so on a line of its own, and db push
tells you it has given that guarantee up:
-- snoutdata:no-transaction
create index concurrently people_email_idx on people (email);
It refuses three things, and overrides only one:
| It refuses | Because | Override |
|---|---|---|
| A file that changed since it ran | The database and the folder now disagree about what happened, and the old statements have already run. The fix is a new migration. | none |
| A file that has gone | Usually a rename, and a rename is how the same SQL runs twice. | none |
| A new file that sorts before one already applied | Two branches merge and 003 lands after 004 ran, which gives this database a history no fresh database will ever have. | --out-of-order |
Every problem is reported at once, not one per run. --dry-run says what would happen and changes
nothing.
Needs psql, because a migration connects to the database like any other client.
db restore
snoutdata db restore --file DUMP [--ref REF] [--force]
Puts a dump into a project. The other half of db export.
The format is sniffed from the file's first bytes, not from its name, because pg_dump writes
four formats and a file called .dump may be any of them. A custom or tar archive goes to
pg_restore, plain SQL to psql, and a file that is neither is refused rather than guessed at.
It refuses a database that already has tables, because the overwhelmingly likely cause is the
wrong --ref. --force is for when you mean it. It also refuses a project that is over its
storage limit, and --force does not get past that one: every write would fail anyway.
Restoring an export of ours into a project of ours produces errors about not owning
pg_stat_statements. Those are expected, nothing is missing, and the command says so instead of
failing. Any other error is still a failure.
Needs psql, and pg_restore as well for an archive.
usage
snoutdata usage [--ref REF] [--days N] [--history]
Size, compute and connections against the plan's limit. --days defaults to 30 and is capped at
365. --history prints the day-by-day table underneath.
first light (x8x3sb2hcx4xn)
7.7 MB of 8.0 GB on plus (0%).
Over 2 days: 1d 6h of compute, 2 connections.
A dash in the size column is a day nothing measured it, which is what a paused project looks like. It is not zero, and the current size is the most recent day that has a reading. Compute and connections are summed, because there a missing day really did have none.
mcp
snoutdata mcp [--allow-delete]
Serves the same operations to an agent over stdio. See for an agent.
Environment variables
| Variable | Does |
|---|---|
SNOUTDATA_ACCESS_TOKEN | The credential to use. Wins over ~/.snoutdata/auth.json. |
SNOUTDATA_PROJECT | The project ref to act on, when there is no --ref. |
NO_COLOR | Turn off the bold and dim escape codes. Colour is off anyway when stdout is not a terminal. |