Achmad 78872de897 Slice 2: dashboard — nav, sandboxes/templates/environments/history pages, basePath
- New /dashboard layout with a top nav (Quick Deploy / Sandboxes /
  Templates / Environments / History) and a Logout button that
  invalidates the session.
- Quick Deploy: stage list switches per repo (Go vs PHP, so the
  composer-install stage is shown for the gateway), env-var textarea,
  host-port input.
- Sandboxes: list, create, clone-from-template, delete.
- Sandbox detail: live <key>_url map from the gateway's config.php,
  per-route toggle (OCP / sandbox override with a URL input),
  microservice deploys with per-service host port and env, branch
  picker.
- Templates / Environments: list + create + delete.
- History: filterable deployment list with state badges.
- Sandbox detail page is a server component with generateStaticParams
  that delegates to a client component; required for the static export.
- API client: prefix all /api and /ws URLs with NEXT_PUBLIC_BASE_PATH
  (set in next.config.js) so the dashboard works under a non-root
  basePath.
- next.config.js: basePath and assetPrefix set to /sandbox/credit-card
  so asset URLs and internal Link hrefs resolve under the sub-path.
  NEXT_PUBLIC_BASE_PATH env is exposed to the browser bundle for the
  fetch() prefix.
2026-06-24 03:59:13 +00:00
2026-06-24 07:25:01 +07:00
2026-06-24 07:25:01 +07:00
2026-06-24 01:43:43 +00:00
2026-06-24 01:43:43 +00:00

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 for the full spec.

Status (Slice 1 — build green, MVP core flow)

./scripts/build.sh produces three Linux/amd64 binaries and a static dashboard. The MVP core flow — login, deploy a microservice or the PHP gateway, watch progress and logs in real time — works end to end. The sandbox / template / route management described in REQUIREMENTS.md is deferred to Slice 2 and is not yet built. See REQUIREMENTS.md for a per-feature checklist.

Layout

.
├── protocol/          # shared wire types (Event, DeployRequest)
├── agentlib/          # Go. Shared agent library: gitutil + deployer (Go/PHP flavours)
├── 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 PHP 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, five modules
└── bin/               # build output (gitignored)

agentlib/ is a shared library used by both agents. It owns the git helpers and the per-deployment state machine, which has two constructors for two build flavours:

  • NewGo — for microservices. Runs go build on the host, then docker run alpine:3.20 with the host repo bind-mounted at /src and the binary as the container command. alpine:3.20 must be pre-loaded on the host (see Offline VMs).
  • NewPHP — for the API Gateway. Runs composer install --no-dev on the host as a best-effort step (skipped if composer or composer.json are absent), then docker run php:8.3-apache with the repo bind-mounted at /app. php:8.3-apache must be pre-loaded on the host. The agent is written in Go; the thing it deploys is a PHP project.

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.24-alpine.

Build

./scripts/build.sh

Outputs:

  • bin/control-plane, bin/agent-micro, bin/agent-gateway (Linux/amd64 ELF, statically linked)
  • dashboard/out/ (NextJS static export)

The build script:

  1. Starts a golang:1.24-alpine container with the repo bind-mounted.
  2. apk add git (the base image has none).
  3. Configures safe.directory /src so the container's root user can read the bind-mounted host tree.
  4. Cross-compiles all three binaries with GOOS=linux GOARCH=amd64 CGO_ENABLED=0, -trimpath (reproducible builds) and -ldflags="-s -w" (strip debug info).
  5. chmod +x the binaries inside the container (the host user can't chmod files written by the container's root).
  6. Builds the Next.js dashboard with npm install && npm run build.

The script verifies each binary with file to catch a missing GOOS/GOARCH.

Deploy

./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):

./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 the language build on the host (Go or composer), then docker run <base-image> with the host repo bind-mounted and the binary / apache as the container command. The base image must be pre-loaded.
  • Offline VMs. alpine:3.20 and php:8.3-apache are pre-loaded via docker load. The dashboard is a static export, no runtime fetches.
  • Persistence. Deployment progress goes to SQLite (<data>/sdp.db). Log lines go to append-only <data>/logs/<deploymentId>.log. SQLite uses modernc.org/sqlite (pure Go, no cgo) so the control plane binary stays statically linkable. The driver name is sqlite (not sqlite3).
  • Docker SDK. The agents use the official Moby Go SDK at github.com/moby/moby/client v0.5.0.
  • 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, not Slice 1 scope)

These are marked with ponytail: comments in the code and are scheduled for later slices. They are not in scope for Slice 1.

  • validateViaAgent (login) — accepts any creds if an agent is connected. Real impl: a git ls-remote probe frame to the agent (control-plane/internal/api/api.go:126).
  • handleListRepos / handleListBranches — hardcoded fixtures. Real impl: a list_repos / list_branches frame to the connected agent. The gitutil.ListBranches helper and the agentlib frame protocol are not yet wired up.
  • handleListDeployments (GET) — returns []. Real impl reads SQLite (control-plane/internal/api/api.go:182).
  • WS auth on /ws/deployments/* — open. Real impl checks session token.
  • Sandbox, Template, Route, Environment CRUD — entirely deferred to Slice 2. The data model, REST endpoints, and dashboard pages do not exist yet.

See also

S
Description
No description provided
Readme 10 MiB
Languages
Go 38.8%
HTML 29.5%
TypeScript 25.7%
Shell 4.7%
JavaScript 0.7%
Other 0.6%