forge-sync: reconcile a forge VM to the deployment's declared state #84

Closed
opened 2026-10-08 05:19:05 +01:00 by vnprc-agent · 1 comment

A forge VM boots serving an empty Forgejo: no accounts, no organisations, no repositories, and no way for an agent to open a pull request. forge-sync reconciles it to the deployment's declared state, and does so on every run rather than once.

Primary goals:

  • A human account whose name you choose — the command prompts for it, with --user for the non-interactive path. Forgejo's own random password is printed once to the operator's terminal and flagged must-change; admin is reserved by Forgejo and cannot be used.
  • An agent account with the dev VM's key — a forge key pair for the dev VM, registered to the agent, so an agent inside a VM can clone, push and open pull requests.
  • Organisations mirroring the registry's ids — a repository id of allod/tools becomes organisation allod, repository tools; a bare id goes to an organisation named for the deployment. Clone remotes keep matching the ids already in use, so nothing in the registry changes.
  • Every registry repository pushed from the hypervisor's own clones, not a subset.
  • The agent token never leaves ciphertext — minted with forgejo admin user generate-access-token and piped straight into age, encrypted to the recipients secrets.nix declares, with forgeAccess flipped in the same edit because that flag is what derives the token's recipients.
  • Branch protection from the policy file — one rule per line of git/protected-branches, reconciled to the file on every run, so a push to master is refused by the server and not only by a client hook.
  • Rerunnable, and named for it — a second run converges rather than failing or duplicating. This is why it is forge-sync and not forge-bootstrap.

The custody rule, binding on every step

No password or token is ever an argument: not to ssh, curl, forge, jq or forgejo. Shell tracing is off around every such pipe. curl reads any header from a config on stdin. The forge CLI reads its token from its file. The admin password is printed once to the operator's terminal and never stored; the agent account's password is random and kept by nobody.

Current state and specifics

Scripts live in scripts/, one shell test each under tests/, wired into the flake's checks — forge-ssh-key.sh, vm-ssh-host-key.sh and provision-vm.sh are the closest shapes to follow for argument handling, registry resolution and failure behaviour.

vm-ssh-host-key is the precedent for a command that mutates the secrets clone and then prints the exact commit and re-lock lines for the operator rather than committing itself; allod/nexus#82 asks for that shape to also print the check that the re-lock took. Follow it: this command should not commit or push on the operator's behalf.

Reachability: the forge answers on its guest address, administrative ssh on 22 as root with the hypervisor's key, the clone daemon on 2222, Forgejo over http on 80. services.forgejoDeployment.domain is the guest's own address, supplied by the service builder, so the command should read the machine's address from the registry rather than taking a URL.

Scope

In: the command, its shell test, and the README. Out: the two-node VM test witness, which needs the NixOS test harness this repository does not have and lands as its own issue in allod/archetypes; the dev VM's flip to cloning from the forge; and allod forge up.

Part of allod/strategy#65

A forge VM boots serving an empty Forgejo: no accounts, no organisations, no repositories, and no way for an agent to open a pull request. `forge-sync` reconciles it to the deployment's declared state, and does so on every run rather than once. Primary goals: - **A human account whose name you choose** — the command prompts for it, with `--user` for the non-interactive path. Forgejo's own random password is printed once to the operator's terminal and flagged must-change; `admin` is reserved by Forgejo and cannot be used. - **An agent account with the dev VM's key** — a forge key pair for the dev VM, registered to the agent, so an agent inside a VM can clone, push and open pull requests. - **Organisations mirroring the registry's ids** — a repository id of `allod/tools` becomes organisation `allod`, repository `tools`; a bare id goes to an organisation named for the deployment. Clone remotes keep matching the ids already in use, so nothing in the registry changes. - **Every registry repository pushed** from the hypervisor's own clones, not a subset. - **The agent token never leaves ciphertext** — minted with `forgejo admin user generate-access-token` and piped straight into `age`, encrypted to the recipients `secrets.nix` declares, with `forgeAccess` flipped in the same edit because that flag is what derives the token's recipients. - **Branch protection from the policy file** — one rule per line of `git/protected-branches`, reconciled to the file on every run, so a push to master is refused by the server and not only by a client hook. - **Rerunnable, and named for it** — a second run converges rather than failing or duplicating. This is why it is `forge-sync` and not `forge-bootstrap`. ### The custody rule, binding on every step No password or token is ever an argument: not to `ssh`, `curl`, `forge`, `jq` or `forgejo`. Shell tracing is off around every such pipe. `curl` reads any header from a config on stdin. The `forge` CLI reads its token from its file. The admin password is printed once to the operator's terminal and never stored; the agent account's password is random and kept by nobody. ### Current state and specifics Scripts live in `scripts/`, one shell test each under `tests/`, wired into the flake's checks — `forge-ssh-key.sh`, `vm-ssh-host-key.sh` and `provision-vm.sh` are the closest shapes to follow for argument handling, registry resolution and failure behaviour. `vm-ssh-host-key` is the precedent for a command that mutates the secrets clone and then prints the exact commit and re-lock lines for the operator rather than committing itself; `allod/nexus#82` asks for that shape to also print the check that the re-lock took. Follow it: this command should not commit or push on the operator's behalf. Reachability: the forge answers on its guest address, administrative ssh on 22 as root with the hypervisor's key, the clone daemon on 2222, Forgejo over http on 80. `services.forgejoDeployment.domain` is the guest's own address, supplied by the service builder, so the command should read the machine's address from the registry rather than taking a URL. ### Scope In: the command, its shell test, and the README. Out: the two-node VM test witness, which needs the NixOS test harness this repository does not have and lands as its own issue in `allod/archetypes`; the dev VM's flip to cloning from the forge; and `allod forge up`. Part of allod/strategy#65
Author

Landed on master as 81eb244.

Landed on master as 81eb244.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
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/nexus#84
No description provided.