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:
co-authored by
Claude Fable 5
parent
c0a353e476
commit
ff3aa61eca
@@ -81,6 +81,7 @@ All major decisions are in `docs/adrs/`. Run `adr-status` (after `source .envrc`
|
|||||||
- **Runtime bundle distribution**: `./gradlew runtimeBundle` builds a jlink-trimmed JRE + jar tarball for hosts without a Java runtime; GraalVM native image and a containerized runtime were rejected (ADR 0006)
|
- **Runtime bundle distribution**: `./gradlew runtimeBundle` builds a jlink-trimmed JRE + jar tarball for hosts without a Java runtime; GraalVM native image and a containerized runtime were rejected (ADR 0006)
|
||||||
- **Build definitions**: a top-level `builds` section of named builds with `trigger` blocks replaces the branch-owned `autoBuild` schedules (ADR 0007)
|
- **Build definitions**: a top-level `builds` section of named builds with `trigger` blocks replaces the branch-owned `autoBuild` schedules (ADR 0007)
|
||||||
- **bwrap build runtime**: on hosts without root and without Docker (Hostsharing Managed Webspaces), builds run in a `bwrap` user-namespace sandbox over a prepared rootfs — filesystem isolation only, network and uid shared with the host; proot/fakechroot and unisolated native builds were rejected (ADR 0008)
|
- **bwrap build runtime**: on hosts without root and without Docker (Hostsharing Managed Webspaces), builds run in a `bwrap` user-namespace sandbox over a prepared rootfs — filesystem isolation only, network and uid shared with the host; proot/fakechroot and unisolated native builds were rejected (ADR 0008)
|
||||||
|
- **Multi-repo instance**: one instance serves a registry of self-contained repositories (instance config in `~/.werkator.yml`, repo config in each repo); revises the one-instance-per-repository tenet, implementation planned as `docs/plan/22-multi-repo.md` (ADR 0009)
|
||||||
|
|
||||||
## Skills
|
## Skills
|
||||||
|
|
||||||
|
|||||||
@@ -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 B–D); until then this ADR documents the target and the existing behavior remains accurate.
|
||||||
@@ -38,14 +38,13 @@ The pinning model is untouched: pinned keys still come from each repo's machine
|
|||||||
|
|
||||||
### A — Decision and schema (ADR 0009)
|
### 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.
|
- ~~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.
|
||||||
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.
|
|
||||||
- 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).
|
- 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.
|
`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.
|
- 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.
|
- 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.
|
- 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 B–D) — until then the existing behavior description remains accurate; the AGENTS.md decision list carries ADR 0009 already.
|
||||||
|
|
||||||
### B — RepoContext refactor, behavior unchanged
|
### 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
|
## 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 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 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.
|
- Session D: both repos browsable in one UI; single-repo installations keep their existing URLs.
|
||||||
|
|||||||
Reference in New Issue
Block a user