Phase 5: multi-host agents (0.5.0)

- stackpilot-agent: slim token-guarded FastAPI (reuses compose_service) exposing
  stack CRUD/lifecycle/logs + system info; same image, different CMD. agent/
  Dockerfile + compose + .env.example.
- Central proxy: Agent model, agent_service (httpx ping/proxy + live status:
  online/offline/unauthorized + hostname/last_seen), routers/agents.py
  (CRUD + ping + proxied stacks/lifecycle/logs/system).
- Frontend: Settings → Remote hosts (add/check/remove, connectivity dot); Stacks
  grouped by host; remote stack detail with lifecycle, live logs, compose/.env edit.

Verified end-to-end: agent+main on a shared network — register (good/bad token),
list/create/start/logs/delete remote stacks, offline detection (502).

Remote backup destinations (SFTP/S3) deferred.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
menzelj
2026-06-07 21:23:17 +00:00
co-authored by Claude Opus 4.8
parent 8d19b09abd
commit 59037f4287
21 changed files with 1380 additions and 39 deletions
+7
View File
@@ -0,0 +1,7 @@
# Shared secret the central StackPilot must present to manage this host.
# Generate with: openssl rand -base64 32
# Enter the SAME value when adding this host under Settings → Remote hosts.
AGENT_TOKEN=change-me-to-a-long-random-shared-secret
# Host directory where this host's stack folders live.
STACKS_HOST_DIR=./data/stacks
+14
View File
@@ -0,0 +1,14 @@
# The agent reuses the backend image (same compose/Docker code + deps) and
# just runs a different ASGI app. Build the backend image first.
ARG BACKEND_IMAGE=10.10.5.10:3020/menzelj/stackpilot-backend:latest
FROM ${BACKEND_IMAGE}
ENV STACKS_DIR=/opt/stacks \
PORT=5010
EXPOSE 5010
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s \
CMD curl -fsS http://localhost:5010/agent/health || exit 1
CMD ["uvicorn", "agent_app:app", "--host", "0.0.0.0", "--port", "5010"]
+23
View File
@@ -0,0 +1,23 @@
# StackPilot agent — deploy this on each remote host you want to manage.
# It needs only the Docker socket and a shared AGENT_TOKEN (must match the
# token you enter when adding this host in the central StackPilot UI).
services:
agent:
image: 10.10.5.10:3020/menzelj/stackpilot-agent:latest
build:
context: .
args:
BACKEND_IMAGE: 10.10.5.10:3020/menzelj/stackpilot-backend:latest
restart: unless-stopped
environment:
- AGENT_TOKEN=${AGENT_TOKEN:?set AGENT_TOKEN in .env}
- STACKS_DIR=/opt/stacks
- HOST_PROC_PATH=/host_proc
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ${STACKS_HOST_DIR:-./data/stacks}:/opt/stacks
- /proc:/host_proc:ro
# Read-only host devices for status/detection parity with the main host.
- /dev:/dev:ro
ports:
- "5010:5010"