This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
devcloud is a local cloud service emulator: a single Go binary that runs compatible development endpoints for Mail (SMTP), S3, GCS, DynamoDB, BigQuery, SQS, Pub/Sub, and Redshift, plus a React dashboard. It targets deterministic local tests and manual inspection — not production parity. New provider behavior should be added deliberately, backed by tests in the relevant package and (usually) an acceptance gate under scripts/*-autoloop/.
go test ./...— run all unit/integration tests. Required to pass before claiming work is done.go test ./internal/services/s3/...— narrow test runs by package; use-run TestNamefor a single test.go run ./cmd/devcloud init— write.devcloud/config.yamlwith default ports/auth/services.go run ./cmd/devcloud up— start all enabled services + dashboard (Ctrl-C to stop).go run ./cmd/devcloud reset— wipe.devcloud/datafor the configured workspace.go build -o /tmp/devcloud ./cmd/devcloud— build the CLI binary.
The dashboard lives in web/dashboard/ (Vite + React 18 + TS) and is embedded into the Go binary at internal/dashboard/assets/react via //go:embed.
cd web/dashboard && npm installnpm run dev— Vite dev server (proxies/apito a runningdevcloud upon:8025).npm run build— typecheck and emit static assets intointernal/dashboard/assets/react/. Run before testing Go changes that depend on the latest UI bundle.npm run typecheck— TypeScript check only.
Each service has a bounded autoloop folder under scripts/<service>-autoloop/. The relevant entry points are:
VERIFY_STAGE=full bash scripts/<service>-autoloop/verify.sh— final acceptance gate for that service (mail,s3,gcs,dynamodb,bigquery,sqs,pubsub). Stages such asfoundation,<svc>-core,dashboard-static,hardeningexist for faster partial checks.- SDK / advanced gates:
scripts/gcs-sdk-compat-autoloop,scripts/bigquery-sdk-compat-autoloop,scripts/pubsub-full-compat-autoloop,scripts/redshift-advanced-compat-autoloop. - The autoloop folders also contain runner state (
progress.md,state.env,runner.log). Treat these as generated; do not mix them with source commits.
scripts/<service>-e2e.sh boots devcloud up, exercises the service, and tears down. Useful env vars: E2E_INTERACTIVE=true keeps the daemon running for browser inspection; E2E_DELETE_DATA=false preserves storage; E2E_<SVC>_PORT / E2E_DASHBOARD_PORT override defaults when the standard ports are busy.
go.mod declares one module (devcloud, Go 1.22). The CLI in cmd/devcloud/main.go dispatches to internal/app. internal/app/daemon.go is the central wiring point: it constructs every service server, the dashboard server, and any shared stores, then launches each enabled server on its own goroutine and fans errors back through a single channel. Any new service must be plumbed through Daemon.Run, enabledServerCount, and Config (see internal/app/config.go).
Each provider lives under internal/services/<svc>/ and exposes a NewServer(Config) *Server constructor plus a Run(ctx) error method. Files are split by concern (e.g., server.go, routes.go, *_handlers.go, store*.go, types.go, responses.go, <feature>_test.go). Cross-service contracts:
- Object stores are shared. S3 and GCS both use
s3svc.BucketStorebacked bys3svc.NewFileBucketStoreon disk. BigQueryjobs.insertload/extract and RedshiftCOPY/UNLOADaccept the same store to read/writegs://ands3://URIs locally without leaving the process. - Redshift backend is pluggable.
internal/services/redshift/backenddefinesSQLBackend; implementations live inbackend/memoryandbackend/postgres. The defaultmanagedmode starts a local PostgreSQL under.devcloud/data/redshift(internal/app/managed_postgres.go);externalmode connects to a user-supplied DSN;memoryis a development fallback. Redshift SQL is rewritten before execution viainternal/services/redshift/translator. - Pub/Sub serves both gRPC and REST from one
Server(different addrs). The gRPC and REST handlers share the in-memory state (topics,subscriptions,messages,deliveries); persistence lives inpersistence.gorooted atStoragePath/MessageStoragePath.
internal/dashboard is the only HTTP entry point users hit at :8025. It serves:
- The React SPA from
assets/react(embedded).assets.gomounts/dashboard/and falls back toindex.htmlfor client-side routes. - A set of
/api/*JSON endpoints (mail_handlers.go,s3_handlers.go,dynamodb_handlers.go,bigquery_handlers.go,sqs_handlers.go,pubsub_handlers.go,redshift_handlers.go, plus the service registry inservices.go). - Safety rule: dashboard mutations MUST go through the provider-protocol path (
/api/<svc>/*forwarding into the in-process service) — never directly through storage. Never log credentials, Authorization headers, signatures, message bodies, or object payloads. SeeAGENTS.mdand the per-service notes inREADME.md.
Every service supports a relaxed mode (default, used by all local tooling) and a stricter mode that validates configured credentials. Relaxed mode is what tests and autoloops assume; if you add credential checks, gate them on the mode string so the existing scripts still pass.
Config lives at .devcloud/config.yaml (custom YAML-ish parser in internal/app/config.go); defaults come from app.DefaultConfig(). Runtime data is rooted at Storage.Path (default .devcloud/data) with per-service subdirectories (mail, s3, dynamodb, bigquery, sqs, pubsub, redshift, gcs/upload_sessions). .devcloud/ is gitignored and must not be committed.
- Idiomatic Go,
gofmt, standard-library-first. Few external dependencies (onlycloud.google.com/go/pubsub, gRPC, protobuf currently). - Package names: short, lowercase (
mail,s3,pubsub,dashboard,blob). - Tests sit next to the package they cover. Behavior-named tests (e.g.,
TestSMTPRejectsOversizeMessage). Service packages additionally have category-split test files (object_test.go,multipart_test.go,versioning_test.go, etc.) — keep new tests in the matching category file rather than a monolithicserver_test.go. - Conventional commit style:
feat(s3): ...,fix(pubsub): ...,refactor(bigquery): ...,test: ...,docs: .... Do NOT add Claude Code signatures orCo-Authored-Bylines. - Do not commit
runner.log,state.env,progress.md, or anything under.devcloud/.