Getting Started
Clone, run the stack locally, and understand the day-one workflow for the Octopus platform.
This page gets a new engineer from zero to a running local stack, and points at the conventions you'll live by.
Prerequisites
| Tool | Version | Notes |
|---|---|---|
| Go | 1.25 | Backend language. |
| Docker + Compose | latest | Runs Postgres, Valkey, RabbitMQ, OTel collector, MySQL. |
| Bun | latest | All JS/TS work (frontends). Never use npm/npx. |
| Node.js | 20 | Only the runtime for the Next.js client portal in prod; local dev uses bun. |
| golangci-lint | latest | Required to pass before commit. |
Use bun, never npm. Every JS/TS directory (frontend/grasshopper, frontend/client, frontend/octopus-website, frontend/octopus-docs, frontend/mocky-balboa, this wiki) uses bun install / bun run x / bunx. Some READMEs are generator boilerplate that still say npm run dev — ignore them.
0. Install the toolchain (fresh machine)
If you're on a brand-new laptop, install everything first. Pick your OS.
macOS (Homebrew):
# Core
brew install go # Go 1.25+ (verify: go version)
brew install --cask docker # Docker Desktop — launch it once so the daemon runs
brew install oven-sh/bun/bun # Bun (all JS/TS work)
brew install node # Node 20+ (runtime for the client portal build/run)
brew install golangci-lint # linter (must pass pre-commit)
brew install postgresql@16 # optional: psql client for DB investigation
brew install cloudflared # optional: Cloudflare tunnel/CLI helpersLinux (Debian/Ubuntu): the server bootstrap script installs the same core stack (Docker, Go, nginx, certbot) — see deploy/init.sh. For a dev box you only need Go + Docker + Bun + Node + golangci-lint; install Go from go.dev/dl, Docker via the official apt repo, Bun via curl -fsSL https://bun.sh/install | bash, Node 20 via NodeSource, and golangci-lint via its install script.
Verify:
go version # go1.25.x
docker info # daemon reachable
bun --version
node --version # v20.x
golangci-lint versionLinter config
The repo pins its linter rules in .golangci.yml (golangci-lint v2 format, default: none with an explicit enable-list: govet, staticcheck, errcheck, ineffassign, unused, unparam, and more; 15m timeout, read-only modules). Run exactly golangci-lint run ./... — the config, not your global defaults, decides pass/fail.
AI-agent tooling (Claude Code, Cursor, etc.)
This repo is set up to be worked on with AI agents, and there are conventions you should adopt on day one:
CLAUDE.md(repo root) — the project brief loaded into every Claude Code session: commands, architecture, coding standards, and the absolute DB safety rule (no ad-hoc writes). Cursor/Copilot users should read it too.AGENTS.md— detailed coding standards for AI agents (errcheck patterns, test-design rules, common codebase patterns).CLAUDE.mddefers to it..claude/settings.local.json— per-repo Claude Code permission allow-list (whichBash/WebFetchcalls run without a prompt). Safe to extend; it's how you cut down permission prompts for common commands likego build,go test,bun run,wrangler d1 migrations apply.- Cloudflare MCP — the live Cloudflare account can be introspected read-only via the Cloudflare MCP server (this is how the Cloudflare page was built). Add it in your Claude Code MCP settings if you'll manage CF.
- Persistent memory — the previous maintainer accumulated a large body of project-specific knowledge in Claude's memory; it's preserved verbatim in the Memory Appendix. Skim it before making changes.
Rotate the leaked sandbox DB password
.claude/settings.local.json currently contains a real sandbox Postgres password in plaintext (inside pre-approved PGPASSWORD=… psql -h 94.130.137.222 … allow-list entries). Treat it as compromised: rotate the octopus DB password on the sandbox host and scrub those entries from the settings file. Never paste live credentials into permission allow-lists — approve the command shape, not the secret.
1. Clone & start the data plane
git clone <repo> octopus && cd octopus
docker compose up -d # postgres:5432, valkey:6379, rabbitmq:5672 (+15672 UI), otel:4328, mysql:3306The compose stack uses user/pass octopus/octopus for Postgres and RabbitMQ by default. Data persists under ./.data/.
2. Configure env
The backend auto-loads a .env at the repo root (via godotenv/autoload). There is no central config struct — config is read ad-hoc through utils.FetchEnv(name, default). The important variables:
# Core
PORT=:8081
APP_ENV=local
LOG_LEVEL=info
# Feature flags — turn on the verticals you need
FEATURE_VOUCHERS_ENABLED=true
FEATURE_TOPUPS_ENABLED=true
FEATURE_ESIM_ENABLED=true
FEATURE_PAYOUTS_ENABLED=false
FEATURE_SHOPIFY_ENABLED=false
# Database (postgres default; sqlite also supported via DATABASE=sqlite)
PG_HOST=localhost
PG_PORT=5432
PG_USER=octopus
PG_PASS=octopus
PG_DB=octopus
# Cache & queue
CACHE_TYPE=valkey # or memory / nil
VALKEY_ADDR=localhost:6379
QUEUE_PROVIDER=rabbitmq # or memory
QUEUE_CONNECTION=amqp://octopus:octopus@localhost:5672/
QUEUE_EXCHANGE=app.default
# Auth (generate real secrets for anything shared)
JWT_SECRET=<openssl rand -base64 64>
APP_KEY=<openssl rand -base64 32>
# Observability (optional locally)
OTEL_ENABLED=falseSee Architecture → Configuration for the full env catalogue.
3. Migrate & seed
You run migrations manually — the app never auto-migrates. The maintainer applies both Goose (Postgres) and D1 (Grasshopper) migrations; agents only write them.
go run main.go migrate up # apply all pending Goose migrations
go run main.go seed all # reference data + fixtures (idempotent)
# or: go run main.go seed fresh # reference data only, preserves clients & vendors4. Run the app
go run main.go server --debug # HTTP API on :8081, pprof on :6060
go run main.go run-all --debug # server + workers + all crons together (prod-style)
go run main.go cron inventory-pump pending-order-retry # run specific crons only- API:
http://localhost:8081(routes under/api/v1,/auth,/client,/g2a,/webhooks). - Admin UI (Jet templates):
http://localhost:8081/admin.
5. Run a frontend (example: client portal)
cd frontend/client
bun install
bun run dev # Next.js on :3001 (Turbopack)Grasshopper (claim worker): cd frontend/grasshopper && bun install && bun run dev (Wrangler dev on :8787). Local secrets go in .dev.vars (see .dev.vars.example).
Day-to-day: adding things
| I want to… | Touch these |
|---|---|
| Add an API endpoint | handler in http/handler/, wire in http/routes/{routes,admin,client}.go, data in database/repo/<aggregate>.go, model in database/models/ |
| Add a background job | implementation in jobs/, cron wrapper in scheduler/, register (feature-gated) in main.go init() |
| Add a vendor | adapter under services/external_vendors/<line>/, register in that line's factory.go, add codes in types/common.go |
| Add a migration | go run main.go migrate create <name> → edit the generated SQL → ask the operator to apply |
Pre-commit checklist
Every change must pass:
go build ./...
go vet ./...
go fmt ./...
golangci-lint run ./...
go test ./...Plus: no hardcoded secrets, no PROD URLs in test files, and X-Automation-Request safety verified (automation requests are blocked in prod). See CLAUDE.md / AGENTS.md for the full standards, and the Memory Appendix for house rules like errcheck patterns and test-design conventions.
The verbatim knowledge base
The previous maintainer accumulated a large body of project-specific knowledge — spec-drift notes for vendors, architectural decisions, UI conventions, and more. All of it is preserved in the Memory Appendix. When something surprises you, check there first.
Start Here — Reading the Labyrinth
How this handover wiki is organized and how to read it — a map of the sections, reading paths by goal (onboarding, firefighting, tracing an order, following the money, touching a vendor, changing the API, deploying), the conventions (file:line citations, Callouts, the 🔴🟠🟢 Known-Issues index, the Memory Appendix), and the trust rules.
Known Issues & Triage
The single prioritized index of every bug, security gap, dead-code smell, and doc discrepancy surfaced while writing this wiki — grouped by severity, each linked to the detail page. Start here.