Commit graph

322 commits

Author SHA1 Message Date
903d8371b3 feat: Forgejo-native CI — a ci-box tenant and a forgejo-runner family
Some checks failed
ci / check (pull_request) Has been cancelled
ci / install (pull_request) Has been cancelled
ci / db-integration (pull_request) Has been cancelled
labels / labels (pull_request) Has been cancelled
rig's CI story was GitHub-shaped end to end. This makes it work against a
self-hosted Forgejo, in three pieces.

The registry fetch becomes forge-aware. templates_resolve hardcoded three
github.com archive URLs; RIG_TEMPLATES_HOST now selects the grammar, because
the forges genuinely differ — GitHub serves refs/tags, refs/heads and bare
paths, Forgejo serves exactly one, and emitting the other two there would mean
two guaranteed 404s per fetch and a failure message listing URLs that never
could have worked. Measured against forgejo.heavyduty.builders, not inferred.
The default stays GitHub, so every existing caller is unchanged. install.sh's
snapshot reads the same variable through a byte-identical copy of the builder,
diffed by the tests: a snapshot cached from a forge converge would never fetch
from is worse than no snapshot, and the pin-in-the-name staleness guard cannot
catch a wrong-ORIGIN snapshot, only an old one.

ci-box is a tenant, not a machine role. The topology is a fleet machine
hosting boxes, one of which runs CI — a '-box' guest by rig's own family rule.
That also deletes the docker-in-docker layer the usual setup needs:
bootstrap-tenant.sh already installs Docker and adds the tenant user to the
group, and the isolation a privileged dind sidecar buys is already paid for by
a box that is network-isolated, inbound-less and disposable. rig runner
install refuses Docker for good reason — it converges a MACHINE, where the
blast radius is the machine. Here it is a guest that gets thrown away.

rig forgejo-runner is a new family beside rig runner, which is untouched.
Forgejo registers against an INSTANCE and the token carries the scope, so
there is no --repo to converge toward and nothing to compare; folding that
into one command would make every guard bimodal to share a flag name while the
contract underneath differs. assert_runner_instance asks the same
trust-boundary question about the axis Forgejo actually has. There is no
repoint and no --local, and both absences are explained where an operator
arriving from the GitHub sibling will hit them.

Forgejo's .runner holds the runner's own long-lived token, unlike GitHub's, so
it is installed 0600 and the mode is re-asserted on every converge — a mode
that drifted leaks the secret silently, since nothing fails and the runner
keeps working. status reports it and never prints the token.

Both downloads verify the published .sha256 before installing: this binary
lands as root and is executed by a systemd unit.

bootstrap --undo learns the guard for the same hazard on the other forge, and
it matters more here — Forgejo has no deregistration endpoint, so the ghost it
would strand has to be deleted by hand.

Known prerequisite, documented rather than assumed: the fetch is
unauthenticated by contract, and a Forgejo with REQUIRE_SIGNIN_VIEW=true
answers 404 for repos it reports as public. Hosting a registry there needs
FORGEJO__service__REQUIRE_SIGNIN_VIEW=false. The refusal names that case,
because it is indistinguishable from a wrong ref.

forgejo#109

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 20:40:54 +00:00
Daniel Marin
93608e8d78
Merge pull request #163 from claude-bot-andresmgsl/build/162-tenant-cron
Some checks failed
ci / check (push) Has been cancelled
ci / install (push) Has been cancelled
ci / db-integration (push) Has been cancelled
release / release (push) Has been cancelled
fix: arm cron on agent tenant boxes
2026-07-25 20:30:17 +01:00
claude-bot-andresmgsl
559a5b23ba test: drive cron converge against stubbed systemctl
The service converge+assert block becomes converge_cron(), behavior-
identical, so test/cli.sh can lift it verbatim (the drop_incus precedent)
and drive it: the already-enabled/active re-run is a no-op, a disabled
stopped unit converges, and an unrecoverably masked or start-dead unit
dies nonzero naming cron and #162 — proven by execution, not only by
structural pins. The converge logs now state the probe fact instead of
claiming an unverified success (panel nit).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 19:11:13 +00:00
claude-bot-andresmgsl
29c4a0840b fix: arm cron on agent tenant boxes
Agent tenants exist to run the cron-driven duty engine, and the engine's
installer runs unprivileged — it can detect a missing cron but never
apt-get it. Install cron with the shared toolbelt, then assert the
effective state, not the package: crontab on PATH AND cron.service
enabled and active, converging (unmask/enable/start) best-effort first.
A masked daemon with the binary present is exactly the silent-inert box
the issue reports, so the service asserts are the authority and either
failing dies naming cron.

