Commit Graph
192 Commits
Author SHA1 Message Date
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
mhoennigandClaude Fable 5.1 b66d26a03a Step 22 C: --repo selects a registered repository in build, retry, and status
The RepoOption mixin resolves the name through the registry; without it a
command means the current directory when served, else the first entry. An
unknown name is a usage error naming the registered repositories.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 09:03:07 +02:00
mhoennigandClaude Fable 5.1 a5027acfaf Step 22 C: the watcher multiplexes the registry
start takes the served repositories; one poll cycle polls each in its own guard
and aggregates the reports into WatcherState, which gains a per-repository list.
With one repository the top-level fields read exactly as before; with several,
every message is prefixed by the repository name. Log lines carry the name too.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 09:00:52 +02:00
mhoennigandClaude Fable 5.1 b16c9c2e27 Step 22 C: the repository registry
RepoRegistry opens one RepoContext per entry of the home configuration, or the
current directory without one. An entry that is no git repository or a name
used twice aborts the start naming the home file; a repository whose config
must not be read is skipped with an error and the others are served. The
current-repo bean now comes from the registry (the cwd when served, else the
first entry), and the pre-rename state-dir migration runs per opened repository.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 08:58:04 +02:00
mhoennigandClaude Fable 5.1 ab2a4133e8 Step 22 C: the instance configuration in ~/.werkator.yml
ConfigLoader reads the home file (WERKATOR_HOME overrides the location): its
defaults block merges below every repository's layers, and once it exists the
server section, executor and watcher.pollInterval come from it alone — a
repository file still carrying them is told so once, naming both files.
InstanceConfig binds the registry entries; nothing reads them yet.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-02 08:55:59 +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
mhoennig ce4b44b2fc Merge pull request #6 from mhoennig/werkdock-extraction
Werkdock bootstrap: a docker-shaped bwrap sandbox CLI, grown in werkdock/
2026-09-02 07:12:01 +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 f2d685932e werkdock store: reserve the .tmp suffix, List skips staging leftovers
The image-name pattern allows dots, so a directory like broken.tmp —
an interrupted load's staging dir — counted as an image. List now skips
.tmp names and Load refuses them, closing the ambiguity the previous
commit's (accidentally pushed red) test exposed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 15:40:00 +02:00
mhoennigandClaude Fable 5 750e9afa33 werkdock: ordered mounts with --tmpfs, images verb, :rw accepted
Session C groundwork: Werkator's git-metadata mask needs a tmpfs
BETWEEN binds (ro-bind .git, tmpfs .git/werkator, bind workspace), so
-v and --tmpfs now collect into one ordered mount list and RunSpec
carries Mounts instead of Binds. --tmpfs DEST is the docker flag of the
same name. `werkdock images` lists loaded image names one per line, so
a consumer can check existence through the CLI. -v accepts the explicit
:rw docker default instead of refusing it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 15:39:31 +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 a30a16c235 rootfs build: trim what a build environment never reads
openjdk-21-jdk-headless instead of the full JDK (drops the X11 and
fontconfig library stack; headless AWT still works for tests),
message catalogs except en/de removed, man pages, package docs, apt
lists and cache cleared after install.

Measured on the JDK+Go+Node image: 516 MB -> 351 MB compressed —
smaller than the original JDK-only archive (375 MB).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 09:02:45 +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 db06d805ec bwrap: reset TMPDIR/TMP to /tmp inside the sandbox
bwrap inherits the server's environment; on hosts with pam_tmpdir that
includes TMPDIR=/tmp/user/<uid>, which does not exist on the sandbox's
fresh tmpfs /tmp. Every tool honoring TMPDIR then fails — seen live on
mih34 as go's 'creating work dir: stat /tmp/user/120957: no such file
or directory'. The JVM ignores TMPDIR, so Gradle builds never noticed.
Explicit environment and bwrap.env entries can still override.

(Werkdock's own engine is immune by design: it runs --clearenv.)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 08:09:03 +02:00
mhoennigandClaude Fable 5 598e8d335f rootfs build: anchor the tar excludes to the archive root
--exclude=sys matched every path component named sys anywhere in the
tree — GNU tar applies slash-less patterns to all components — and so
silently dropped usr/share/go-1.24/src/internal/runtime/sys from the
archive, breaking every go build in the sandbox with 'package
internal/runtime/sys is not in std'. The excludes are now anchored
(./proc, ./sys, ./dev), so only the top-level mountpoints stay out.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 08:02:08 +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 431e64bfb9 Werkdock RFC 0003 candidate: composable toolchain mounts
Run-time composition instead of baked-in toolchain combinations: a slim
base image plus per-toolchain prefix binds (--with jdk-21 --with go-1.24),
possible because official toolchain tarballs live under their own
prefixes — plain ro-binds, no overlayfs, no root, today's bwrap.
Comes due when a second toolchain combination is needed; until then the
one fat image stays the deliberate choice.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 07:28:56 +02:00
mhoennigandClaude Fable 5 afcacd8d71 Werkdock README: record the disk-footprint contract
Flat images without layers (~2 GiB unpacked for a fat JDK+Go+Node
buildenv), near-free instances (read-only rootfs bind + tmpfs), the
one-fat-image recommendation, the orphaned-environment trap on archive
path changes, and the future overlay/hardlink-dedup options.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 07:26:07 +02:00
mhoennigandClaude Fable 5 4bbd3483d5 Werkdock builds itself: werkdock build definition
A second named build beside the default: gofmt gate, go vet, go test,
and the static binary as the artifact (werkdock/dist). Verified locally
end to end and via config:print.

Expected red on the webspace instance until the trixie rootfs learns
the go toolchain — that image extension (Go + Node, roadmap step 2 of
plan 21) is the follow-up.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 07:09:20 +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
mhoennigandClaude Fable 5 4fba73a1d0 Werkdock RFC 0002: docker-compatible surface in three levels
Level 1: docker-compatible CLI (verbs, flags, loud refusal of isolation
flags the filesystem-only contract cannot honor) — built in session B.
Level 2: OCI image pull, flattened to a rootfs — deferred, stdlib-doable.
Level 3: a daemon speaking the Docker Engine API subset Testcontainers
actually uses (Testcontainers never calls the CLI) — deferred, but the
CLI is built as a thin frontend over the same internal service from the
start. Records the port-mapping crux of host networking and the
unprivileged-netns escape hatch as a future RFC.

Step 21 session B and the werkdock README follow the docker-shaped
semantics: run takes an image, instances correspond to containers.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 06:44:35 +02:00
mhoennigandClaude Fable 5 c1ff830a3c Werkdock RFC 0001 accepted: Go it is
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-09-01 06:42:13 +02:00
mhoennigandClaude Fable 5 7b4f34e349 Werkdock RFC 0001: implementation language evaluation, proposing Go
The full five-way evaluation (bash, Python 3, Kotlin Native, Rust, Go)
with the numeric scoring, the Kotlin Native deep-dive, and the
own-namespaces-instead-of-bwrap analysis, ending in a concrete proposal:
Go, stdlib-only, one static binary, sandbox engine behind an interface
so bwrap can later be swapped for native namespaces.

Status proposed — the decision outcome is recorded once made.

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