ADR 0009: one Werkator instance serves a set of repositories

Records the tenet revision and its shape: instance registry and
instance keys in ~/.werkator.yml (one instance per OS user), optional
repo defaults in an explicit defaults: block merged below every repo's
own layers, repo config unchanged in each repository, registry-wins
precedence, basename repo names. Rejected: a federation dashboard over
single-repo instances (keeps N services), and the status quo.
Konzept/AGENTS architecture wording changes only when the
implementation lands; the decision list carries 0009 now.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
mhoennig
2026-09-01 14:15:15 +02:00
co-authored by Claude Fable 5
parent c0a353e476
commit ff3aa61eca
3 changed files with 64 additions and 4 deletions
@@ -0,0 +1,60 @@
# One Werkator Instance Serves a Set of Repositories
**Status:**
- proposed: 2026-09-01
- accepted: 2026-09-01
- rejected: -
- superseded: -
**Decision [accepted]:** The founding tenet "one instance per repository" is revised to "one instance per repository *set*": a Werkator instance aggregates self-contained repositories listed in an instance registry, sharing one service, one port, one UI, one watcher schedule, and one global executor cap.
Implementation is planned as `docs/plan/22-multi-repo.md`; this ADR records the decision and its shape, not the code.
## Context and Problem Statement
"One instance per repository" (docs/Werkator-Konzept.md) does not scale even to two repositories on a Hostsharing Managed Webspace: building Werkbaum next to Werkator on mih34 would need a second service, a second assigned port, a second tunnel or domain, a second UI and metrics page.
Every repository multiplies operations while the instance-level resources could be shared.
### Technical Background
Everything repository-specific already lives inside the repository or is keyed by it: machine config with secrets, build results, auto-build slots, worktrees, and buildenvs under `.git/werkator/`; the artifact store under a per-repo key.
An instance can therefore aggregate repositories without absorbing their state — adding or removing a repository is a registry entry, never a data migration, and single-repo mode stays the degenerate case (a registry of one, implicitly the current working directory).
## Considered Options
* One instance per repository set (registry + aggregation) — chosen
* A federation dashboard proxying several single-repo instances
* Status quo: one instance, one repository
### One Instance per Repository Set
Good:
- One service, port, UI, tunnel/domain, metrics page for any number of repositories.
- Repositories stay self-contained; per-repo secrets, history, and pinning semantics are untouched.
- Global concurrency control across all repositories.
Bad:
- The repo dimension must be threaded through executor pools, watcher cycle, routes, and UI — the largest refactor since the rewrite (mitigated by a behavior-preserving `RepoContext` session first).
- Instance-level and repo-level configuration must be split cleanly (see below).
### Federation Dashboard over Single-Repo Instances
Rejected: less invasive, but it keeps N services, N ports, and N tunnels — it solves only the UI aggregation, not the operations burden that motivated the change.
### Status Quo
Rejected: on a webspace, ports and domains are the scarce, manually assigned resource; per-repository services do not scale there.
## Decision Outcome
One instance per repository set, with the configuration split decided 2026-09-01:
- **Instance config** lives in `~/.werkator.yml` in the home directory of the user running the instance — one instance per OS user, matching the platform model. It carries `server.*` (port, domain/public base URL, nginx), the repository registry, the control token, the global `executor.maxConcurrent`, and the watcher schedule. The file name stays `.werkator.yml` in all three locations; the location carries the meaning.
- **Repo defaults** may live in the home file, but only in an explicit `defaults:` block, merged *below* every repository's own layers (home defaults → committed project config → repo machine config → branch layer); pinning semantics are unchanged. Accepted cost: secrets may then live in two places.
- **Repo config** stays in each repository: the committed `.werkator.yml` and the machine config in its `.git/werkator/`.
- Instance keys found in a repo's machine config are ignored with a warning naming both files once a home config exists — never merged silently.
- When a home config with a registry exists, `werkator server` serves the registry regardless of the current directory; without one it serves the current directory exactly as before.
- Repository names (routes, UI) default to the directory basename, are overridable per registry entry, and duplicates abort the start loudly.
Consequences: `docs/Werkator-Konzept.md` and AGENTS.md change their wording when the implementation lands (plan step 22 sessions BD); until then this ADR documents the target and the existing behavior remains accurate.
+3 -4
View File
@@ -38,14 +38,13 @@ The pinning model is untouched: pinned keys still come from each repo's machine
### A — Decision and schema (ADR 0009)
- Write ADR 0009: revise the one-instance-per-repository tenet to one-instance-per-*set*; record the aggregator idea and the key ownership split above.
Considered alternative to record: a federation dashboard proxying several single-repo instances — less invasive, but it keeps N services/ports and solves only the UI, not the operations burden.
- ~~Write ADR 0009~~ — done 2026-09-01: `docs/adrs/0009-2026-09-01.multi-repo-instance.md` revises the one-instance-per-repository tenet to one-instance-per-*set* and records the aggregator idea, the key ownership split, the four 2026-09-01 decisions, and the rejected federation-dashboard alternative.
- Define the instance config: `~/.werkator.yml` (decided 2026-09-01) — the repository registry plus the instance-level keys above; one instance per OS user, which matches the platform model (pac users on a webspace, service users elsewhere).
`werkator server` without a home config serves the current directory exactly as today.
- Decide the transition for instance keys that today sit in a repo's machine config (mih34's carries `server.*`): once a home config exists, repo-level instance keys are ignored with a warning naming both files — never merged silently.
- Repo identity for display and routes (decided 2026-09-01): a short unique name per registry entry, defaulting to the repository's directory basename, overridable in the entry; duplicate resulting names abort the start loudly. Used as the route segment (`/repos/<name>/…`) and UI grouping key.
- Precedence (decided 2026-09-01): when a home config with a registry exists, `werkator server` serves the registry regardless of the current directory — one user, one instance, deterministic; without a home config it serves the current directory exactly as today.
- Update `docs/Werkator-Konzept.md` and AGENTS.md wording ("one instance per repository set").
- Update `docs/Werkator-Konzept.md` and the AGENTS.md architecture wording ("one instance per repository set") when the implementation lands (sessions BD) — until then the existing behavior description remains accurate; the AGENTS.md decision list carries ADR 0009 already.
### B — RepoContext refactor, behavior unchanged
@@ -80,7 +79,7 @@ The pinning model is untouched: pinned keys still come from each repo's machine
## Acceptance Criteria
- Session A: ADR 0009 merged; registry and key-ownership documented in `docs/configuration.md`.
- Session A: ADR 0009 written (done 2026-09-01); the registry and key ownership land in `docs/configuration.md` together with the implementing sessions, since that reference describes implemented configuration only.
- Session B: full suite green with `RepoContext` threaded through; no route or behavior change observable.
- Session C: an instance with two registered repos builds pushes in both, with per-repo error isolation proven by a test (one broken origin, the other keeps building).
- Session D: both repos browsable in one UI; single-repo installations keep their existing URLs.