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 local / ds worktree commands skip it).ds 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. |
For any AWS-bound command, ./ds 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 needed: ds run local and ds worktree. Both drive the fully-local docker stack, which needs neither AWS SSO nor SST resource binding, so ./ds execs them directly (this is exactly what makes the no-AWS docker flow work). For every other command, -u is the way to bypass the check — useful for tight loops on commands that don't touch AWS, but since most ds run, ds migrations, ds data, and ds golden commands need live credentials, 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 eight top-level commands, one file each under cli/commands/. Each is its own Commander program — there's no shared subcommand namespace beyond these eight.
| Command | What it does | Covered in depth |
|---|---|---|
ds run | Runs the backend via sst dev (live-lambda mode), starts the frontend Vite dev server, or (ds run local) brings up the fully-local docker stack. | 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 worktree | Manages sandboxed per-worktree docker instances (create/list/remove, run CLI commands against one). Like ds run local, it skips the SSO gate and bind entirely. | Dockerized Worktree 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.