With -tt allocating a remote PTY, systemctl and journalctl would
sometimes open a pager (less/more) even with --no-pager, leaving
the script blocked until the user hits q or Ctrl-C.
Force PAGER=cat and SYSTEMD_PAGER=cat inside every remote sudo
call and inside the status_block journalctl command. Add
--output=cat to journalctl too as belt-and-suspenders.
Status output is also piped through | head -3 / | head -20 to
guarantee a finite output even if the pager or color escape
handling misbehaves.
Adds password piping so the script works without a sudoers NOPASSWD
rule, on the assumption that the SSH login password is the same as
the sudo password (common on these VMs).
- ssh -tt now forces a TTY allocation; sudo -S requires one and
was failing with 'sudo: no tty present' over plain non-interactive
ssh.
- New run_remote_sudo helper pipes the per-VM password to
'sudo -S -p ""' so each remote call authenticates without a
prompt. The empty -p suppresses '[sudo] password for ...' from
appearing in journal tail output.
- install_unit, restart_unit, and the journalctl call in
status_block all go through run_remote_sudo. systemctl status
no longer needs sudo (the unit is owned by administrator and
status doesn't require root for it).
- If your sudo password differs from the login password, the
script will silently no-op the install/restart steps. Fix by
setting the right password via SDP_92_PASS / SDP_186_PASS, or
add a NOPASSWD rule in /etc/sudoers.d/sdp-deploy and revert
this change.
- systemd/sdp-control-plane.service: plain host process on 186,
listens on :3452, data dir ~/SDP/data. MemoryMax=512M,
Restart=always, ReadWritePaths scoped to the data dir.
- systemd/sdp-agent-micro.service: plain host process on 92,
default SDP_CP_URL=ws://172.18.139.186:3452/ws/agent. Operator
can drop /etc/default/sdp-agent-micro to override. Depends on
docker.service so the dockerd is up before the agent starts.
- systemd/sdp-agent-gateway.service: plain host process on 186,
default SDP_CP_URL=ws://127.0.0.1:3452/ws/agent (loopback since
both live on the same VM). Same env-file override pattern.
- All three use Type=simple, Restart=always, RestartSec=2s. The
agents already reconnect on transient network drops, so
restart-on-crash is the right policy.
- The agents talk to the host dockerd via /var/run/docker.sock to
spawn the actual service containers (sdp-<repo>). Service
containers are managed by docker, not systemd — only the
long-running agents and the control plane are under systemd.
- scripts/deploy.sh: now a one-shot — scp's binaries, dashboard,
and unit files; systemctl daemon-reload + enable --now + restart
each service in the right order (control plane first on 186 so
the gateway agent has something to dial). Prints status + last
10 journal lines per service so the user can see it came up.
- AGENTS.md, README.md: layout tree updated, deploy section
rewritten, the systemd units documented alongside the agents
and control plane.
- control-plane default listen addr is now :3452 (was :8080). An
unusual port to avoid collisions on the VM.
- agent-micro and agent-gateway default SDP_CP_URL points at
ws://localhost:3452/ws/agent. docker-compose.yml updates the
control plane command, host port mapping, and agent -cp URLs.
- nginx/nginx.conf (the legacy root-mount reference) uses
127.0.0.1:3452 for the upstream. nginx/sandbox.conf is the new
deployment config: four location blocks for the /sandbox/credit-card
mount — _next/static serves cached chunks, /api/ and /ws/ proxy
to 127.0.0.1:3452, /sandbox/credit-card serves the static
dashboard with try_files for SPA routing.
- scripts/patch-nginx.sh: deleted. The user configures nginx on 186
by hand. scripts/deploy.sh no longer calls it.
- AGENTS.md: new file. Documents the build/lint/test commands
(with the golang:1.24-alpine container — local Go can't fetch
the toolchain), the wire protocol, the Slice-2 conventions
(sdp-<repo> container naming, snapshot persistence,
PreGitReset/AfterStart hooks), the repo-path gotcha, and the
build-artifacts-in-git rationale.
- dashboard/out: now tracked in git, alongside bin/. The dashboard
static export is scp'd to 186 on deploy; the VMs have no
internet so they can't regenerate it. .gitignore comment
explains this and warns against re-ignoring.
- README.md / REQUIREMENTS.md: status updated to 'Slice 2 done',
per-feature checklist marked. Erangel repo path corrected to
/var/www/html/erangel-ocean (was wrongly ~/SDP in earlier docs).
Sandbox Deployment Platform — Go control plane + agents, NextJS dashboard,
nginx reverse proxy. Cross-compile via Docker; deploy via sshpass to
172.18.136.92 (micro) and 172.18.139.186 (gateway).
- control-plane: HTTP API, WS hub, SQLite (modernc.org/sqlite) for
progress, .log files for log persistence
- agent-micro / agent-gateway: alpine:3.20 + bind-mounted repo,
binary exec'd in container, no Dockerfile build step
- dashboard: NextJS static export + shadcn/ui components, single
WebSocket hook
- docker-compose.yml: three services on alpine:latest with docker
socket bind for agents
- scripts/: build.sh (golang:1.23-alpine cross-compile), deploy.sh,
patch-nginx.sh (idempotent nginx splice), ssh wrappers
Runtime model: pass-through Bitbucket creds per deploy, never logged or
persisted on the agent. Control plane never touches git or docker
directly — agents do all the work locally.