Implement ACTIVITY-WP-0024 operator automation console
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 2s
Build and Publish Container Image / build-and-push (push) Successful in 36s

Add /ops REST inventory, status, runs, and fail-closed operator-token
mutations (trigger, enable/disable, pause/unpause) with audit trail.
Ship thin HTML UI at /ops/ui, runbook/k8s access docs, and contract tests.
This commit is contained in:
tegwick 2026-07-21 23:51:39 +02:00
parent 81d350de71
commit 71027f0a67
11 changed files with 1494 additions and 20 deletions

View file

@ -43,11 +43,92 @@ ACTCORE_DB_URL=postgresql+asyncpg://actcore:actcore@localhost:5433/actcore \
|---------|-----|
| Temporal Web UI | http://localhost:8080 |
| REST API docs (Swagger) | http://localhost:8010/docs |
| Operator console UI | http://localhost:8010/ops/ui |
| Operator status JSON | http://localhost:8010/ops/automations/status?since=sunday |
| NATS monitoring | http://localhost:8222 |
| Prometheus metrics (worker) | http://localhost:9090/metrics |
---
## Operator automation console (ACTIVITY-WP-0024)
Prefer the **ops console** over ad-hoc SSH/SQL for “did automations run?” and
“run this now”.
### Auth
| Env | Purpose |
| --- | --- |
| `ACTIVITY_CORE_OPERATOR_TOKEN` | Shared operator token; required for **mutations** |
| `ACTIVITY_CORE_OPS_ALLOW_UNAUTH_MUTATIONS` | `1` only for local dev without a token |
Mutations: `POST /ops/automations/{id}/trigger|enable|disable|pause|unpause`
Header: `X-Operator-Token: <token>` (or `Authorization: Bearer <token>`).
Fail-closed: if the token is unset and unauth is not allowed, mutations return
**403**. Reads (`GET /ops/...`) do not require the token (ClusterIP / port-forward
posture). **Do not** put the token in git, chat, or workplans. Store in
`actcore-runtime-secret` (or local `.env`) via operator custody.
### Daily checklist
```bash
# How did automations go since Sunday?
curl -sS "http://localhost:8010/ops/automations/status?since=sunday" | python3 -m json.tool
# or CLI equivalent:
make automation-status SINCE=sunday
# Inventory
curl -sS "http://localhost:8010/ops/automations" | python3 -m json.tool
# Run now (requires token)
curl -sS -X POST "http://localhost:8010/ops/automations/<id>/trigger" \
-H "X-Operator-Token: $ACTIVITY_CORE_OPERATOR_TOKEN" \
-H "Content-Type: application/json" -d '{}'
# Side-effect activities (e.g. forgejo prune) need explicit confirm:
curl -sS -X POST "http://localhost:8010/ops/automations/<id>/trigger" \
-H "X-Operator-Token: $ACTIVITY_CORE_OPERATOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"confirm_side_effect": true}'
# Pause / disable schedule (token required)
curl -sS -X POST "http://localhost:8010/ops/automations/<id>/pause" \
-H "X-Operator-Token: $ACTIVITY_CORE_OPERATOR_TOKEN"
curl -sS -X POST "http://localhost:8010/ops/automations/<id>/disable" \
-H "X-Operator-Token: $ACTIVITY_CORE_OPERATOR_TOKEN"
```
Thin UI: open `/ops/ui`, paste the operator token into the browser field
(localStorage only), then use Run now / pause actions. **Cron edits are not in
the UI** — change definition files and sync.
### Production access (railiance01)
API remains **ClusterIP** (no public Ingress in WP-0024).
```bash
# From a machine with kubectl to railiance01:
kubectl -n activity-core port-forward svc/actcore-api 8010:8010
# Browser: http://127.0.0.1:8010/ops/ui
# Ensure ACTIVITY_CORE_OPERATOR_TOKEN is set on actcore-api (runtime secret key).
```
Bootstrap token (operator workstation; never commit the value):
```bash
# Generate and inject (example — adjust secret key name to match cluster)
TOKEN=$(openssl rand -hex 24)
kubectl -n activity-core create secret generic actcore-runtime-secret \
--from-literal=ACTIVITY_CORE_OPERATOR_TOKEN="$TOKEN" \
--dry-run=client -o yaml | kubectl apply -f - # only if creating fresh;
# Prefer: kubectl patch / edit to merge the key into existing secret, then
kubectl -n activity-core rollout restart deploy/actcore-api
unset TOKEN
```
---
## REST API — common operations
```bash