Ops-dashboard. deployen van repos naar docker
  • TypeScript 88.3%
  • Shell 10.3%
  • CSS 0.6%
  • Go Template 0.5%
  • JavaScript 0.2%
Find a file
janpeter 2123c16d68
Some checks are pending
CI / Ops-agent checks (push) Waiting to run
CI / Deploy artifact checks (push) Waiting to run
CI / Docker image build (push) Waiting to run
CI / Root app checks (push) Has started running
package.json bijwerken
2026-08-20 08:10:57 +02:00
.forgejo/workflows fix(ci): install integration test dependencies 2026-07-30 11:07:11 +02:00
.superpowers docs(control-room): track task 12 release brief 2026-08-02 15:14:45 +02:00
app fix(hub-settings): MD3-warning-tokens in plaats van rauwe amber-paletkleuren 2026-08-20 08:02:26 +02:00
components chore(messages): verwijder AgentMessage-model en nav-entry 2026-07-18 09:33:49 +02:00
deploy fix(hub-settings): sudoers-kandidaat valideren vóór het echte bestand aanraken 2026-08-20 07:45:58 +02:00
docs docs(hub-settings): veldkennis uit de uitrol — agent op docker-bridge, fallback-envpad, ts-node-pad is alleen de Mac 2026-08-20 07:53:25 +02:00
hooks fix(control-room): preserve live and dialog state 2026-08-01 16:18:29 +02:00
lib feat(hub-settings): toestellen en approval-historie, met echte statusuitkomst 2026-08-20 03:09:45 +02:00
ops-agent feat(hub-settings): whitelist-entries + install-module (entries in beide kopieën; die lopen historisch uiteen) 2026-08-20 02:44:40 +02:00
prisma feat(hub-settings): snapshotkolommen + ordening-sequence op Instance 2026-08-20 02:49:40 +02:00
public style: use ops dashboard icon set 2026-06-10 16:54:25 +02:00
reviews feat: Add DR-drill plan reviews and updates 2026-05-21 23:35:08 +02:00
scripts feat(hub-settings): snapshot verzamelen en monotoon wegschrijven via de hartslag 2026-08-20 02:59:35 +02:00
test fix(hub-settings): MD3-warning-tokens in plaats van rauwe amber-paletkleuren 2026-08-20 08:02:26 +02:00
vendor chore(vendor): bump scrum4me-copilot 4bef512 -> ca913d2 (M23 kit) 2026-07-09 18:15:23 +02:00
.dockerignore feat: Dockerfile, deploy configs en Caddy-block voor ops.jp-visser.nl 2026-05-13 17:12:37 +02:00
.env.example feat(copilot): catch-all SSE route + 2 user-scoped FlowRun app-tools (Fase 5 B3) 2026-06-12 10:02:25 +02:00
.gitignore Sprint: M21 E2E DEPLOY test — real DONE deploy via auto-chain (non-docs marker) (#96) 2026-07-07 15:31:55 +02:00
.gitmodules feat(copilot): vendor @s4m-kit submodule + tsconfig/next.config/zod (Fase 5 B1+B2) 2026-06-12 09:58:56 +02:00
AGENTS.md docs: Scrum4Me-product-binding (methodiek + product_id) 2026-05-29 09:05:10 +02:00
CLAUDE.md feat: Next.js + Tailwind + shadcn/ui project skeleton 2026-05-13 16:59:21 +02:00
components.json feat: Next.js + Tailwind + shadcn/ui project skeleton 2026-05-13 16:59:21 +02:00
Dockerfile fix(ci): install ops-agent dependencies in image build 2026-07-30 11:38:37 +02:00
next.config.ts feat(copilot): vendor @s4m-kit submodule + tsconfig/next.config/zod (Fase 5 B1+B2) 2026-06-12 09:58:56 +02:00
package-lock.json chore(qr-login): add qrcode.react dependency 2026-06-15 17:53:33 +02:00
package.json package.json bijwerken 2026-08-20 08:10:57 +02:00
postcss.config.mjs feat: Next.js + Tailwind + shadcn/ui project skeleton 2026-05-13 16:59:21 +02:00
prisma.config.ts feat(worker-logs): persist non-idle runs to Postgres via Prisma 2026-05-17 20:51:52 +02:00
proxy.ts feat(qr-login): serve /m/pair as public shell 2026-06-15 18:14:59 +02:00
README.md docs(audit): verwijder stale fragment-instructie uit config-tabel (PR #106) 2026-07-12 07:59:48 +02:00
task-2-fix-rereview.md feat: Add DR-drill plan reviews and updates 2026-05-21 23:35:08 +02:00
tsconfig.json chore(qr-login): revert onnodige tsconfig ES2018-bump (regex /s was redundant) 2026-06-15 17:59:50 +02:00
vitest.config.ts fix(ci): harnas-stub voor het nieuwe script, en een timeout die past bij de suite 2026-08-02 14:27:07 +02:00

Ops Dashboard

Single-user ops dashboard voor jp-visser.nl.

See docs/runbooks/ for setup, deployment, and operational procedures.

CI

Forgejo is the leading forge for this repo. CI lives in .forgejo/workflows/ci.yml and runs on pull requests and pushes to main.

The required merge gate is the CI workflow:

  • root app: npm ci, npx prisma generate with a build-time placeholder DATABASE_URL, npm run test, npm run typecheck, npm run build
  • ops-agent: npm --prefix ops-agent ci, npm --prefix ops-agent run check
  • deployment artifacts: parse deploy/workflow YAML
  • production image: docker build -t ops-dashboard:ci .

The root app and Docker image jobs fetch vendor/scrum4me-copilot over HTTPS with the S4M_COPILOT_READ_TOKEN Actions secret, because @s4m-kit/* resolves from that private submodule. The workflow pins the submodule URL to git.jp-visser.nl before credentials are offered.

Do not add a duplicate .github/workflows/ workflow for the GitHub mirror.

Branch protection

main is protected and requires these exact status contexts before a pull request can be merged:

  • CI / Root app checks (pull_request)
  • CI / Ops-agent checks (pull_request)
  • CI / Deploy artifact checks (pull_request)
  • CI / Docker image build (pull_request)

Direct pushes to main are allowed (enable_push = true, no push whitelist; enabled 2026-07-09). Pull requests remain the default route, but git push origin main is no longer rejected by the pre-receive hook.

Be aware of what that means: the four required contexts above only gate the merge of a pull request. A direct push bypasses them entirely — CI still runs on main afterwards, but nothing blocks the push if it fails. Use a pull request whenever you want the gate to actually hold.

To reinstate the hard gate, set enable_push back to false on the main branch protection (Forgejo: repo → Settings → Branches, or PATCH /api/v1/repos/janpeter/Ops-dashboard/branch_protections/main). Note that Forgejo does not exempt site admins from a protected branch.

Installation

Prerequisites

  • Docker + Docker Compose (plugin) installed on the host
  • A PostgreSQL service named postgres already running in the same Compose stack
  • The repository cloned to /srv/scrum4me/ops-dashboard
  • /srv/scrum4me/compose/docker-compose.yml as the shared Compose file

1. Configure environment

cp deploy/ops-dashboard.env.example /srv/scrum4me/ops-dashboard/.env
# Edit /srv/scrum4me/ops-dashboard/.env — set DATABASE_URL, AUTH_SECRET, etc.

2. Install ops-agent

sudo deploy/ops-agent/setup.sh

This creates the ops-agent system user, installs /opt/ops-agent, generates /etc/ops-agent/secret, and enables the systemd unit.

For an existing install, add or update the Docker inspection module:

sudo deploy/ops-agent/install-docker-inspection-module.sh

The installer restarts ops-agent by default. When batching this with a larger setup/update, run it with OPS_AGENT_SKIP_RESTART=1 and restart the service once after all changes are in place.

Docker inspection uses /etc/ops-agent/container-inspection.yml as the allowlist for containers, expected env keys, and config file paths. Standard views show missing expected keys, redacted logs, and redacted config content; raw env values and config files are only returned by explicit reveal actions.

Copy the generated secret into the web-app env file:

sudo cat /etc/ops-agent/secret
# Paste the value as OPS_AGENT_SECRET= in /srv/scrum4me/ops-dashboard/.env

3. Build and start the dashboard

sudo docker compose -f /srv/scrum4me/compose/docker-compose.yml build ops-dashboard
sudo docker compose -f /srv/scrum4me/compose/docker-compose.yml up -d ops-dashboard

The dashboard is now reachable on 127.0.0.1:3001 (proxied by Caddy).

4. Install the self-update script

sudo deploy/ops-dashboard-updater/install.sh

To enable scheduled updates (daily at 03:00):

sudo systemctl enable --now ops-dashboard-updater.timer

To trigger a manual update via SSH:

sudo systemctl start ops-dashboard-updater.service
# or:
sudo /opt/ops-dashboard-updater/update.sh

Never trigger updates through the dashboard UI — the script restarts the container that serves the UI.

5. Register in the fleet (heartbeat)

Keep this host visible/"online" in the shared fleet registry via a systemd timer that POSTs to the in-container heartbeat endpoint (the containerized app can't run the ts-node CLI heartbeat used on native hosts like the Mac):

sudo cp deploy/ops-dashboard-heartbeat/ops-dashboard-heartbeat.service /etc/systemd/system/
sudo cp deploy/ops-dashboard-heartbeat/ops-dashboard-heartbeat.timer   /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable --now ops-dashboard-heartbeat.timer
sudo systemctl start ops-dashboard-heartbeat.service   # register once now

The endpoint reads INSTANCE_SLUG and INSTANCE_BASE_URL (and optional INSTANCE_CAPABILITIES, INSTANCE_HOSTNAME, INSTANCE_TAILSCALE_IP, APP_VERSION) from the container env and authenticates with OPS_INGEST_SECRET (the same secret the worker-logs ingest uses). In a container os.hostname() is the container id — set INSTANCE_HOSTNAME (e.g. scrum4me-server) in the env for a readable name.

Configuration

File Purpose
/srv/scrum4me/ops-dashboard/.env Web-app environment (DATABASE_URL, AUTH_SECRET, OPS_AGENT_SECRET, …)
/etc/ops-agent/secret Shared HMAC secret between web-app and ops-agent
/etc/ops-agent/commands.yml Whitelist of commands the ops-agent may run
/etc/ops-agent/container-inspection.yml Allowed containers, env keys and configfile paths for Docker inspection/reveal; review and customize before use
/etc/ops-agent/flows/ Flow YAML files (backup, caddy reload, etc.)
/srv/scrum4me/compose/docker-compose.yml Main Compose file (ops-dashboard service is part of this stack)
deploy/ops-agent/commands.darwin.yml macOS command whitelist (git, processes, MCP) for the Mac instance
deploy/ops-dashboard-heartbeat/ systemd timer + service that POST the fleet heartbeat (keeps this host "online")

Ops-agent auth

The web-app communicates with the ops-agent via a shared secret stored in /etc/ops-agent/secret (mode 0640, owner root:ops-agent).

  • The ops-agent reads the secret at startup via OPS_AGENT_SECRET_PATH.
  • Every request from the web-app carries Authorization: Bearer <secret>.
  • The agent validates using a constant-time comparison to prevent timing attacks.
  • The web-app reads the secret value from the OPS_AGENT_SECRET environment variable.

Fail-closed behavior

The agent refuses to start when no secret file is present at OPS_AGENT_SECRET_PATH (it logs the reason and exits non-zero). For local development without a secret, set OPS_AGENT_ALLOW_INSECURE=true to explicitly run in an unauthenticated mode — never use this in a deployed environment. Even at runtime, a missing secret makes every request return 503 (not a silent bypass).

Secret rotation procedure

  1. Generate a new secret on the server:
    openssl rand -hex 32 | sudo tee /etc/ops-agent/secret
    sudo chown root:ops-agent /etc/ops-agent/secret
    sudo chmod 0640 /etc/ops-agent/secret
    
  2. Update OPS_AGENT_SECRET in the web-app's environment file (/srv/scrum4me/ops-dashboard/.env) with the new value.
  3. Restart both services:
    sudo systemctl restart ops-agent
    sudo docker compose -f /srv/scrum4me/compose/docker-compose.yml restart ops-dashboard
    
  4. Verify the dashboard is operational and that systemctl status ops-agent shows the service running without errors.