Commit graph hyperhive/nix/modules/hive-forge.nix
Author SHA1 Message Date
atlas
9c27c4076f nix/hive-forge: unify on cfg.domain as full hostname, drop subdomain label (mara #754:9684)
mara on PR #754: "would it be better to specify full forge domain in
options instead?"

Drops the awkward `cfg.subdomain` label option. Now `cfg.domain` is
the single source of truth for both the forgejo `DOMAIN` setting
(existing semantics) AND the gateway vhost server-name (new).

## Before / after

```nix
# before: separate label + cfg.domain juggling
services.hyperhive.forge.subdomain = "forge";       # → forge.<hive>
services.hyperhive.forge.domain = "localhost";      # unused for vhost

# after: full domain, single option
services.hyperhive.forge.domain = "forge.darkest.space";  # ← used for ROOT_URL + vhost
```

## Default

`cfg.domain` default auto-derives:
- `forge.<services.hyperhive.domain>` when hive-domain is set
- `"localhost"` otherwise (pre-#749 direct-on-port shape)

So the common case (hive-domain set) gets `forge.<hive>` for free,
operators with a bespoke shape (`git.example.com`) set the full
hostname directly.

## Assertions

- `cfg.domain != ""` — empty would render `.<hive>` shaped garbage
  in both server_name + /etc/hosts.
- `cfg.behindGateway → gateway.enable` — can't route through a
  gateway that isn't running.

(The previous "subdomain = empty" assertion is dropped — that
edge case is gone with the rename.)

## Verified

- default with `hyperhive.domain = "test.local"` → `forge.test.local`,
  `ROOT_URL = http://forge.test.local/`, vhost present
- `forge.domain = "git.example.com"` → `git.example.com`,
  `ROOT_URL = http://git.example.com/`, vhost = `["_", "git.example.com"]`
- `gateway.enable = false` → `forge.domain` falls back to `localhost`,
  `ROOT_URL = http://localhost:3000/`, no gateway vhost
  (`behindGateway = false`)
- `/etc/hosts` (when `localHostsEntry = true`) → unique entries for
  hive-domain + forge.domain (de-duped via `lib.unique` for the
  edge case where forge.domain = hive-domain)
- full container toplevel builds clean

## PR title

(Will fix the PR title separately — still says "/forge/" which is
wrong since the rewrite to sub-domain shape.)
2026-05-31 13:38:25 +02:00
atlas
de67002c94 nix/hive-forge: reject subdomain="" with assertion (argus #754 v2 🟡)
argus on PR #754 v2 review:
> `subdomain = ""` edge case: when `cfg.subdomain = ""`, the
> `localHostsEntry` appends ".${domain}" (invalid hostname; bare
> domain is already covered) and the virtualHosts key becomes
> ".${domain}" (nginx treats this as a wildcard catch-all, not a
> bare-domain server block). docs call this "advanced: collides
> with dashboard server block" — the actual nginx behavior is
> more surprising than that.

Fix: reject `""` at assertion time rather than ship the surprising
behaviour. Bare-domain landing is what the dashboard already
serves; there's no use case for `""` that null doesn't already
cover. Updated option description + dropped the now-dead branch
from the `subdomain` let-binding.

Verified: `services.hyperhive.forge.subdomain = ""` triggers the
new assertion at toplevel build with a clear message pointing at
`null` as the right opt-out. Default + `null` paths still build
clean.
2026-05-31 13:38:25 +02:00
atlas
f037056015 nix/hive-{forge,gateway}: move forge to forge.<hive-domain> sub-domain (#749, mara verdict)
mara on #749:9609: "we will go with sub domains for forge and matrix
(redirected in well known in the latter case, not user visible). close /
fix PRs you have open that dont match this."

Reshapes the v1 sub-path (`<host>/forge/`) approach into a sub-domain
vhost (`forge.<host>/`) per the mara verdict. matrix gets the same
treatment in damocles's #751 follow-up.

## Why sub-domain

- forgejo's default `ROOT_URL = http://<host>/` works without any
  `X-Forwarded-Prefix` gymnastics — sub-domain hosting is the
  canonical Forgejo deploy shape, matches every upstream-doc example.
- Cookie / storage isolation between the dashboard and forge (XSS blast
  radius shrinks; a future forge XSS can't reach dashboard session).
- matches the matrix-spec pattern that #751 wires up for the
  homeserver.

## Mechanics

**forge options:**
- `services.hyperhive.forge.subdomain` — nullable str, default `"forge"`
  → rendered sub-domain is `forge.<hive-domain>`. Set to `null` to opt
  out (forge stays direct on `httpPort`); set to `""` for bare-domain
  landing (advanced, collides with dashboard).
- `services.hyperhive.forge.rootUrl` — nullable str override. When
  null, auto-derived: `http://<subdomain>.<hive>/` when gateway is on
  + subdomain set, else `http://<domain>:<httpPort>/` (direct).
- **Asserts** rootUrl ends with `/` (argus 🟡 on #754: forgejo's
  ROOT_URL contract requires trailing slash, else emits
  `https://forge.example.com.user.id` shaped garbage). Asserts
  `subdomain != null` requires `hyperhive.domain` set.

**gateway:**
- New `virtualHosts."<subdomain>.<hive-domain>"` server block —
  separate from the `"_"` catch-all. Proxies all `/` →
  `http://127.0.0.1:<forge.httpPort>/` so forgejo handles requests at
  root (no prefix translation needed; matches the upstream-default
  ROOT_URL shape).
- Git-tuned: `client_max_body_size 1G`, `proxy_read_timeout 1h`,
  `proxy_send_timeout 1h`, `proxy_buffering off`,
  `proxyWebsockets = true`. SSH stays direct on `cfg.sshPort`.
- `networking.hosts` (when `localHostsEntry = true`) now also adds
  `forge.<hive-domain> -> 127.0.0.1` for the dev loop.

## Verified

- `nix eval ROOT_URL` → `http://forge.test.local/` (default with
  gateway on)
- `nix eval ROOT_URL` with `gateway.enable = false` → `http://localhost:3000/`
  (current direct shape preserved)
- `nix eval virtualHosts attrs` → `["_", "forge.test.local"]`
- `nix eval networking.hosts` with `localHostsEntry = true` →
  `{"127.0.0.1": ["test.local", "forge.test.local"], ...}`
- bad rootUrl (no trailing /) triggers assertion at toplevel build
  with the spelled-out forgejo failure mode
- full container toplevel builds clean
  (`nixos-system-hive-gateway-26.05pre-git`)

## Migration

ROOT_URL change is a one-way migration on rebuild:
- Existing agent `git remote origin` URLs (`http://localhost:3000/...`)
  **keep working** — forgejo accepts any inbound URL; the URL on the
  agent side is unchanged.
- New clone-link copy-paste from forge UI uses `forge.<hive>/...` —
  operators copying clones after this lands need to go through the
  new sub-domain.
- Direct browsing on `:3000` shows pages with `forge.<hive>` links →
  works if hosts entry / DNS resolves, broken otherwise. Operators
  should switch to `http://forge.<hive>/`.

## Out of scope

- TLS termination (mara explicit on #15: no TLS v0)
- SSH-over-HTTPS / wildcard cert provisioning
- matrix sub-domain (damocles's #751, sibling work)

Closes #749. Addresses argus 🟡 on #754.
2026-05-31 13:38:25 +02:00
damocles
4526e40a49 lifecycle: drop manager port special case (#753) — manager hashes into 8100..8999 like every other agent 2026-05-31 13:25:13 +02:00
atlas
85790b0cca nix: add breaking-change note to each openFirewall description (argus #653)
argus picked option (a) on #653: put the upgrade note in each option's
`description` so it shows up in `nix flake show` + the rendered
options docs, right next to the option itself. cheapest option, no
eval-time noise (a `warnings` block would fire on every new
deployment that wants false — the normal case now).

Appended a `**Breaking change as of #651**` paragraph to each of the
three `openFirewall` descriptions, naming the exact option string the
operator needs to set to restore the old behaviour.

Gateway's note specifically calls out that external reach is the
common case (operator's primary entry point), so the upgrade hint
is most likely needed there.
2026-05-30 19:30:11 +02:00
atlas
7feef4cc5d nix: openFirewall defaults false across forge/gateway/matrix (#651)
mara on #651: "Dont default openFirewall to true."

Flip the `openFirewall` default from `true` to `false` for all three
modules that expose host-side ports:

- `services.hyperhive.forge.openFirewall` (httpPort 3000 + sshPort 2222)
- `services.hyperhive.gateway.openFirewall` (port 80)
- `services.hyperhive.matrix.openFirewall` (httpPort 8008)

Rationale: secure-by-default. With shared host netns, the host +
every agent container reach these services via `localhost` regardless
of the firewall — the open only matters for access from outside the
host. Operators who want external reach now flip the bool explicitly:

    services.hyperhive.gateway.openFirewall = true;

Each description updated to explain the new default + when to flip
it (operator's browser, external git clients, federation announcement,
etc.). Behind a host-level reverse proxy that handles TLS, leave off.

Verified via `nix eval` on a clean stub config:
- forge openFirewall = false
- gateway openFirewall = false
- matrix openFirewall = false
- networking.firewall.allowedTCPPorts = [] (was: [80 2222 3000 8008])

Note: c0re's direct ports (7000/8000/8100-8999) are gated separately
via #621 on `gateway.enable` — that gate stays; this PR only touches
the per-module `openFirewall` knobs.

Closes #651.
2026-05-30 19:25:28 +02:00
atlas
3b500bba1b nix: pivot to services.hyperhive.* per mara directive (#612)
Per [mara on PR #615 comment 7349](http://localhost:3000/hyperhive/hyperhive/pulls/615#issuecomment-7349):
> follow nix conventions, services.hyperhive it is. the earlier we
> change this, the less breakage.

Renames the entire host-side option tree under `services.hyperhive.*`:

- `services.hive-c0re.*` → `services.hyperhive.c0re.*`
- `hyperhive.enable` → `services.hyperhive.enable`
- `hyperhive.domain` → `services.hyperhive.domain`
- `hyperhive.forge.*` → `services.hyperhive.forge.*`
- `hyperhive.matrix.*` → `services.hyperhive.matrix.*`

Per mara's "earlier = less breakage", the previous `services.hive-c0re.enable`
deprecation alias is dropped. Operators get a clear eval error on the
old paths pointing at the rename. Single migration moment.

Per-agent options in `nix/templates/harness-base.nix` (`hyperhive.model`,
`hyperhive.allowedRecipients`, etc.) stay at `hyperhive.*` — they're
container-level config, not services in the NixOS sense.

Verified via `nix flake check --no-build` + an end-to-end NixOS eval
exercising every renamed path.

Follow-up needed: rust source comments referencing the old NixOS
option names (`hive-c0re/src/{meta,coordinator,main,dashboard}.rs`)
should be updated in a separate pure-rust PR to keep this one
strictly nix-only.
2026-05-30 11:07:57 +02:00
damocles
cbd4b71322 fix #296: auto-generate GPG signing key for Forgejo on first boot 2026-05-22 22:29:57 +02:00
damocles
310fd0b481 hive-forge: add APP_NAME, replace logo.png/favicon.png with hyperhive branding 2026-05-21 20:21:44 +02:00
damocles
468d682085 forge: use branding/hyperhive.svg for logo and favicon 2026-05-21 17:50:46 +02:00
damocles
615928453d forge: replace forgejo logo with hyperhive mark (closes #143) 2026-05-21 17:50:46 +02:00
damocles
cc7d349139 fix: use --no-preserve=mode when copying forgejo static root 2026-05-20 20:09:00 +02:00
damocles
30c7274cc7 fix forge theme: bake css into static root via STATIC_ROOT_PATH 2026-05-20 20:02:06 +02:00
iris
e6469403ee fix forge theme: add forgejo-* to THEMES, use C+ copy for CSS 2026-05-20 16:22:02 +02:00
iris
67f948028c add catppuccin mocha × vibec0re theme to forge
- new nix/forge-theme/theme-catppuccin-vibec0re.css: full Catppuccin Mocha
  palette mapped to all Forgejo CSS custom properties + chroma syntax
  highlighting; vibec0re glow effects on primary buttons, nav, and links
- hive-forge.nix: ui.DEFAULT_THEME + ui.THEMES settings
- systemd.tmpfiles.rules symlinks the nix-managed CSS into
  /var/lib/forgejo/custom/public/assets/css/ before forgejo starts;
  container rebuild picks up CSS changes automatically

Closes #55
2026-05-20 15:42:32 +02:00
müde
6ab3810e18 docs: refresh for the dashboard rework + recent harness commits
- web-ui.md: side panel, approval card + 3-way diff base, stats
  page, forge config links, removed agent.nix viewer, per-agent
  loose-ends inline answer.
- approvals.md: forge mirror section + diff base toggle.
- turn-loop.md: recv(max), get_logs, remind, loose-ends, whoami.
- agent.md / manager.md prompts: recv(max), remind, get_logs.
- CLAUDE.md: forge.rs / stats.rs / hive-forge.nix in the file
  map, scratchpad refresh.

also: forgejo migrations.ALLOW_LOCALNETWORKS = true so an in-hive
mirror of the hyperhive repo can import from a localhost source.
2026-05-20 11:34:43 +02:00
müde
49f4e9cc89 dashboard: forge-linked config + approval card + 3-way diff base
- forge nix option moves to hyperhive.forge.enable, defaults true;
  hive-c0re imports the forge module so it's on by default.
- drop the agent.nix container-row viewer + /api/agent-config; link
  to the agent-configs forge repo instead.
- restructure pending approvals into a card (identity header /
  what-changed body / decision actions) with a link to the proposal
  commit on the forge.
- diff opens in the side panel with a 3-way base toggle: vs applied
  (running) / vs last-approved / vs previous proposal, served by the
  new /api/approval-diff/{id}?base= endpoint.
2026-05-20 11:22:28 +02:00
müde
608de57924 hive-forge: default to pkgs.forgejo (15.x), expose package option
nixpkgs's services.forgejo defaults to forgejo-lts (11.0.13 today);
LTS lags far enough behind that any prior non-LTS run against the
same state dir leaves the DB at a migration the LTS binary can't
read ('database newer than binary, refusing to start'). default to
the latest release line and let operators opt down to LTS by
overriding services.hive-forge.package.
2026-05-17 01:29:19 +02:00
müde
fed943a04e hive-forge: pin F3 PATH absolute (init runs even when disabled)
forgejo's F3 init resolves data-dir before checking ENABLED, so
`forgejo admin user create` still fataled on the RO nix-store
default. set [F3] PATH = /var/lib/forgejo/data/f3 alongside the
disable.
2026-05-17 00:25:55 +02:00
müde
3e3c27ac48 hive-forge: disable F3 (federation) — defaults to RO nix-store path
forgejo's F3 federation subsystem resolves its data dir relative to
the binary, which under nixos lands at /run/current-system/sw/bin/data/f3
(read-only nix store) and fatals the daemon at boot. we don't
federate; turn it off.
2026-05-17 00:03:41 +02:00
müde
6e9c67dd94 hive-forge: wrap forgejo in a nixos-container
avoids fighting an operator-side `services.forgejo` over the
singleton module options. container shares host netns
(`privateNetwork = false`) so agents still dial the forge via
plain `localhost:<httpPort>` and the host firewall is the only
layer that matters. container name is `hive-forge` (no `h-`
prefix) so hive-c0re's lifecycle scanner ignores it — operator
manages it with the standard `nixos-container` CLI. state lives
at `/var/lib/nixos-containers/hive-forge/var/lib/forgejo/` and
survives restarts.
2026-05-16 20:52:36 +02:00
müde
c2d176ed13 add hive-forge module: private forgejo for agents
new `services.hive-forge.enable` (off by default) wraps
`services.forgejo` with hyperhive-friendly defaults: sqlite (no
extra db service), built-in ssh on 2222 so it doesn't fight the
host's openssh, http on 3000 (outside hyperhive's 7000/8000/8100-8999
ranges), registration off (operator seeds agent users), private
repos by default. exported as `nixosModules.hive-forge` — operator
imports it on the host alongside hive-c0re. container-side wiring
(MCP tools or a bind-mounted token) is deferred; containers already
share the host netns so they can reach http://localhost:3000 today.
2026-05-16 20:50:36 +02:00