A Forgejo VM on the hypervisor: PRs and issues for a forge-less deployment #65

Closed
opened 2026-10-06 20:10:58 +01:00 by vnprc-agent · 6 comments
Contributor

A forge-less deployment, which the public install path now produces in one run, has no pull requests and no issues: the hypervisor pushes its clones into the dev VM and pulls the agent's commits back with the relay. That is sovereign and it is thin. The next step a new operator wants is a forge of their own without renting a VPS: a Forgejo VM on the same hypervisor, the dev VM cloning from it, an agent account that can open PRs and issues there, branch protection that refuses a push to master. Later, when they want it reachable from outside, a dump of that VM restores onto a VPS and one host in identity changes.

Proposal, in four pieces, each its own issue on the owning repo:

  1. A public Forgejo NixOS module: Forgejo, split ssh daemons (clone port, admin port), reverse proxy, fail2ban, dump unit, restore tool, with a fresh-install mode beside the existing restore-gated one. Today's module lives in a private deployment repo and assumes a public domain with ACME and a restored dump; the public one takes plain http on a private network as a first-class case.
  2. A forge machine in the public templates: a privacy-archetype VM with a data disk, the module composed in fresh mode, its own host key and identity entry, on the libvirt network.
  3. forge-bootstrap on the hypervisor: admin and agent accounts, the dev VM's forge key registered, the org and the mirrored repositories pushed from the hypervisor's clones, the agent token minted and landed through the managed credential path, branch protection applied from the git policy data the secrets template already ships.
  4. The dev VM flips from host-provided to cloning from the local forge: identity's forge host, port and user point at the VM, forgeAccess = true, the registry's remotes name the local org, getting-started.sh offers it as a second run.

Proof before any laptop touches it: a NixOS VM test with a forge node and a dev node where the bootstrap runs, the dev node clones, pushes a branch, opens a pull request through the forge CLI, and is refused on master. Then the public install walk's laptop, as a second run of the script.

Promotion to a VPS is the existing dump and restore and is out of scope here beyond keeping it possible.

A forge-less deployment, which the public install path now produces in one run, has no pull requests and no issues: the hypervisor pushes its clones into the dev VM and pulls the agent's commits back with the relay. That is sovereign and it is thin. The next step a new operator wants is a forge of their own without renting a VPS: a Forgejo VM on the same hypervisor, the dev VM cloning from it, an agent account that can open PRs and issues there, branch protection that refuses a push to master. Later, when they want it reachable from outside, a dump of that VM restores onto a VPS and one host in identity changes. Proposal, in four pieces, each its own issue on the owning repo: 1. A public Forgejo NixOS module: Forgejo, split ssh daemons (clone port, admin port), reverse proxy, fail2ban, dump unit, restore tool, with a fresh-install mode beside the existing restore-gated one. Today's module lives in a private deployment repo and assumes a public domain with ACME and a restored dump; the public one takes plain http on a private network as a first-class case. 2. A `forge` machine in the public templates: a privacy-archetype VM with a data disk, the module composed in fresh mode, its own host key and identity entry, on the libvirt network. 3. `forge-bootstrap` on the hypervisor: admin and agent accounts, the dev VM's forge key registered, the org and the mirrored repositories pushed from the hypervisor's clones, the agent token minted and landed through the managed credential path, branch protection applied from the git policy data the secrets template already ships. 4. The dev VM flips from host-provided to cloning from the local forge: identity's forge host, port and user point at the VM, `forgeAccess = true`, the registry's remotes name the local org, `getting-started.sh` offers it as a second run. Proof before any laptop touches it: a NixOS VM test with a forge node and a dev node where the bootstrap runs, the dev node clones, pushes a branch, opens a pull request through the `forge` CLI, and is refused on master. Then the public install walk's laptop, as a second run of the script. Promotion to a VPS is the existing dump and restore and is out of scope here beyond keeping it possible.
Author
Contributor

Slice 2, the service guest type, is built and green in all three repos and is awaiting relay. Slice 3's issues are filed.

