Commit Graph
151 Commits
Author SHA1 Message Date
mhoennigandClaude Sonnet 5 ed89911871 docs(prs): the maintenance-page PR-doc
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 17:49:21 +02:00
mhoennigandClaude Sonnet 5 ca9c23d55b Show a static maintenance page while the service restarts
A deployment's ErrorDocument-covered downtime window looked dead: Apache's
default 502/503/504 error page, or a hung request, while instance-update
briefly restarts the systemd unit. init --systemd now also writes a static
werkator-maintenance.html next to the generated .htaccess, and the .htaccess
maps a refused connection straight to it — served by Apache alone, no
change needed to Werkator itself since it is exactly the process that is
down.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 17:48:06 +02:00
mhoennigandClaude Sonnet 5 06967c551e docs(prs): PR#16 verified live on mih09
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 17:40:07 +02:00
mhoennigandClaude Sonnet 5 61c235313f Implement quota-aware disk metrics (PR#16)
The system page showed the disk of the host volume, not the budget the
instance can actually fill — on a Hostsharing Managed Webspace that is a
group quota, tighter than the volume by an order of magnitude, so the
warn/critical highlighting could never fire before a build failed with
"Disk quota exceeded".

DiskQuota parses `quota -u -g --no-wrap --raw-grace` and picks the
tightest of the user quota, the group quota, and the volume itself;
SystemMetricsCollector reports whichever binds, resets the disk
min/max/avg when the binding source changes, and the system page names
the source in its info line. A host without a binding quota (Docker
hosts, developer machines) renders exactly as before.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 17:23:39 +02:00
mhoennigandClaude Sonnet 5 be965617cd docs(prs): das PR-Dokument der Quota-Anzeige trägt seine echte Nummer — PR#16
Die alte GitHub-PR#14 kollidiert mit der Gitea-Nummerierung, die nach dem
Umzug bei 1 neu beginnt; PR#16 ist die tatsächliche Nummer auf git.javagil.de.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-03 17:04:27 +02:00
mhoennigandClaude Fable 5.1 7eac1c342d docs(prs): das PR-Dokument der Quota-Anzeige trägt seine echte Nummer — PR#14
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 17:04:27 +02:00
mhoennigandClaude Fable 5.1 623266e060 docs(prs): PR#13 — die Anzeige nimmt den kleinsten Spielraum aus User-Quota, Gruppen-Quota und freiem Plattenplatz
Die Volume-Zahlen sind nicht mehr nur Rückfall, sondern dritter Kandidat: Gebunden
ist, was am wenigsten Platz lässt; eine später eingeführte User-Quota geht ohne
Änderung in den Vergleich ein.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 17:04:27 +02:00
mhoennigandClaude Fable 5.1 22a1b28824 docs(prs): Plan für die quota-bewusste Plattenplatz-Anzeige der Systemübersicht — PR#13
Auf mih09 zeigt /system die 71-GiB-Platte des Hosts, gemessen bindet aber die
Gruppen-Quota des Pakets (8 GiB soft, 12 GiB hard, 1,04 GiB belegt). Das PR-Doc
plant: quota(1) per CLI lesen, die bindende Zeile (User/Gruppe, Dateisystem des
Repos) als Disk-Budget nehmen, ohne Quota unverändert der File Store, Serien-Reset
bei Quellenwechsel, Budget in der Info-Zeile benannt. Nur Plan, kein Code.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 17:04:27 +02:00
mhoennigandClaude Fable 5.1 fe11a339ad UI: das Repository wird in der Seitenüberschrift per Drop-Down gewählt
Die Link-Leiste neben dem Menü entfällt. An ihrer Stelle steht dort, wo
bisher der Repository-Name stand, eine Auswahlliste der bedienten
Repositories; werkator.js hängt die aktuelle Ansicht (branches, history,
current) an, damit der Wechsel auf derselben Seite bleibt. Mit nur einem
Repository bleibt der Name wie bisher reiner Text.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-03 15:26:29 +02:00
mhoennigandClaude Opus 5 e61adfbbaa docs: nachgemessen, wann die entfernte Build-Definition wirklich verschwindet
Die offene Frage im PR-Dokument sagte „löst sich beim Merge". Das Log sagt
es genauer: Am Merge-Commit wurde `werkdock` noch eingereiht, erst am
nächsten nicht mehr. Die Projektschicht ist die Datei im Arbeitsbaum des
beobachteten Klons — die Definition überlebt, bis dieser Baum an der
Entfernung vorbei ist, der Merge selbst bekommt also einen letzten
Geister-Build.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 15:22:10 +02:00
mhoennigandClaude Opus 5 c0fd1de954 docs: das PR-Dokument der Werkdock-Herauslösung trägt seine echte Nummer
Geschrieben wurde es als #14 — vergeben wurde beim Öffnen #15. Die
Namenskonvention (docs/prs/README.md) will die Nummer aus dem Tracker,
sonst zeigt später nichts mehr aufeinander.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 15:18:14 +02:00
Michael HönnigandClaude Opus 5 ac53b5f211 step 21e werkdock moved to own repo (#15)
* refactor: werkdock/ verlässt dieses Repository

Die neun Commits unterhalb von werkdock/ sind per `git subtree split`
historieerhaltend nach https://git.javagil.de/mi/werkdock gehoben; hier
bleibt nichts davon zurück.

Mit dem Verzeichnis geht die Build-Definition `werkdock`: Sie beschrieb ein
Go-Modul, das der Kotlin-Build um sie herum nicht berühren kann — jeder
Werkator-Branch baute es dennoch als `<branch>@werkdock` mit. Drüben ist
sie die einzige und heißt `default`.

Die Kopplung bleibt, wie Sitzung C sie gemacht hat: Werkator ruft das
`werkdock`-Binary über PATH (`bwrap.werkdock`, weiterhin gepinnt), so wie
es `git` ruft.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* refactor(remote): das werkdock-Binary kommt aus einem eigenen Checkout

Bisher baute das Skript es aus dem Unterverzeichnis. Jetzt nennt
WERKDOCK_REPO den Checkout (Vorgabe: ein Geschwister dieses Repositories —
die übliche Ablage, wenn man an beidem arbeitet), WERKDOCK_BINARY das
gebaute Binary darin; beides ist wie jeder Transportwert aus der Umgebung
übersteuerbar.

Fehlt der Checkout, nennt die Meldung die Klon-URL, statt bloß „file not
found" zu sagen: Das Skript kann die Lage nicht mehr selbst beheben, also
sagt es, was sie beheben würde.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs: Sitzung E — Werkdock hat ein eigenes Repository

Plan-Schritt 21 bekommt die Sitzung, die die Herauslösung beschreibt, samt
Abnahmekriterium; Plan-Index und Architektur-Skill sagen nicht mehr, sie
stehe noch bevor, und der Deployment-Leitfaden verlinkt das Repository
statt eines Verzeichnisses, das es hier nicht mehr gibt.

Dazu das PR-Dokument nach Hausregel (docs/prs/README.md).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(remote): repo-add wendet das Instanz-Fragment an

`repo-add` rief `werkator init` ohne `--apply`, `repo-init` dagegen mit.
Folge: Ein so aufgenommenes Repository bekam die Sandbox-Politik nicht —
`bwrap.rootfs` und `bwrap.werkdock` fehlten, und der erste Build lief auf
dem nackten Host statt in der Sandbox. Gefunden beim Aufnehmen von
Werkdock: `gofmt: command not found`, weil die Go-Toolchain nur im
Sandbox-Image liegt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* fix(remote): repo-add erkennt einen ~/-Registry-Eintrag, und der Kommentar sagt die Wahrheit

Die Registry-Prüfung suchte nur den absoluten Pfad und meldete deshalb
„not registered yet“, obwohl der Eintrag als `path: ~/werkdock` dasteht —
eine Einladung, ihn ein zweites Mal einzutragen. Gegriffen wird jetzt
lokal, damit beide Schreibweisen ohne zweite Schicht Remote-Quoting
treffbar sind.

Dazu der Kommentar über dem Laden der Env-Datei: Er behauptete, die
ausdrückliche Umgebung gewinne. `set -a; source` weist unbedingt zu — die
Datei gewinnt. Beim Nachziehen des Werkdock-Fragments hat mich genau das
eine Runde gekostet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 15:15:15 +02:00
mhoennigandClaude Opus 5 c1629cace3 feat(remote): repo-add bereitet ein weiteres Repository für die Registry vor
`tools/remote` konnte genau ein beobachtetes Repository einrichten
(`repo-init`, fest auf $WERKATOR_PATH/werkator). Für eine Instanz, die eine
Registry bedient (ADR 0009), fehlte der Weg, ein zweites hinzuzunehmen —
und das braucht der Rollout unabhängig davon, welche Repositories es am Ende
sind.

`repo-add <url> [name]` klont daneben, führt `init` darin aus und PRÜFT, ob
die Registry den Pfad schon nennt — geschrieben wird sie nicht. `~/.werkator.yml`
ist die Datei der Instanz: Port, globale Nebenläufigkeit, womöglich geteilte
Zugangsdaten. Ein Skript, das sie in place umschreibt, verändert die
Konfiguration des Betreibers hinter dessen Rücken. Klonen und Initialisieren
ist mechanisch, Registrieren ist eine Entscheidung — also druckt es den
Eintrag und sagt, dass die Registry beim Start gelesen wird.

Der Name muss ein Pfadsegment sein: Er wird das Routen-Segment /repos/<name>.
docs/deployment.md verweist im Registry-Abschnitt darauf.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 13:59:24 +02:00
mhoennigandClaude Opus 5 80a14a7467 feat(gitea): der Commit-Status verlinkt endlich die Artefaktseite — repo-bezogen
Der letzte offene Punkt der Sitzung D („Gitea status links use the repo-scoped
URLs"). Beim Nachsehen war es kein Scoping-Problem, sondern ein fehlendes
Feature: `server.publicBaseUrl` ist als „used for all links posted to Gitea"
dokumentiert, der Executor postete aber `targetUrl = null` — es gab überhaupt
keinen Link. Jetzt zeigt der Status auf die Artefaktseite des Builds, also auf
die Logs, um die es geht. Ohne `publicBaseUrl` bleibt er null: ein relativer
Link in einem Gitea-Status wäre schlimmer als keiner, er löste gegen die Forge
auf.

Die Präfix-Regel wohnt jetzt an einer Stelle (`RepoLinks`), weil drei
dasselbe damit tun: die Seiten, die API-Antworten und diese Ziel-URLs — und
eine dreimal ausgeschriebene Regel driftet. Die Slice-Tests binden die echte
Komponente per @Import ein, statt die Regel im Test nachzubauen.

Ein neuer Test (mit publicBaseUrl trägt der Status die Artefaktseite; die
vorhandenen Tests halten fest, dass ohne sie weiterhin null gepostet wird).
499 Tests grün, ktlint sauber. Plan und Architektur-Skill nachgezogen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 13:53:51 +02:00
mhoennigandClaude Opus 5 91e1dd47a3 docs(deployment): wie eine Instanz mehrere Repositories bedient
Der aus Sitzung C nach E übertragene Doku-Punkt: docs/deployment.md
beschreibt die Registry jetzt betrieblich — klonen, vorbereiten, in
~/.werkator.yml eintragen, neu starten. Dazu die Regeln, die beim Eintragen
weh tun, wenn man sie nicht kennt: Der Name ist Routen-Segment und
Umschalter-Eintrag und muss eindeutig sein (ein Doppel bricht den Start ab,
ebenso ein Eintrag, der kein git-Repository ist), eine unlesbare Konfiguration
überspringt nur ihr Repository, geteilte Zugangsdaten können einmal im
defaults-Block liegen, und die Unit behält ihren Namen — sie gehört der
Instanz, nicht einem Repository.

Dazu, was die Adressen tun: mit einem Repository bleibt jede URL, wie sie
war; mit mehreren tragen Seiten und API das Repository, und die unscoped
Pfade meinen weiterhin das bediente.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 13:41:26 +02:00
mhoennigandClaude Opus 5 c82f2a965c feat(server): Routen, Seiten und Artefakte tragen das Repository — /repos/<name>/… (Sitzung D)
Zweite Hälfte von Sitzung D (docs/plan/22-multi-repo.md, PR #13): Der Server
bediente bisher genau ein Repository der Registry. Jede Route — API, Seiten,
Artefakt-Dateien — arbeitete auf `registry.current()`; ein zweites
registriertes Repository wurde gebaut und gepollt, war aber unsichtbar und
unerreichbar.

Jeder Controller löst sein Repository jetzt je Anfrage auf, statt das
bediente als Bohne zu halten; jede Route ist zweimal gemappt. Die unscoped
Form ist kein Übergangs-Alias, sondern dauerhaft die Art zu sagen „das
bediente Repository" — Lesezeichen und die nach Gitea geposteten Links
kennen kein Segment.

Entschieden gegen die Repo-Spalte: Die Seiten bleiben je Repository, die
Navigation bekommt einen Umschalter. Die Aktionen einer Zeile brauchen das
Repository ohnehin, Branches kommen von einem origin und Artefakte aus einem
Store — und bei dem einen Repository, das die meisten Installationen haben,
wäre eine Spalte nur Rauschen.

Das Link-Präfix folgt der ZAHL der bedienten Repositories, nicht dem Weg, über
den eine Seite erreicht wurde: mit einem behält die Installation ihre
bisherigen URLs (Abnahmekriterium der Sitzung), mit mehreren benennt jeder
Link sein Repository. werkator.js liest das Präfix einmal aus einem
`werkator-repo-base`-Meta. `BranchPermalinks.permanentUrl` bekommt es
ebenfalls — der permanente Schlüssel ist ein Hash des Build-Namens allein,
zwei Repositories mit `main` teilten sich sonst eine permanente URL.

Fünf neue Tests, Gegenprobe per Mutation gezogen (Präfix fest auf leer →
genau der Mehr-Repo-Test fällt). 498 Tests grün, ktlint sauber. PR-Dokument
docs/prs/2026-09-03-PR#13-…, Plan, Architektur-Skill und AGENTS.md
nachgezogen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 13:39:09 +02:00
mhoennigandClaude Opus 5 6ca67a6a37 feat(build): every running build knows its repository — die laufenden Builds und das Worktree-Aufräumen unterscheiden Repositories
Der letzte Übertrag aus Sitzung C: `RunningBuild` trug kein Repository, der
Executor ist aber instanzweit. Zwei Folgen, beide gemessen und jetzt behoben:

- Das Worktree-Aufräumen schützte die Worktrees ALLER Repositories. Ein
  laufender Build eines anderen Repositories auf einem gleichnamigen Branch
  hielt hier einen Worktree fest, dessen Branch längst von origin weg war.
- Die Current-Builds-Ansicht und `/api/builds/current` zeigten die Builds
  aller Repositories, schlugen ihren Status aber in den Ergebnissen NUR
  dieses Repositories nach — ein fremder Build fiel auf RUNNING zurück und
  zeigte einen Zustand, den niemand aufgezeichnet hat. Dasselbe beim
  Live-Log: der Schlüssel eines fremden Builds wurde beantwortet.

`RunningBuild.repo` ist der `RepoContext` selbst, verglichen wird per
Referenz — der Kontext ist die Identität (ADR 0009), und der Executor hat ihn
in `startBuild` ohnehin zur Hand. Watcher, API und UI filtern damit auf ihr
eigenes Repository.

Drei neue Tests, Gegenprobe per Mutation gezogen: Nimmt man die drei Filter
wieder heraus, fallen genau diese drei und sonst keiner. 493 Tests grün,
ktlint sauber.

Architektur-Skill und Plan (docs/plan/22-multi-repo.md) nachgezogen — die
Aussage „RunningBuild carries no repository" stimmte nicht mehr. Was von
Sitzung D bleibt: die repo-bezogenen Routen und die Oberfläche, die weiterhin
nur `registry.current()` bedienen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 12:36:07 +02:00
mhoennigandClaude Opus 5 6fba43c627 docs(prs): das PR-Dokument der Repository-Registry trägt seine echte Nummer — PR#12
Die Datei lag als `PR#000`-Platzhalter, wie es die Konvention in
docs/prs/README.md vorsieht: Die Nummer gibt es erst, wenn der Pull Request
offen ist. Er ist es — #12. Umbenannt samt der fünf Szenario-IDs
(`Scenario#000.01` … `.05` -> `Scenario#12.01` … `.05`), wie es Schritt 21
für PR #4 vorgemacht hat; sonst zeigen die IDs auf eine Nummer, die es nicht
gibt. Kein `#000` bleibt in der Datei, keine andere Datei verweist auf den
alten Namen.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-03 12:35:53 +02:00
mhoennigandClaude Fable 5.1 58799ed0ed Step 22 C: metrics over the registry, documentation, PR-doc
The metrics page sums the registered repositories' sizes; the instance
configuration is documented in docs/configuration.md, the architecture skill,
AGENTS.md and the step 22 plan (session C ticked, fairness decided FIFO).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 09:06:13 +02:00
Michael HönnigandClaude Fable 5.1 096cce3659 step 22b: repo context (#11)
* Step 22 B: RepoContext over the current repository

A RepoContext bundles a repository's primary checkout with the state that lives
inside or is keyed by it (results, artifact store) and carries its name. Today
there is exactly one, opened over the current working directory; the result and
artifact-store beans now come from it, so nothing else changes yet.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Step 22 B: the watcher polls a RepoContext

start/poll/recoverOnStartup take the context instead of a working directory and
read results and artifacts from it; the per-repository poll memory (logged fetch
error, deprecation warning, cached branch definitions) moves into a RepoWatch
keyed by context, so the next session can iterate contexts without one
repository's outage silencing another's. The shared WatcherState is unchanged.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Step 22 B: the executor runs builds of a RepoContext

startBuild takes the context first; builds serialize per (context, branch) and
share the global maxConcurrent cap across repositories, results and artifacts go
to the build's own context. ConsoleBuildRunner, the build/retry commands and the
builds API restart pass the current repository's context along.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Step 22 B: the UI and the branch listing read their RepoContext

UiController and BuildsApiController take the current repository's context
instead of a settable working directory; BranchListing lists the branches of a
context and reads the results from it.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

* Step 22 B: document the RepoContext, PR-doc for PR #11

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 08:49:21 +02:00
mhoennig 713edf77d6 Merge pull request #10 from mhoennig/22-multi-repo
ADR 0009 and step 22 plan: one Werkator instance serves a set of repositories

# Conflicts:
#	docs/plan/22-multi-repo.md
#	docs/prs/2026-09-01-PR#6-werkdock-bootstrap.md
2026-09-02 07:22:59 +02:00
mhoennig a1de3c861f Merge pull request #9 from mhoennig/init-apply-config
Step 23 sessions A-C: init --apply, applied instance fragment, tools/remote wraps
2026-09-02 07:12:19 +02:00
mhoennig 717cd65ea1 Merge pull request #8 from mhoennig/23-init-owns-the-files
Plan step 23: init owns the files, tools/remote wraps
2026-09-02 07:12:12 +02:00
mhoennig 3af8ac20ef Merge pull request #7 from mhoennig/werkator-consumes-werkdock
BwrapBuildRunner delegates to werkdock; webspace install path replaces the self-build prototype
2026-09-02 07:12:07 +02:00
mhoennigandClaude Sonnet 5 6a5e843b32 PR-doc for PR #10: step 22 multi-repo decision (ADR 0009)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 07:11:35 +02:00
mhoennigandClaude Sonnet 5 bc358900d7 PR-doc for PR #8: step 23 plan, init owns the files
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 07:11:26 +02:00
mhoennigandClaude Sonnet 5 8279e855cb PR-doc for PR #7: BwrapBuildRunner delegates to werkdock, webspace install path
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 07:11:17 +02:00
mhoennigandClaude Sonnet 5 d45e22e47b Assign PR #6 to werkdock-extraction, renumber PR-doc and scenario IDs
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 07:11:06 +02:00
mhoennigandClaude Sonnet 5 ee6940ac27 PR-doc for PR #9: init --apply, applied instance fragment, control-token
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-02 07:10:48 +02:00
mhoennigandClaude Fable 5 1d1bd19e10 Step 23 sessions B+C: tools/remote wraps, verified live on mih34
The wrapper's config writing is gone: --env-file FILE selects the
target instance (default .env; named like docker's flag — --env means a
single variable there), the init fragment named by WERKATOR_INIT_CONFIG
is uploaded and installed remotely via 'werkator init --apply', and
instance-start places the .htaccess that init now generates. The
heredoc/sed machine-config writing is deleted; control-token delegates
to the werkator CLI; check-prerequisites uploads werkdock and runs its
doctor — tools/werkator-build-prerequisites.sh retires. The idle-check
and port-forward port lookups read the effective config via
config:print, so a port living in the applied fragment is found too.

Verified live against mih34 with the .env.mih34 + .env.mih34.yml pair
(gitignored via the new /.env.* rule): update, doctor PASS 6/6,
repo-init applying the fragment as the new layer, instance-start
placing the generated proxy and restarting the unit, control-token.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 19:00:38 +02:00
mhoennigandClaude Fable 5 5f1a669771 Step 23 session A: init --apply, the applied instance layer, control-token
'werkator init --apply FILE' installs a config-schema YAML fragment as
its own layer: validated strictly (an unknown key is refused loudly,
never ignored — a typo must not install a silent no-op), then copied
verbatim to .git/werkator/.werkator.applied.yml, above the project
config and below the hand-edited machine config, which always wins.
Deviation from the plan sketch, recorded there: a separate layer
instead of an in-place merge, because merging would re-serialize the
machine config — destroying its comments and rewriting the file that
holds the secrets; re-apply is a plain file replacement.

init --systemd now also generates werkator.htaccess beside the units
whenever a publicBaseUrl is configured — generated host integration for
the managed-webspace Apache, copied into the docroot by the wrapper.

New subcommand 'werkator control-token' prints (and lazily creates) the
token via ControlTokenService, so no wrapper needs its own generator.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 18:46:31 +02:00
mhoennigandClaude Fable 5 7a24ad1d7f Step 23 refined: init takes a YAML fragment in the config schema
Refinement decided 2026-09-01: each side gets its native format — the
wrapper keeps a bash-sourceable transport env file, Werkator takes a
YAML fragment in its own config schema via 'init --apply FILE',
deep-merged idempotently. The env-to-config mapping table disappears
entirely: the fragment IS configuration in the one schema, validated by
the existing binding, documented by the existing reference. The env
file names the fragment (WERKATOR_INIT_CONFIG), keeping one entry point
per instance.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 18:32:29 +02:00
mhoennigandClaude Fable 5 f8e8188fc6 Plan step 23: init owns the files, tools/remote wraps
Decided 2026-09-01: Werkator becomes the executing app, the remote
script a thin wrapper (build, transport, remote execution, service
switching). Parameters travel as env files instead of many options —
'remote --env .env.mih34 werkator ...' selects the target, and
'werkator --env ... init' reads the same file and writes real values
into the files it owns, idempotently; the wrapper's YAML heredocs and
sed patches (the duplicate-block class) die. control-token becomes a
werkator subcommand, check-prerequisites delegates to werkdock doctor.
Explicitly not the removed legacy env-to-YAML conversion: the env file
is setup-time input to init, never runtime configuration.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 18:26:39 +02:00
mhoennigandClaude Fable 5 2b538b668e Step 21 session D: the managed-webspace install path replaces the self-build
tools/remote reworked to role-named commands (the builder-vs-built
tangle noted 2026-09-01): instance-install/instance-update/
instance-start manage the installed Werkator + werkdock, repo-init the
watched repository. The self-build is retired — no repo clone for
building, no GitHub-key step; the instance installs from locally built
artifacts (ADR 0006), the watched repo clones anonymously via https.

instance-update refuses to swap the runtime under a running build
(FORCE=1 overrides) and keeps the previous runtime as the rollback
asset. repo-init is idempotent: checksum-skipped rootfs upload, and the
machine-config guard is fixed — its indentation mismatch (grep for two
spaces, block written with four) appended the bwrap block on every run,
the source of the nine duplicates found on mih34. The retired
install/build/start commands fail loudly naming their successors.

docs/deployment.md gains the Hostsharing Managed Webspace as the third
deployment variant, written from the live-verified setup: both new
commands ran against mih34 (full update cycle; fully skipping re-init).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 17:49:41 +02:00
mhoennigandClaude Fable 5 fc59c31963 Step 21 session notes: session C deployed to mih34
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 15:48:51 +02:00
mhoennigandClaude Fable 5 823c8adc36 Step 21 session C: BwrapBuildRunner delegates to the werkdock CLI
The runner no longer assembles raw bwrap argv: it checks image
existence via 'werkdock images', loads the rootfs archive once per
source as image werkator-buildenv-<hash> into werkdock's store (shared
across every repository of the OS user — resolving step 22's
buildenv-sharing question), and runs builds through 'werkdock run --rm'
with the git-metadata mask expressed as ordered -v/--tmpfs flags.

New pinned config key bwrap.werkdock (the executing binary, default via
PATH) — a branch must not substitute the executing binary; synced in
WerkatorConfig, BwrapOverrides, the pinned-key strip, the init
template, docs/configuration.md, and AGENTS.md.

werkdock's --clearenv means the server environment no longer leaks into
builds; the TMPDIR workaround is gone with the raw invocation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 15:46:19 +02:00
mhoennigandClaude Fable 5 ff3aa61eca 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>
2026-09-01 14:15:15 +02:00
mhoennigandClaude Fable 5 debfe57451 Step 21 session notes: sessions A and B done and live-verified
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 14:14:18 +02:00
mhoennigandClaude Fable 5 c0a353e476 Step 22: four decisions folded in
Repo defaults allowed in the home file, but only inside an explicit
defaults: block, merged below every repo's own layers (accepted cost:
secrets may live in two places). Registry wins over cwd when a home
config exists. Repo names default to the directory basename,
overridable per registry entry, duplicates abort loudly. The file name
stays .werkator.yml in all three locations — the location carries the
meaning.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 13:34:49 +02:00
mhoennigandClaude Fable 5 da1054bf90 Step 22: instance config decided as ~/.werkator.yml, open questions sharpened
Host/instance configuration (domain, port, registry, global concurrency)
lives in a .werkator.yml in the home directory of the user running the
instance — one instance per OS user, matching the platform model; repo
configuration stays in each repository's .git/werkator/. Repo-level
instance keys get ignored with a warning once a home config exists.
The open questions now name the decisions still needed: repo defaults
in the home file, cwd-vs-registry precedence, repo naming, and whether
the third .werkator.yml location should carry a distinct name.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 12:49:45 +02:00
mhoennigandClaude Fable 5 d477423263 PR-doc: step 22 moved to its own branch
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 12:48:57 +02:00
mhoennig 0c39de5cdf Revert "Plan step 22: one Werkator instance, many repositories"
This reverts commit 9c9da2b438.
2026-09-01 12:48:43 +02:00
mhoennig 3f14b8a720 Revert "PR-doc: step 22 plan rides along in this PR"
This reverts commit a8df7cc9d4.
2026-09-01 12:48:43 +02:00
mhoennigandClaude Fable 5 a8df7cc9d4 PR-doc: step 22 plan rides along in this PR
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 09:35:20 +02:00
mhoennigandClaude Fable 5 9c9da2b438 Plan step 22: one Werkator instance, many repositories
The one-instance-per-repository tenet does not scale to a second
repository on a webspace (second service, port, tunnel, UI). Step 22
plans the revision in five sessions: ADR 0009 + instance registry and
key-ownership split, a RepoContext refactor with unchanged behavior,
watcher/executor multiplexing with per-repo error isolation, repo-scoped
routes and UI with single-repo back-compat, and the mih34 rollout with
Werkbaum as the second repository. Guiding idea: the repository stays
self-contained (state in its own .git/werkator), the instance is only
an aggregator — adding a repo is a registry entry, never a migration.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 09:34:48 +02:00
mhoennigandClaude Fable 5 9724d2a1ed PR-doc for the werkdock bootstrap PR (number pending)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 09:32:51 +02:00
mhoennigandClaude Fable 5 8853006348 Step 21 session D: note the builder-vs-built role tangle in tools/remote
tools/remote mixes commands that manage the builder (the installed
Werkator instance) with commands that act on the built (the watched
repository); on the self-building instance both roles coincide in one
product, which misleads. Session D's replacement must name the role in
every command and delegate built-side operations to the werkator CLI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 09:01:54 +02:00
mhoennigandClaude Fable 5 4cdd5a1986 artifacts: archive non-report dirs at their own paths, list them on the artifact page
Every artifactDir landed below reports/ — the legacy
archived_artefact_dir_path layout — which mislabeled non-report outputs
(reports/werkdock/dist/werkdock) AND hid them: the artifact page's
report index only scans reports/ for HTML pages, so a built binary was
stored but never shown.

Now build/reports keeps archiving as reports/ (the browsable anchor and
every existing link), every other directory archives at its
workspace-relative path, and the artifact page gains a plain-files list
for everything outside reports/ (log files stay in their own section;
capped at 200 entries). Existing stored artifacts keep their old layout
and remain served; only new builds use the new one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 08:51:09 +02:00
mhoennigandClaude Fable 5 771ed711da Step 21 session notes: fat build image live on mih34
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 07:43:55 +02:00
mhoennigandClaude Fable 5 67e5b120e7 Werkdock skeleton: doctor, load, run over the bwrap engine (Go)
The minimal build-capable CLI decided in RFC 0002's outcome: stdlib-only
Go module, one static binary.

- engine: RunSpec behind the Engine interface (RFC 0001); the Bwrap
  engine ports Werkator's hardened invocation — uid-0 mapping, read-only
  rootfs at /, proc/dev/tmp/root before the user binds so binds below
  them land inside, mountpoint pre-creation in the rootfs including file
  mountpoints, and a guard against binds escaping the rootfs. --clearenv
  gives docker-style clean environments (HOME/PATH set explicitly).
- store: images under $WERKDOCK_HOME (default ~/.werkdock), load
  unpacks via the tar CLI into a tmp dir and renames atomically.
- cli: docker-shaped run flags (-v/-e/-w/--rm); refused docker flags
  (-p, --network, --memory, --cpus, --user, -d) fail loudly with the
  reason; exit codes follow docker (125 CLI errors, child code through).
- doctor: port of werkator-build-prerequisites.sh — userns probe with
  the three signals, tar/zstd, free space and group-quota headroom via
  testable df/quota parsers, same PASS/FAIL output.
- tests: argv golden test, mountpoint and escape tests, flag refusals,
  store round trip, doctor parsers — plus real-sandbox integration
  tests that skip where bwrap or userns are unavailable.

Also records in step 21: RFC 0002 levels 2/3 deferred; next goal is
sandbox builds of Werkator, Werkbaum, and Werkdock itself.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 07:01:21 +02:00