From 4ab04d29a82fcc54629262c0bc12ff6e721401c0 Mon Sep 17 00:00:00 2001 From: Niklas Ye Date: Thu, 1 Oct 2026 19:16:53 +0200 Subject: [PATCH] DESIGN.md: document operator mode and what it deliberately doesn't lock MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No operator-mode section existed here before -- terdut-server's own README.md documents the feature, but this repo's design doc never mentioned it. Added as §6 point 7, confirmed against source (internal/api/middleware.go's OperatorModeBlock, router.go's opMode wrapper): it blocks human writes to exactly the resources this operator's CRDs manage (team identity, OIDC-group binding, escalation, dead man's switches, integrations), and nothing else -- team membership, invites, and the on-call schedule/rota stay human-editable regardless, confirmed from the router rather than assumed from the README's prose alone. --- DESIGN.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/DESIGN.md b/DESIGN.md index 7102e3e..61bfd64 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -566,6 +566,27 @@ when nothing ever crosses into a tenant namespace in the first place. Secret is updated in place. No DB-level workaround, no re-triggering a single-shot endpoint that can't fire twice (which is what made rotation unworkable under the old `/api/bootstrap`-only design). +7. **Operator mode** (`TERDUT_OPERATOR_MODE`, terdut-server's own + deploy-time flag, off by default) is the complementary half of this + trust model: it makes terdut-server itself refuse a *human* write (a + session or a user's own API key) on a route, while a service account's + — this operator's — still goes through. Confirmed against source + (`internal/api/middleware.go`'s `OperatorModeBlock`, + `internal/api/router.go`'s `opMode` wrapper): the blocked set is exactly + team create/rename/delete, a team's OIDC-group binding, its escalation + policy, its dead man's switches, and its integrations — precisely the + resources `TerdutTeam`, `TerdutEscalationRule`, `TerdutDeadmanSwitch` and + `TerdutAlertSource` manage, and nothing more. **Deliberately not + blocked, confirmed against the same router**: team membership and + invites (the router's own comment: "membership is deliberately never + gitops-managed"), and the on-call schedule/rota + (`/api/teams/{teamID}/schedule`, `/api/schedule/current`) — neither + route carries the `opMode` wrapper at all. A human can still add or + remove a team member, or assign who's on call, on a server running in + operator mode; only the CRD-shaped resources above are locked to + GitOps. This isn't a gap to close — it's the same boundary §4.2 already + draws for membership, confirmed to hold on the server side too, not + just stated as an intent here. ## 7. Ownership, status, garbage collection