Developer Docs

Introduction to the ds CLI

What the ds CLI actually wraps, how ./ds resolves and runs a command, the global auth/bind flags, and the seven real top-level commands.

Introduction to the ds CLI

The 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.

What ds Wraps

  • AWS, authenticated via AWS SSO — every invocation either verifies or refreshes your SSO session before doing anything else.
  • SSTds run backend runs sst dev (SST's live-lambda mode) against the backend; the frontend ships as an SST StaticSite in the same deploy.
  • Drizzle SQL migrations against the stage's bound PostgreSQL database.
  • Stripe, for product/customer/subscription provisioning.
  • A CQRS code generator for scaffolding backend commands and queries.

There is no infrastructure-as-code tool being orchestrated here beyond SST itself, and no local database-branching product is involved at any point.

Entry-Point Mechanics

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:

  1. ./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.
  2. 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.
  3. It executes that bundle with the remaining args (run), which resolves to a Commander subcommand inside cli/commands/migrations.ts.
  4. On exit, it deletes the temp .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.

Global Wrapper Flags

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]
FlagEffect
-bForce a re-bind of the stage/region config, even if a cached .ds/bind/<stage>-<region>.env already exists.
-rTarget remote: unsets IS_LOCAL and sets DEVSTRIDE_REMOTE=true.
-uSkip the AWS SSO auth check entirely for this invocation.

The AWS SSO Gate

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:

  1. If DEVSTRIDE_DEV_PROFILE is set, it's exported as AWS_PROFILE.
  2. 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.
  3. 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.

Stage/Region Bind Cache

After the SSO gate, ./ds resolves which stage and region you're targeting and caches that resolution to disk:

  • The cache file lives at .ds/bind/<DEVSTRIDE_STAGE>-<DEVSTRIDE_REGION>.env.
  • If that file doesn't exist yet, or you passed -b, ./ds runs node ./cli/bin/store_bind.mjs $DEVSTRIDE_STAGE $DEVSTRIDE_REGION to (re)generate it.
  • The resulting .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.

The 7 Top-Level Commands

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.

CommandWhat it doesCovered in depth
ds runRuns the backend via sst dev (live-lambda mode) or starts the frontend Vite dev server.Local Development
ds migrationsRuns Drizzle SQL migrations against the bound stage's PostgreSQL database.Local Development
ds dataImports, exports, wipes, or org-scoped-copies SQL table data on the bound stage.Local Development
ds goldenBuilds, publishes, imports, reanchors, and manages the Acme golden demo/test dataset.Golden Dataset
ds scriptA grab-bag of one-off maintenance, diagnostic, and codegen scripts (config push, orphan cleanup, DB reset, and more).Maintenance & Codebase Checks
ds stripeProvisions Stripe products and customers, and reports subscription seat counts.Stripe Integration
ds gScaffolds a new CQRS command or query into a backend module.Local Development

Next Steps

  • Command Reference — the complete list of every subcommand and flag across all seven commands
  • Local Development — running the backend and frontend day-to-day, migrations, and data import/export
  • Golden Dataset — the Acme demo/test fixture and where its canonical docs live