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.
This commit is contained in:
parent
c2d176ed13
commit
6e9c67dd94
1 changed files with 67 additions and 55 deletions
|
|
@ -8,21 +8,24 @@ let
|
||||||
cfg = config.services.hive-forge;
|
cfg = config.services.hive-forge;
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
# Thin wrapper around `services.forgejo` with hyperhive-friendly
|
# Private Forgejo for hyperhive agents, wrapped in a nixos-container
|
||||||
# defaults: sqlite (no extra service to manage), built-in SSH on a
|
# so it doesn't fight any `services.forgejo` the operator already
|
||||||
# non-22 port so it doesn't fight the host's sshd, registration off
|
# runs on the host. The container shares the host network namespace
|
||||||
# (agents get accounts seeded out of band), and ports opened in the
|
# (`privateNetwork = false`) so agents reach the forge at
|
||||||
# firewall.
|
# `http://localhost:<httpPort>` without any extra plumbing —
|
||||||
|
# nixos-container is just here for state + systemd-unit isolation,
|
||||||
|
# not network isolation.
|
||||||
#
|
#
|
||||||
# Forge wiring into the agent containers is intentionally out of
|
# Container name is `hive-forge` (not `h-*`), so hive-c0re's
|
||||||
# scope here — containers already share the host network namespace,
|
# lifecycle scanner ignores it; the operator manages it via the
|
||||||
# so once this module is enabled an agent can reach the forge at
|
# standard `nixos-container` CLI.
|
||||||
# http://localhost:<httpPort> without any extra bind mount. The MCP
|
#
|
||||||
# tool surface (open PR, list repos, etc.) lives in a separate
|
# State lives at `/var/lib/nixos-containers/hive-forge/var/lib/forgejo/`
|
||||||
# follow-up that the operator opts into per agent.
|
# and survives container restart / host reboot. To wipe, destroy the
|
||||||
|
# container.
|
||||||
|
|
||||||
options.services.hive-forge = {
|
options.services.hive-forge = {
|
||||||
enable = lib.mkEnableOption "hive-forge — private Forgejo instance for hyperhive agents";
|
enable = lib.mkEnableOption "hive-forge — private Forgejo (in a nixos-container) for hyperhive agents";
|
||||||
|
|
||||||
httpPort = lib.mkOption {
|
httpPort = lib.mkOption {
|
||||||
type = lib.types.port;
|
type = lib.types.port;
|
||||||
|
|
@ -30,7 +33,8 @@ in
|
||||||
description = ''
|
description = ''
|
||||||
TCP port the forge serves HTTP on. Default 3000 sits outside
|
TCP port the forge serves HTTP on. Default 3000 sits outside
|
||||||
hyperhive's claimed ranges (dashboard 7000, manager 8000,
|
hyperhive's claimed ranges (dashboard 7000, manager 8000,
|
||||||
sub-agents 8100..8999) so they don't collide.
|
sub-agents 8100..8999). Change this if you already have
|
||||||
|
another forgejo bound to 3000.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -49,10 +53,10 @@ in
|
||||||
default = "localhost";
|
default = "localhost";
|
||||||
example = "forge.internal";
|
example = "forge.internal";
|
||||||
description = ''
|
description = ''
|
||||||
Hostname agents and operator dial the forge by. `localhost` is
|
Hostname used in repo clone URLs the forge advertises. The
|
||||||
fine for a single-host setup since containers share the host
|
container shares host netns so `localhost` works for any
|
||||||
netns; set a real hostname when you want clones from outside
|
agent on the same host; set a real hostname when you want
|
||||||
the host to look canonical.
|
clones from outside the host to look canonical.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -60,15 +64,27 @@ in
|
||||||
type = lib.types.bool;
|
type = lib.types.bool;
|
||||||
default = true;
|
default = true;
|
||||||
description = ''
|
description = ''
|
||||||
Open `httpPort` + `sshPort` in the host firewall. Off when the
|
Open `httpPort` + `sshPort` in the host firewall. Off when
|
||||||
forge should only be reachable from inside the host (the
|
the forge should only be reachable from inside the host.
|
||||||
forge listens on all interfaces either way; this just gates
|
(The container shares host netns, so this is the only
|
||||||
external access).
|
firewall layer that matters.)
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = lib.mkIf cfg.enable {
|
config = lib.mkIf cfg.enable {
|
||||||
|
containers.hive-forge = {
|
||||||
|
autoStart = true;
|
||||||
|
ephemeral = false;
|
||||||
|
# Share host netns — forgejo's HTTP / SSH listeners then look
|
||||||
|
# exactly like a host-side service, no port forwarding dance,
|
||||||
|
# and agent containers (which also share host netns) reach it
|
||||||
|
# via plain `localhost`.
|
||||||
|
privateNetwork = false;
|
||||||
|
config =
|
||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
system.stateVersion = "25.11";
|
||||||
services.forgejo = {
|
services.forgejo = {
|
||||||
enable = true;
|
enable = true;
|
||||||
database.type = "sqlite3";
|
database.type = "sqlite3";
|
||||||
|
|
@ -84,9 +100,9 @@ in
|
||||||
BUILTIN_SSH_SERVER_USER = "git";
|
BUILTIN_SSH_SERVER_USER = "git";
|
||||||
DISABLE_SSH = false;
|
DISABLE_SSH = false;
|
||||||
};
|
};
|
||||||
# Registration off — operator creates agent users via
|
# Registration off — operator seeds agent users via
|
||||||
# `forgejo admin user create` (or the API once seeded).
|
# `nixos-container run hive-forge -- forgejo admin
|
||||||
# Agents that need access get a dedicated account + token.
|
# user create …`.
|
||||||
service = {
|
service = {
|
||||||
DISABLE_REGISTRATION = true;
|
DISABLE_REGISTRATION = true;
|
||||||
REQUIRE_SIGNIN_VIEW = false;
|
REQUIRE_SIGNIN_VIEW = false;
|
||||||
|
|
@ -95,10 +111,12 @@ in
|
||||||
DEFAULT_BRANCH = "main";
|
DEFAULT_BRANCH = "main";
|
||||||
DEFAULT_PRIVATE = "private";
|
DEFAULT_PRIVATE = "private";
|
||||||
};
|
};
|
||||||
# Quiet logs at idle; bump when debugging.
|
|
||||||
log.LEVEL = "Warn";
|
log.LEVEL = "Warn";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
environment.systemPackages = [ pkgs.forgejo ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
networking.firewall = lib.mkIf cfg.openFirewall {
|
networking.firewall = lib.mkIf cfg.openFirewall {
|
||||||
allowedTCPPorts = [
|
allowedTCPPorts = [
|
||||||
|
|
@ -106,11 +124,5 @@ in
|
||||||
cfg.sshPort
|
cfg.sshPort
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
# Convenience: drop the forgejo CLI on the host PATH so the
|
|
||||||
# operator can `forgejo admin user create …` without hunting for
|
|
||||||
# the wrapped binary. The forgejo service runs as its own user;
|
|
||||||
# admin commands need `sudo -u forgejo`.
|
|
||||||
environment.systemPackages = [ pkgs.forgejo ];
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue