Bring up the local stack (+ optionally exercise a change)
Gets a developer from nothing to a running, DB-connected local stack: sandbox infra (Mongo, MySQL, Redis, LocalStack) and the NestJS services. It is idempotent โ it inspects what is already up and starts only what is missing, and never fails just because something is already running. It is also the hands-on counterpart to /verify (static gate: lint/typecheck/test/build): once the stack is up you can exercise a change end-to-end (step 6).
verify-integration-local (in backend-services) builds on this skill. Discover machine-specific values (ports) at runtime; do not hardcode.
0. Prerequisiteโ
- Docker Desktop running (
docker infosucceeds). On macOS:open -a Dockerand wait.
1. Infra โ sandboxโ
bewith sandbox status # already healthy? then skip the next line
bewith sandbox up # only if infra is not already up
bewith sandbox up is not a no-op โ it kills and rebuilds the containers every run. MySQL and Mongo data survive (host bind-mounts ./mysql8, ./mongodata8); Redis is reset. So gate it on status and run up only when the stack is not already healthy.
Containers started by up: mongodb8-dev, mysql8-dev, redis-dev, localstack-dev, php-nginx-dev. (The infra MCP infra-mcp-dev is not part of up โ it is gated behind the compose mcp profile and started on demand by bewith sandbox mcp register.)
2. Discover the Mongo host port + align .env (do not hardcode)โ
The sandbox Mongo container maps to a host port that can differ from the committed .env.
docker port mongodb8-dev 27017 # -> 0.0.0.0:<HOST_PORT> (often 27018)
Make sure backend-services .env MONGODB_URI uses <HOST_PORT> before starting the services. Creds: root / mypass, authSource=admin, DB dev.
3. Verify DB connectivity (communication)โ
docker exec mongodb8-dev mongosh "mongodb://root:mypass@localhost:27017/dev?authSource=admin" --quiet --eval 'db.runCommand({ ping: 1 }).ok' # -> 1
docker exec mysql8-dev mysql -uroot -pmypass ebdb -N -e "SELECT 1;" # -> 1
docker exec redis-dev redis-cli ping # -> PONG
From the host, also confirm Mongo answers on <HOST_PORT> (this is what the apps use).
3b. Apply pending migrations (prevents MySQL schema drift)โ
The MySQL ebdb dump can lag the codebase, so group creation fails with Unknown column '...'. Apply pending migrations non-interactively before starting services โ it is idempotent (only runs migrations not yet applied):
docker exec php-nginx-dev php yii migrate/up --interactive=0
This is the real fix for schema drift; a one-off ALTER TABLE is only a fallback when a specific migration can't run locally.
Empty
ebdb(clean machine):bewith sandbox up(CLI โฅ 0.1.17) auto-loads a committed schema baseline before this step, somigrate/uphere is a no-op (or applies only genuinely new deltas) instead of failing onTable 'ebdb.organizations' doesn't exist. The active migrations are deltas over that baseline โ there's no external dump to fetch; seesandbox/db/baseline/.
4. Start the NestJS services (headless "Debug-Watch All Services")โ
This replaces clicking VSCode โ Run and Debug โ "Debug-Watch All Services". The helper script (a backend-services dev tool at scripts/start-local-services.mjs) reads .vscode/launch.json, starts each app's program with ts-node as a background process, logs to logs/<app>.log, and waits for the gateways.
.vscode/launch.json is gitignored โ if it is missing (fresh checkout/worktree), copy it from the committed .vscode/launch.template.json first (the template defines the Debug-Watch All Services / Debug-Watch Selected Services compounds the script reads).
# from the backend-services working tree whose code you want under test (apps run the cwd's source)
node scripts/start-local-services.mjs
- Idempotent: if the gateways (
INTERNAL_GATEWAY_PORT8082,EXTERNAL_GATEWAY_PORT8083) are already listening, it does nothing. - Subset: pass a comma list to start fewer apps. The minimal set for an import (DB-level verify) is:
node scripts/start-local-services.mjs external-gateway,internal-gateway,integrations,groups,communities,vector-search,address-provider,auth,authorization,organizations,user(include address-provider โgoogle-mapslocations are geocoded there). - For a UI check, start the FULL compound (no subset arg). The consumer event-grid's
resources/availableread path needs more than the import subset โoptions,user-communities,user-groups, andorganizations-statsas well โ and returns 500 if any are missing. The import subset only saves boot time for a DB-only run; when you also want the browser check, run everything. - Runs from
process.cwd()โ run it from the branch under test (the apps execute that source). - Requires
.envwith the correct Mongo port (step 2).
5. Verify services are up + connectedโ
lsof -nP -iTCP:8082 -sTCP:LISTEN && lsof -nP -iTCP:8083 -sTCP:LISTEN # gateways listening
grep -lE "successfully started|Nest application" logs/*.log # apps booted
grep -liE "econnrefused|MongoServerError|ER_ACCESS|unable to connect" logs/*.log # connection errors (should be empty)
Tail a service's log to confirm it connected to Mongo/MySQL: tail -f logs/integrations.log.
6. (Optional) Exercise a change end-to-endโ
Once the stack is healthy, verify a change actually behaves โ the reason to run locally instead of trusting /verify alone.
- Frontend (if the change is UI):
For auth, mint a local JWT via thecd ~/workspace/management-webapp && npm run dev # :8091, NEXT_PUBLIC_BACKEND_V2_URL=http://localhost:8083/api/v2# or bewith-consumer-frontend: npm run dev (:3000) โ set the local backend URL
permissions-demo-tokensskill (demo user per role). - Exercise + verify: hit the touched endpoint(s) โ prefer a saved Postman request (
postman-endpoint) orcurlโ and confirm the response. For SQS flows, the schedule โ wait for the every-minute cron โ check history pattern applies; startsqs-consumer(npm run start:dev) if the change touches notifications. For UI, drive the actual screen (states: default/loading/empty/error/success). - Report: what was exercised, the observed result, and anything that couldn't be verified locally.
Needs a human (surface, never fake)โ
- Secrets in
.env(AWS keys, JWT secret, encryption keys) โ copy from the team template / vault; never paste into chat or a committed file. - Data dumps โ seed SQL/Mongo from
s3://bewith-dev-dumps/โฆ(needs AWS creds) or a dump from a senior dev (sandbox/README.md). An empty DB boots but most flows need data. - Port conflicts (3306/6379/27017/8082-8084/8091) โ detect and report; don't kill processes blindly.
Boundariesโ
- Verifies, doesn't ship. No push/PR/merge โ that's the normal flow /
/continue. - Honest results only. If infra won't come up or a secret/dump is missing, say exactly what's blocked โ never report "verified" on a partial run.
- Test strategy (what to cover, unit vs integration vs e2e) is
qa-engineer; this skill executes a local run.
Stopโ
- Services:
/kill-service-ports, ortest -s logs/local-services.pids && awk '{print $1}' logs/local-services.pids | xargs kill. - Infra:
bewith sandbox down.
Gotchasโ
- Mongo host port is dynamic โ discover via
docker port mongodb8-dev 27017; the committed.envmay be stale (27017vs27018). Apps and scripts must use the live port. - Services run the cwd's source โ start from the branch under test, or you will run stale code.
- Running from a git worktree โ copy the gitignored files first. A fresh worktree lacks
.envand.vscode/launch.json(both gitignored). Without.envthe services boot with production defaults and crash at startup โgetaddrinfo ENOTFOUND redis(RedisService),SQS queueUrl is required for SqsTransportStrategy,JwtStrategy requires a secret or key(andenvironment: productionin the logs). Copy both from the main checkout before running the script:cp <main>/.env <worktree>/.envandcp <main>/.vscode/launch.json <worktree>/.vscode/launch.json. (The start script preflights these and fails fast with this message.) The external-gateway also loads its SSL cert relative to__dirname(../../../../developer-kit/nginx-selfsigned.{key,crt}); from a worktree that resolves to<repo>/.claude/worktrees/developer-kit/โ create that dir and copy the certs from the main repo'sdeveloper-kit/, or external-gateway crashes onENOENTand never binds 8083. - Adding one service while the stack is up: the script's idempotency only skips when both gateways are already listening; to start a single missing microservice (e.g.
address-provider) while gateways are up, run it directly:node -r ts-node/register -r tsconfig-paths/register apps/<app>/src/main.ts &. - ts-node boot is slow for many apps; the first start of the full compound takes a while. A curated subset starts faster.
- DB reads can be done directly via
docker exec ... mongosh/... mysqlif the local DB MCP (sandbox/infra-mcp) is not registered in the session (MCP servers register at session start).
Scriptsโ
backend-services/scripts/start-local-services.mjsโ headless equivalent of "Debug-Watch All Services" (parses.vscode/launch.json, spawns apps with ts-node, health-checks gateways, idempotent). Lives in backend-services because it is repo-specific (its launch.json + apps). Optional first arg = comma-separated subset of app names; override the compound with theCOMPOUNDenv var.
Cross-referencesโ
/verifyโ the static gate (lint/typecheck/test/build); run it too.qa-engineerยทpermissions-demo-tokensยทpostman-endpointยทobs-logs(read local/prod logs when a run misbehaves).