# R-717 — opengist's sign-up switch: its container has no tool to write it: a one-page design (2026-10-08) **Status:** design only. Read today: controller `main` a0370b4ed8ef, catalog `main` 32d134639c0c, opengist upstream `master` (fetched 2026-10-08). Architecture: `09-update-architecture.md` §3 decisions 46-47 (setup gate, sign-up block), `01-topology-and-trust.md` §5 (the gate). ## Where it stands - Wishlist is done (controller v0.301.0 `open_command`, proven on 9202 2026-10-06). The row is narrowed to opengist. - Opengist (`templates/opengist/docker-compose.yml`, image `ghcr.io/thomiceli/opengist:1.15`) is closed by the address block alone: `signup_block: "PathRegexp(`(?i)^/+-/+register`)"` (`.felhom.yml:42`). Measured 2026-09-29: every trick shape — trailing slash, upper/mixed case, percent-encoded, double slash, query string — answers 403 (`audits/signup-lock-2026-09-29/B/B6-tricks-all-regex.txt`). The app publishes no port; traefik is its only door. - **Upstream, read today:** the switch is row `disable-signup` in opengist's admin-settings table; startup seeds it to `"0"` from a hard-coded map — no config key and no env feeds it (`internal/db/db.go`; `config.yml` has no signup/register key). **The settings are read from the database on every request** (`dataInit` middleware → `loadSettings` → `db.GetSettings()`, `internal/web/server/middlewares.go`). So a row written while the app runs takes effect at the next request — the row's „needs its sqlite with the app stopped" is not needed. - The controller already embeds a pure-Go SQLite (`modernc.org/sqlite`, `go.mod:11`) and already runs short helper containers on app volumes (`docker run --rm -v vol:…`, `internal/stacks/undo.go:205-235`, image `alpine`, which has no sqlite). ## Options | | What | Costs | Risk | |---|---|---|---| | **A — the box writes the row with its own image** | New `after_setup` form `sqlite: {volume, file, close_sql, open_sql, check_sql}`; the controller runs `docker run --rm -v opengist_data:/v ` that opens the file with modernc sqlite (busy timeout), runs the statement, reads it back and prints the marker. Feeds the existing close/open/loop machinery unchanged. | ~1 session controller + catalog line + a 9202 proof. No new external image. | A second writer on a live SQLite (normal locking; same kernel). A schema rename upstream → the read-back fails → the existing failure record, never silent. | | **B — a sidecar with a sqlite tool** | Add a small sqlite image as a second service in the template. | ~½ session. | **A new external dependency** (fenced — operator only), RAM and an image to keep current on every box. | | **C — keep the address block alone** | Accept the measured block as the lock for opengist; close the row as decided. | Nothing. | The switch stays „open" inside the app; only a path traefik does not see could reach it, and the app has no such path today. | **Pick: C, with A ready.** The block is measured against every trick shape and the app has no door but traefik, so A buys defence in depth for a P3-LOW. A is the right build if the operator wants the app's own switch closed too: it adds no dependency and serves any later app whose switch lives only in its database. **First slice of A (only on the operator's yes), with its red test:** `after_setup.go` accepts the `sqlite` form; a test with a temp SQLite file runs close → the read-back prints the marker and the row reads `1`; open → `0`; a missing table → an error and no marker (red today: the form is unknown and the spec is refused). Then the opengist template line and a 9202 proof in wishlist's shape (stranger 403/refused after setup, a family member signs up inside the window, closed again after it). **Small, for the parent now (comments only, no decision):** `internal/stacks/after_setup.go:33` names opengist among the apps „closed by `command`" — today only wishlist is; `internal/stacks/signup_block.go:22` shows opengist's block as ``PathPrefix(`/-/register`)`` while the template uses the case-insensitive `PathRegexp` above. ## One question for the operator **Is the measured address block enough for opengist, or should the box also close opengist's own sign-up switch (about one working session)?** *If you do nothing:* opengist stays closed by the address block alone, and the row closes as decided.