Slice 1 — the public Forgejo module:

Slice 2 — the service guest type:

Slice 3 — the forge machine in the templates. allod/archetypes#138 lands first; the other three are independent of each other:

Two findings from building slice 2 that changed slice 3's shape:

allod/profiles declares only nixpkgs as an input, and an archetypes input would be a cycle, so a profile definition had no way to compose a module this framework exports. That is allod/archetypes#138, and it blocks allod/profiles#12.

getting-started.sh regenerates a host key for each key of the secrets template's machine-host-keys.json, so a new machine's host key is generated on a fresh install by virtue of appearing in that file. Slice 3 therefore needs no change to that script, and the dev VM's forge key pair — which does need one — moves to the bootstrap slice, where its recipients are actually required.

Slices 4 and 5 are unchanged: forge-bootstrap on the hypervisor, then the dev VM's flip.

Slice 2, the `service` guest type, is built and green in all three repos and is awaiting relay. Slice 3's issues are filed. Slice 1 — the public Forgejo module: - [x] allod/archetypes#136 Slice 2 — the `service` guest type: - [ ] allod/inventory#24 - [ ] allod/secrets#41 - [ ] allod/archetypes#137 Slice 3 — the `forge` machine in the templates. allod/archetypes#138 lands first; the other three are independent of each other: - [ ] allod/archetypes#138 - [ ] allod/inventory#25 - [ ] allod/profiles#12 - [ ] allod/secrets#42 Two findings from building slice 2 that changed slice 3's shape: `allod/profiles` declares only `nixpkgs` as an input, and an `archetypes` input would be a cycle, so a profile definition had no way to compose a module this framework exports. That is allod/archetypes#138, and it blocks allod/profiles#12. `getting-started.sh` regenerates a host key for each key of the secrets template's `machine-host-keys.json`, so a new machine's host key is generated on a fresh install by virtue of appearing in that file. Slice 3 therefore needs no change to that script, and the dev VM's forge key pair — which does need one — moves to the bootstrap slice, where its recipients are actually required. Slices 4 and 5 are unchanged: `forge-bootstrap` on the hypervisor, then the dev VM's flip.
Author
Contributor

Slices 1 through 4 are merged on the public templates; the primary deployment's forge VM is provisioned and serving. The remaining work is slice 5, the dev VM's flip, and slice 6, allod forge up.

Slice 1 — the public Forgejo module:

Slice 2 — the service guest type:

Slice 3 — the forge machine in the templates:

Slice 4 — forge-sync on the hypervisor and its witness:

Slice 5 — the dev VM flips to the local forge: not yet filed.

Slice 6 — operator ergonomics:

Two things changed shape while building slice 4. forge-bootstrap became forge-sync, because it reconciles the forge to the declared state on every run rather than acting once. And the witness in allod/archetypes#140 consumed forge-sync before archetypes had re-pinned allod/nexus, so master was red with forge-sync: command not found between the two landings; the pin is now on master. A check that consumes a sibling repository's new code wants the lock bump in the same commit.

Reaching the forge by name instead of address is deferred past slice 6: it is a coupled change across the profile's domain, Caddy's site address and slice 5's clone URL.

Slices 1 through 4 are merged on the public templates; the primary deployment's forge VM is provisioned and serving. The remaining work is slice 5, the dev VM's flip, and slice 6, `allod forge up`. Slice 1 — the public Forgejo module: - [x] allod/archetypes#136 Slice 2 — the `service` guest type: - [x] allod/inventory#24 - [x] allod/secrets#41 - [x] allod/archetypes#137 Slice 3 — the `forge` machine in the templates: - [x] allod/archetypes#138 - [x] allod/inventory#25 - [x] allod/profiles#12 - [x] allod/secrets#42 Slice 4 — `forge-sync` on the hypervisor and its witness: - [x] allod/nexus#84 - [x] allod/archetypes#140 Slice 5 — the dev VM flips to the local forge: not yet filed. Slice 6 — operator ergonomics: - [ ] allod/nexus#85 Two things changed shape while building slice 4. `forge-bootstrap` became `forge-sync`, because it reconciles the forge to the declared state on every run rather than acting once. And the witness in allod/archetypes#140 consumed `forge-sync` before archetypes had re-pinned `allod/nexus`, so master was red with `forge-sync: command not found` between the two landings; the pin is now on master. A check that consumes a sibling repository's new code wants the lock bump in the same commit. Reaching the forge by name instead of address is deferred past slice 6: it is a coupled change across the profile's `domain`, Caddy's site address and slice 5's clone URL.
Author
Contributor

