# Sandbox Deployment Platform (SDP) Internal deployment platform for Backend/QA. Lets a developer deploy a feature branch into an isolated sandbox, with the API Gateway routing selected services to the sandbox and the rest to OCP. See [REQUIREMENTS.md](REQUIREMENTS.md) for the full spec. ## Layout ``` . ├── protocol/ # shared wire types (Event, DeployRequest) ├── control-plane/ # Go. HTTP API + WS hub + SQLite/.log persistence ├── agent-micro/ # Go. Runs on 172.18.136.92, deploys Go microservices ├── agent-gateway/ # Go. Runs on 172.18.139.186, deploys the API Gateway ├── dashboard/ # NextJS static export, served by nginx ├── nginx/ # reverse proxy + try_files for the dashboard ├── scripts/ # build, deploy, ssh wrappers, nginx patch ├── docker-compose.yml # all three services on alpine:latest ├── go.work # Go workspace — one build, four modules └── bin/ # build output (gitignored) ``` ## Prerequisites - Docker (for the build container) - Node 18+ (for the dashboard) - `sshpass` (for the deploy scripts: `brew install sshpass`) No Go install needed locally — `scripts/build.sh` cross-compiles inside `golang:1.23-alpine`. ## Build ```bash ./scripts/build.sh ``` Outputs: - `bin/control-plane`, `bin/agent-micro`, `bin/agent-gateway` (Linux/amd64 ELF) - `dashboard/out/` (NextJS static export) The script verifies each binary with `file` to catch a missing `GOOS`/`GOARCH`. ## Deploy ```bash ./scripts/deploy.sh ``` This script: 1. SSHs to **172.18.136.92** (`administrator`) and pushes `bin/agent-micro` to `~/SDP/bin/` 2. SSHs to **172.18.139.186** (`administrator`) and pushes `bin/control-plane`, `bin/agent-gateway`, and `dashboard/out/` to `~/SDP/` 3. Idempotently splices the SDP location block into `/etc/nginx/sites-available/default` on 186 and reloads nginx Override the creds via `SDP_92_PASS` / `SDP_186_PASS` env vars. ## Local dev (docker compose) For dev on a single host (e.g. a laptop with Docker): ```bash ./scripts/build.sh docker compose up -d ``` Three services come up on `alpine:latest`: - `control-plane` → `:8080` - `agent-micro` (connects to control plane, has docker socket + repos mounted) - `agent-gateway` (same shape) ## Architecture notes - **Pass-through creds.** Bitbucket credentials travel with each deploy request from control plane to agent, are used once for `git fetch`/`checkout`/ `pull`, and are never logged or persisted on the agent. - **No Dockerfile build on the agent.** Each agent does `go build` on the host, then `docker run alpine:3.20` with the host repo bind-mounted at `/src` and the binary exec'd as the container command. - **No internet on the VMs.** `alpine:3.20` is pre-loaded via `docker load`. The dashboard is a static export, no runtime fetches. - **Persistence.** Deployment progress goes to SQLite (`/sdp.db`). Log lines go to append-only `/logs/.log`. SQLite uses `modernc.org/sqlite` (pure Go, no cgo) so the control plane binary stays statically linkable. - **Realtime transport.** WebSocket end-to-end. Agents connect to `/ws/agent` on the control plane; the dashboard subscribes to `/ws/deployments/{id}`. ## MVP stubs (intentional) These are marked with `ponytail:` comments in the code and will be replaced before production: - `validateViaAgent` (login) — accepts any creds if an agent is connected. Real impl does a `git ls-remote` probe frame. - `handleListRepos` / `handleListBranches` — hardcoded fixtures. Real impl forwards to the connected agent. - `handleListDeployments` (GET) — returns `[]`. Real impl reads SQLite. - WS auth on `/ws/deployments/*` — open. Real impl checks session token. ## See also - [REQUIREMENTS.md](REQUIREMENTS.md) — full spec, infra, MVP success criteria - [nginx/nginx.conf](nginx/nginx.conf) — reference nginx config - [docker-compose.yml](docker-compose.yml) — three-service dev stack