ds CLIThe ds command-line interface is the entry point for working with DevStride's AWS infrastructure and databases from a developer machine. It wraps AWS authentication and SST (the framework that deploys the backend Lambda/API Gateway stack and the frontend static site) behind a small set of Commander-based subcommands.
ds golden push/import, ds data copy) contains Neon-specific logic (direct vs. pooled endpoint selection, idle-timeout workarounds). What's fictional is Neon's copy-on-write branching feature being exposed as a CLI operation — the real CLI deliberately sidesteps branching in favor of pg_dump/pg_restore, so there's no ds db create-branch-style command to be found.ds Wrapsds run backend runs sst dev (SST's live-lambda mode) against the backend; the frontend ships as an SST StaticSite in the same deploy.There is no infrastructure-as-code tool being orchestrated here beyond SST itself, and no local database-branching product is involved at any point.
ds is not a single monolithic program — it's a thin bash dispatcher that compiles and runs one TypeScript file per invocation. The chain is:
./ds [-b] [-r] [-u] <command> [subcommand] [args]
│
├─ ./ds ← root bash script: parses -b/-r/-u, runs the SSO gate,
│ resolves/regenerates the stage+region bind cache
│
├─ ./cli/run_script.sh ← esbuild-bundles cli/commands/<command>.ts
│ to .ds/.tmp/<command>.mjs
│
└─ node .ds/.tmp/<command>.mjs "$@" ← the actual Commander program runs,
then the temp bundle is deleted
Concretely, running ds migrations run:
./ds parses wrapper flags, runs the SSO gate (unless -u), resolves the stage/region bind cache, then hands off everything after the flags to cli/run_script.sh.cli/run_script.sh takes $1 (migrations) and calls node ./cli/bin/build.js ./cli/commands/migrations.ts ./.ds/.tmp/migrations.mjs — an esbuild bundle, built fresh on every single run.run), which resolves to a Commander subcommand inside cli/commands/migrations.ts..mjs/.mjs.map and propagates the child process's exit code.Every top-level command is its own file under cli/commands/*.ts — there is no central command registry; each file builds its own Commander program and parses process.argv independently.
ds command pays a small esbuild cost before it starts doing real work.These three flags belong to the ./ds wrapper itself, not to any subcommand, and must come before the command name:
./ds [-b] [-r] [-u] <command> [subcommand] [args]
| Flag | Effect |
|---|---|
-b | Force a re-bind of the stage/region config, even if a cached .ds/bind/<stage>-<region>.env already exists. |
-r | Target remote: unsets IS_LOCAL and sets DEVSTRIDE_REMOTE=true. |
-u | Skip the AWS SSO auth check entirely for this invocation. |
Unless you pass -u, every single invocation of ./ds — not just deploy-shaped commands — runs an AWS auth check before your command's code ever executes:
DEVSTRIDE_DEV_PROFILE is set, it's exported as AWS_PROFILE.AWS_SDK_LOAD_CONFIG=1 is exported, so AWS SDK v2 (used by some Node-side scripts) reads SSO profiles from ~/.aws/config instead of only ~/.aws/credentials.aws sts get-caller-identity is run. If it fails, ./ds prints No active session found. Logging in... and runs aws sso login. If it succeeds, you'll see Already authenticated.-u is the only way to bypass this. It's useful for tight loops on commands that don't touch AWS at all, but most ds run, ds migrations, ds data, and ds golden commands do need live credentials, so treat -u as the exception, not the default.After the SSO gate, ./ds resolves which stage and region you're targeting and caches that resolution to disk:
.ds/bind/<DEVSTRIDE_STAGE>-<DEVSTRIDE_REGION>.env.-b, ./ds runs node ./cli/bin/store_bind.mjs $DEVSTRIDE_STAGE $DEVSTRIDE_REGION to (re)generate it..env file is exported into the shell environment before cli/run_script.sh is invoked, so every subcommand sees a consistent, already-resolved stage/region without re-deriving it itself.In practice this means the first ./ds command after switching DEVSTRIDE_STAGE or DEVSTRIDE_REGION pays a one-time bind cost, and every subsequent invocation reuses the cached file until you force a refresh with -b.
There are exactly seven top-level commands, one file each under cli/commands/. Each is its own Commander program — there's no shared subcommand namespace beyond these seven.
| Command | What it does | Covered in depth |
|---|---|---|
ds run | Runs the backend via sst dev (live-lambda mode) or starts the frontend Vite dev server. | Local Development |
ds migrations | Runs Drizzle SQL migrations against the bound stage's PostgreSQL database. | Local Development |
ds data | Imports, exports, wipes, or org-scoped-copies SQL table data on the bound stage. | Local Development |
ds golden | Builds, publishes, imports, reanchors, and manages the Acme golden demo/test dataset. | Golden Dataset |
ds script | A grab-bag of one-off maintenance, diagnostic, and codegen scripts (config push, orphan cleanup, DB reset, and more). | Maintenance & Codebase Checks |
ds stripe | Provisions Stripe products and customers, and reports subscription seat counts. | Stripe Integration |
ds g | Scaffolds a new CQRS command or query into a backend module. | Local Development |
ds help and no ds menu — there's no help.ts (or equivalent) anywhere under cli/commands/, and running either of those would simply fail to resolve a module. The full, authoritative list of every command, subcommand, and flag lives on the Command Reference page — that's where to look when you need the complete picture instead of guessing at a flag name.