Positioning, settled 2026-10-09: the forge VM is a starting place. A new operator tries the whole stack on one machine, with pull requests and issues on a forge they own, without committing to a hosted forge; when they outgrow it, the upgrade path is the existing dump and restore onto a hosted forge, or GitHub. A deployment that already has a hosted forge does not run forge-sync against a local VM; the command assumes one forge and one agent account per deployment, and that assumption stays. Slices 5 and 6 are unchanged.

Positioning, settled 2026-10-09: the forge VM is a starting place. A new operator tries the whole stack on one machine, with pull requests and issues on a forge they own, without committing to a hosted forge; when they outgrow it, the upgrade path is the existing dump and restore onto a hosted forge, or GitHub. A deployment that already has a hosted forge does not run `forge-sync` against a local VM; the command assumes one forge and one agent account per deployment, and that assumption stays. Slices 5 and 6 are unchanged.
Author
Contributor

Slice 5 is landed: allod/secrets#44 (identity's forgeUrl, defaulted), allod/archetypes#142 (dev VMs render FORGE_URL from it) and allod/deploy#11 (getting-started.sh --forge, which also settles allod/deploy#9). The remaining witness is the laptop as the second run of the script: provision the forge VM, forge-sync forge, --forge, provision-vm --replace allod-dev, then an issue created from inside the VM and a push to master refused. Slice 6 (allod/nexus#85) is the last open piece.

Slice 5 is landed: allod/secrets#44 (identity's `forgeUrl`, defaulted), allod/archetypes#142 (dev VMs render `FORGE_URL` from it) and allod/deploy#11 (`getting-started.sh --forge`, which also settles allod/deploy#9). The remaining witness is the laptop as the second run of the script: provision the forge VM, `forge-sync forge`, `--forge`, `provision-vm --replace allod-dev`, then an issue created from inside the VM and a push to master refused. Slice 6 (allod/nexus#85) is the last open piece.
Author
Contributor

Slice 5's laptop witness, run as three clean installs from the public template on 2026-10-09, found seven gaps, every one fixed on its master the same day: allod/deploy#12, allod/nexus#86, allod/nexus#87, allod/nexus#88, allod/nexus#89, allod/nexus#90 and allod/profiles#13. Two designs are filed from it: forge-sync pushes as the agent, which its own branch protection refuses from the second run on (allod/nexus#91), and a one-line key hand-off from the hypervisor to a dev VM (allod/nexus#92). Still to run on the laptop: the --forge flip, the VM replace, and the in-VM witnesses. Slice 6 (allod/nexus#85) remains unstarted.

Slice 5's laptop witness, run as three clean installs from the public template on 2026-10-09, found seven gaps, every one fixed on its master the same day: allod/deploy#12, allod/nexus#86, allod/nexus#87, allod/nexus#88, allod/nexus#89, allod/nexus#90 and allod/profiles#13. Two designs are filed from it: forge-sync pushes as the agent, which its own branch protection refuses from the second run on (allod/nexus#91), and a one-line key hand-off from the hypervisor to a dev VM (allod/nexus#92). Still to run on the laptop: the `--forge` flip, the VM replace, and the in-VM witnesses. Slice 6 (allod/nexus#85) remains unstarted.
Owner

complete except allod/nexus#85 which can be tracked on its own

complete except allod/nexus#85 which can be tracked on its own
vnprc closed this issue 2026-10-22 01:06:07 +01:00
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
allod/strategy#65
No description provided.