staging-box stays exempt with the rest of the agent-assert block: no
agent, no duty engine. Machine roles (bootstrap.sh) are deliberately
not widened — the issue names tenants as the demonstrated gap.

Closes #162

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 18:54:27 +00:00
Daniel Marin
34ff1c8917
Merge pull request #161 from claude-bot-andresmgsl/build/160-self-scoped-tags
fix: scope the netmap tag read to Self, not the whole document
2026-07-25 17:16:07 +01:00
Daniel Marin
4703c2b570
Merge pull request #159 from codex-bot-andresmgsl/build/153-registry-snapshot
feat: install the pinned template registry snapshot
2026-07-25 17:15:43 +01:00
claude-bot-andresmgsl
56478d7a7d fix: scope the netmap tag read to Self
json_string_array took the first "Tags" array anywhere in the document,
justified by Self-before-Peer field order. An untagged Self omits the key
entirely (Go omitempty), so the match fell through into Peer and returned
a peer's tag — false-refusing every login join and false-verifying
untagged authkey joins on any tailnet with a tagged node. Extract Self by
brace counting (Location nests an object, so slicing to the next key
would truncate) and read the array inside it.

Refs #160

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-25 16:02:54 +00:00
codex-bot-andresmgsl
dfd2debff1 fix: group template snapshot changelog entry 2026-07-25 16:00:29 +00:00
codex-bot-andresmgsl
b849eb57d3 test: prove drill records registry source 2026-07-25 13:41:01 +00:00
codex-bot-andresmgsl
247d9b9ec5 feat: install and record registry snapshot 2026-07-25 13:38:54 +00:00
codex-bot-andresmgsl
5794a80d0e feat: prefer installed template snapshot 2026-07-25 13:34:58 +00:00
Daniel Marin
2af3b24fd8
Merge pull request #156 from codex-bot-andresmgsl/build/152-machine-role-template
feat: add registry-backed machine roles
2026-07-25 14:31:40 +01:00
codex-bot-andresmgsl
c08bcc6622 fix: align machine role boundaries 2026-07-25 13:07:53 +00:00
codex-bot-andresmgsl
86bf52d4a5 fix: group machine role changelog entry 2026-07-25 13:07:01 +00:00
codex-bot-andresmgsl
597e1105f4 fix: format changelog fragment as entry 2026-07-25 11:02:27 +00:00
codex-bot-andresmgsl
88e59b6ec8 test: cover machine template registry 2026-07-25 11:00:21 +00:00
codex-bot-andresmgsl
587a44617b feat: add registry-backed machine roles 2026-07-25 10:57:52 +00:00
github-actions[bot]
535caea94b chore: bump main to 0.3.2-dev — a dev install must not impersonate 0.3.1 2026-07-25 10:32:29 +00:00
Daniel Marin
c9df5f35db
Merge pull request #145 from codex-bot-andresmgsl/build/144-release-0-3-1
Some checks failed
release / release (push) Has been cancelled
chore: cut the 0.3.1 release
2026-07-25 11:32:17 +01:00
codex-bot-andresmgsl
c0aa5ce716 chore: refresh 0.3.1 release notes 2026-07-25 09:29:49 +00:00
codex-bot-andresmgsl
f885f4da27 chore: record 0.3.1 drill waiver 2026-07-25 09:28:22 +00:00
codex-bot-andresmgsl
bb54166bb1 chore: stamp 0.3.1 release candidate 2026-07-25 09:28:22 +00:00
Daniel Marin
5294b53f9f
Merge pull request #150 from claude-bot-andresmgsl/build/149-readme-quickstart-channels
docs: quick-start fence names the development channel and carries the release command below it
2026-07-25 02:14:13 +01:00
claude-bot-andresmgsl
897779b925 docs: quick-start fence names the development channel and carries the release command below it
The first README fence becomes two labelled, copyable commands — the
RIG_REF=main development command first (byte-identical to before, so the
#89 guard at test/cli.sh:514 still passes) and the bare latest-release
command below it, each preceded by a comment line naming its channel.

Refs #149

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 23:57:16 +00:00
Daniel Marin
e250f7f7da
Merge pull request #146 from claude-bot-andresmgsl/build/110-rig-templates
feat: role templates move to heavy-duty/rig-templates — pinned by default, overridable per mint
2026-07-25 00:37:40 +01:00
claude-bot-andresmgsl
af81f8f4cf docs: README points the tenant table at the registry; the drill-record doctrine and the changelog fragment (#110)
The security-trade paragraph lands verbatim in the README (its twin is in
rig-templates' README, per the issue's docs task), with the 2026-07-24
ruling recorded in place.
2026-07-24 23:14:40 +00:00
claude-bot-andresmgsl
e7f31046d9 chore: RIG_TEMPLATES_PIN names the seeded registry (rig-templates#1's head) (#110) 2026-07-24 23:13:17 +00:00
claude-bot-andresmgsl
79363c2631 test: the tenant surface re-proven against fixture registries — offline, by refusal and by identity (#110)
The suite drives the whole new surface without a network: the family-suffix
dispatch and hard cut, the marker guards firing with the registry
unreachable, the unknown-role refusal listing the resolved source, every
malformed-definition refusal named by key, DIR-vs-REF identity through a
stubbed curl (tags-first precedence pinned), the renderer's box#80 guard,
the one-line pin, and template-lint's whole gate.
2026-07-24 23:08:47 +00:00
claude-bot-andresmgsl
11b02eb070 feat(drill): the record cites the rig-templates SHA the converge read (#110)
Override via RIG_TEMPLATES_REF when the drill was pointed somewhere; else
the pin read from the INSTALLED candidate tree — what actually landed,
never this checkout's copy.
2026-07-24 23:02:12 +00:00
claude-bot-andresmgsl
b99d08ea3d feat(bootstrap): the tenant mechanism converges from fetched definitions — the agent case arms are cut (#110)
bootstrap-tenant.sh is one mechanism parameterized by a registry definition
(template.env parsed against the allowlist, install.sh run as root with the
tenant in its environment, creds.md spliced into the shared context
skeleton). staging-box stays in-tree — mechanism-adjacent, no agent. The
dispatch in bootstrap.sh moves to the '-box' family-suffix rule so a
template added to the registry is mintable with zero code changes here; an
unknown role's refusal lists what the resolved source actually contains,
naming the source. lib/tenant-config.sh — the per-tenant case-arm table —
is deleted: the hard cut, #76's discipline.
2026-07-24 23:00:41 +00:00
claude-bot-andresmgsl
c9c8ad9ba9 feat(templates): rig template-lint — the registry repo's CI gate, dispatched from bin/rig (#110) 2026-07-24 22:54:34 +00:00
claude-bot-andresmgsl
29563a1246 feat(templates): the registry lib — three-knob resolution, the allowlist parser, the definition lint (#110)
The role-template registry moves out of rig's tree (heavy-duty/rig-templates,
ruled 2026-07-24: pinned to the rig version by default, overridable per mint).
This is the schema-and-resolution half rig keeps: RIG_TEMPLATES_DIR >
RIG_TEMPLATES_REF > the in-tree RIG_TEMPLATES_PIN (the BOX_RELEASE
discipline), template.env parsed against an allowlist and never sourced,
every refusal naming the failing key, and the lint the registry repo's CI
will run on every definition.

The pin currently names the registry's pre-seed head; it bumps to the seeded
tree in this PR's course (the seed PR is the other half of the build task).
2026-07-24 22:53:48 +00:00
Daniel Marin
5e70c0bb66
Merge pull request #137 from codex-bot-andresmgsl/build/136-adopt-changelog-fragments
feat: adopt changelog fragments
2026-07-24 20:05:32 +01:00
codex-bot-andresmgsl
10975d9ac8 fix: complete ceremony 0.3.0 labels adoption 2026-07-24 18:37:44 +00:00
codex-bot-andresmgsl
89a7fcc5b6 chore: update ceremony pin to 0.3.0 2026-07-24 18:05:09 +00:00
codex-bot-andresmgsl
728b97df8a feat: convert unreleased changelog to fragments 2026-07-24 13:55:28 +00:00
codex-bot-andresmgsl
e22e0dd4a8 chore: bump ceremony pin to 0.2.0 2026-07-24 13:54:11 +00:00
Daniel Marin
20438f09e8
Merge pull request #127 from codex-bot-andresmgsl/build/63-bootstrap-undo
feat: add guarded bootstrap undo
2026-07-24 11:41:28 +01:00
codex-bot-andresmgsl
b6d580ea3e test: make bootstrap provenance count assertive 2026-07-24 10:17:22 +00:00
codex-bot-andresmgsl
53ff0daeff test: isolate bootstrap undo fixtures 2026-07-24 09:41:48 +00:00
codex-bot-andresmgsl
c0272cbae6 test: drive bootstrap undo safety gates 2026-07-24 09:41:48 +00:00
codex-bot-andresmgsl
8b4a55a712 feat: add guarded bootstrap undo 2026-07-24 09:41:48 +00:00
Daniel Marin
9cc7568ca5
Merge pull request #128 from claude-bot-andresmgsl/build/106-install-lifecycle
feat: CI drills the install lifecycle — four beats against a real tree
2026-07-24 10:35:48 +01:00
claude-bot-andresmgsl
5a8badd532 feat: CI drills the install lifecycle — four beats against a real tree (#106)
install from this checkout via RIG_INSTALL_SOURCE, assert what landed,
a converging re-run proven by an empty diff, uninstall --all ending in
the absence assert (! -e AND ! -L). test/install-lifecycle.sh carries
the beats plus the instrument-honesty checks (the diff and the absence
assert are broken against scratch trees on every run, so 'demonstrated,
not assumed' is mechanical); test/release.sh gains the local channel's
contract — dir, tarball, loud refusal, never a silent download fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 08:58:53 +00:00
Daniel Marin
51281cdd18
Merge pull request #126 from codex-bot-andresmgsl/build/119-scope-labels
feat: add missing scope labels
2026-07-24 09:57:03 +01:00
codex-bot-andresmgsl
4579e65de9 feat: add missing scope labels 2026-07-24 08:26:57 +00:00
Daniel Marin
243deb1a4c
Merge pull request #129 from claude-bot-andresmgsl/build/95-machine-id
feat(platform): stable machine ID — derived from /etc/machine-id, stored nowhere
2026-07-24 09:12:56 +01:00
Daniel Marin
7f19eed3d5
Merge pull request #134 from claude-bot-andresmgsl/build/133-drill-defaults-prose
docs(drill): the defaults are sane now, and that is exactly the danger
2026-07-24 09:12:13 +01:00
claude-bot-andresmgsl
506b7506a5 docs(platform): ID beside HOSTNAME — the slot vs the machine, the derivation, and the cloned-image caveat (#95)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 07:48:57 +00:00
claude-bot-andresmgsl
bd2918d0c5 test(platform): the identity contract, pinned — derivation, determinism, shape, confidentiality, loud degradation (#95)
Both fixture digests are computed outside the implementation, so a refactor
that changes the prefix, the hash or the slicing renames the whole fleet and
fails here. The negative half is the spec's: empty and 'uninitialized'
machine-ids must never be hashed (the collision id is asserted absent, not
just the unavailable line present), and the raw machine-id must never
appear in the output.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-24 07:48:45 +00:00