Compare commits
4 changed files with 2 additions and 123 deletions
|
|
@ -48,25 +48,6 @@ pub fn swarm_name() -> Option<String> {
|
||||||
.filter(|s| !s.is_empty())
|
.filter(|s| !s.is_empty())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// One peer hive in the same swarm. Parsed from `HYPERHIVE_PEERS`.
|
|
||||||
#[derive(Debug, Clone, serde::Deserialize)]
|
|
||||||
pub struct PeerHive {
|
|
||||||
pub label: String,
|
|
||||||
pub domain: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Peer hives in the same swarm, parsed from `HYPERHIVE_PEERS` env var
|
|
||||||
/// (JSON array of `{label,domain}` objects, emitted by the c0re NixOS
|
|
||||||
/// module from `services.hyperhive.peers`). Returns empty vec on
|
|
||||||
/// single-hive deploys (env var absent).
|
|
||||||
#[must_use]
|
|
||||||
pub fn peers() -> Vec<PeerHive> {
|
|
||||||
env::var("HYPERHIVE_PEERS")
|
|
||||||
.ok()
|
|
||||||
.and_then(|s| serde_json::from_str(&s).ok())
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Hive-qualified agent identity. When the hive domain is configured, returns
|
/// Hive-qualified agent identity. When the hive domain is configured, returns
|
||||||
/// `${label}@${domain}` (e.g. `iris@darkest.space`); when not, returns just
|
/// `${label}@${domain}` (e.g. `iris@darkest.space`); when not, returns just
|
||||||
/// the short label so callers can render a single string regardless of
|
/// the short label so callers can render a single string regardless of
|
||||||
|
|
|
||||||
|
|
@ -266,24 +266,6 @@ struct StateSnapshot {
|
||||||
/// var, set from `services.hyperhive.swarmName`. `None` when
|
/// var, set from `services.hyperhive.swarmName`. `None` when
|
||||||
/// unset — chrome omits the swarm segment of the breadcrumb.
|
/// unset — chrome omits the swarm segment of the breadcrumb.
|
||||||
swarm_name: Option<String>,
|
swarm_name: Option<String>,
|
||||||
/// Peer hives in the same swarm. Parsed from `HYPERHIVE_PEERS`
|
|
||||||
/// (JSON array of `{label,domain}` objects, emitted by the c0re
|
|
||||||
/// NixOS module from `services.hyperhive.peers`). Empty on
|
|
||||||
/// single-hive deploys. Feeds the P33RS dashboard tab. Each entry
|
|
||||||
/// exposes `name` (the operator-chosen label) and `url` (dashboard
|
|
||||||
/// link derived as `http://<domain>/`; HTTP-only until the gateway
|
|
||||||
/// has TLS and the scheme is threaded through).
|
|
||||||
peer_hives: Vec<PeerHiveView>,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// One peer hive for the P33RS dashboard tab. Derived from
|
|
||||||
/// `HYPERHIVE_PEERS` env; `url` is the peer's dashboard root so the
|
|
||||||
/// tab can render a clickable card without knowing the remote port.
|
|
||||||
/// HTTP-only until per-peer TLS is wired through the gateway layer.
|
|
||||||
#[derive(Serialize)]
|
|
||||||
struct PeerHiveView {
|
|
||||||
name: String,
|
|
||||||
url: String,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `OpQuestion` + computed `question_refs` / `answer_refs`. Built
|
/// `OpQuestion` + computed `question_refs` / `answer_refs`. Built
|
||||||
|
|
@ -493,36 +475,9 @@ async fn api_state(headers: HeaderMap, State(state): State<AppState>) -> axum::J
|
||||||
forge_public_url: std::env::var("HIVE_FORGE_PUBLIC_URL").ok().filter(|s| !s.is_empty()),
|
forge_public_url: std::env::var("HIVE_FORGE_PUBLIC_URL").ok().filter(|s| !s.is_empty()),
|
||||||
hive_name: std::env::var("HYPERHIVE_HIVE_NAME").ok().filter(|s| !s.is_empty()),
|
hive_name: std::env::var("HYPERHIVE_HIVE_NAME").ok().filter(|s| !s.is_empty()),
|
||||||
swarm_name: std::env::var("HYPERHIVE_SWARM_NAME").ok().filter(|s| !s.is_empty()),
|
swarm_name: std::env::var("HYPERHIVE_SWARM_NAME").ok().filter(|s| !s.is_empty()),
|
||||||
peer_hives: parse_peer_hives(),
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Parse `HYPERHIVE_PEERS` env var into dashboard-ready `PeerHiveView`
|
|
||||||
/// entries. The env var is a JSON array of `{label, domain}` objects
|
|
||||||
/// emitted by the c0re NixOS module from `services.hyperhive.peers`.
|
|
||||||
/// Each entry becomes `{ name: label, url: "http://domain/" }` for the
|
|
||||||
/// P33RS tab. Returns empty vec when unset (single-hive deploy).
|
|
||||||
fn parse_peer_hives() -> Vec<PeerHiveView> {
|
|
||||||
#[derive(serde::Deserialize)]
|
|
||||||
struct Raw {
|
|
||||||
label: String,
|
|
||||||
domain: String,
|
|
||||||
}
|
|
||||||
let Ok(json) = std::env::var("HYPERHIVE_PEERS") else {
|
|
||||||
return Vec::new();
|
|
||||||
};
|
|
||||||
let Ok(raw): Result<Vec<Raw>, _> = serde_json::from_str(&json) else {
|
|
||||||
tracing::warn!("HYPERHIVE_PEERS is not valid JSON; ignoring");
|
|
||||||
return Vec::new();
|
|
||||||
};
|
|
||||||
raw.into_iter()
|
|
||||||
.map(|r| PeerHiveView {
|
|
||||||
name: r.label,
|
|
||||||
url: format!("http://{}/", r.domain),
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Group live containers by their assigned web UI port; clusters with
|
/// Group live containers by their assigned web UI port; clusters with
|
||||||
/// more than one member are port-hash collisions the operator needs
|
/// more than one member are port-hash collisions the operator needs
|
||||||
/// to resolve by renaming. Manager (fixed at 8000) and sub-agents
|
/// to resolve by renaming. Manager (fixed at 8000) and sub-agents
|
||||||
|
|
|
||||||
|
|
@ -325,8 +325,6 @@ const CANONICAL_INPUTS: &[&str] = &["nixpkgs", "nixpkgs-unstable"];
|
||||||
/// touching process-wide env).
|
/// touching process-wide env).
|
||||||
const FORWARDED_VARS: &[&str] = &[
|
const FORWARDED_VARS: &[&str] = &[
|
||||||
"HIVE_FORGE_URL",
|
"HIVE_FORGE_URL",
|
||||||
"HIVE_FORGE_PUBLIC_URL",
|
|
||||||
"HYPERHIVE_PEERS",
|
|
||||||
"HYPERHIVE_HIVE_DOMAIN",
|
"HYPERHIVE_HIVE_DOMAIN",
|
||||||
"HYPERHIVE_HIVE_NAME",
|
"HYPERHIVE_HIVE_NAME",
|
||||||
"HYPERHIVE_SWARM_NAME",
|
"HYPERHIVE_SWARM_NAME",
|
||||||
|
|
|
||||||
|
|
@ -53,7 +53,7 @@ in
|
||||||
message if it's missing. Exposed to agents as
|
message if it's missing. Exposed to agents as
|
||||||
`HYPERHIVE_HIVE_DOMAIN`; consumed by
|
`HYPERHIVE_HIVE_DOMAIN`; consumed by
|
||||||
`hive-ag3nt::identity::hive_domain()` for `<name>@<domain>`
|
`hive-ag3nt::identity::hive_domain()` for `<name>@<domain>`
|
||||||
qualified labels.
|
qualified labels (#589).
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -86,57 +86,12 @@ in
|
||||||
description = ''
|
description = ''
|
||||||
Human-readable name of the wider swarm this hive belongs to.
|
Human-readable name of the wider swarm this hive belongs to.
|
||||||
Hives at different DNS domains can share a swarm name when
|
Hives at different DNS domains can share a swarm name when
|
||||||
they federate together. Exposed to agents as
|
they federate together (#589). Exposed to agents as
|
||||||
`HYPERHIVE_SWARM_NAME`; surfaced in the dashboard chrome and
|
`HYPERHIVE_SWARM_NAME`; surfaced in the dashboard chrome and
|
||||||
per-agent system prompt when set.
|
per-agent system prompt when set.
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
# Peer hives in the same swarm. Each entry declares a remote hive
|
|
||||||
# reachable from this host. Serialised to JSON and injected as
|
|
||||||
# `HYPERHIVE_PEERS` into the hive-c0re service and forwarded to agent
|
|
||||||
# containers via `meta.rs::FORWARDED_VARS`. Consumed by
|
|
||||||
# `identity.rs::peers()` + the dashboard's `peer_hives` state field
|
|
||||||
# (feeds the P33RS dashboard tab).
|
|
||||||
options.services.hyperhive.peers = lib.mkOption {
|
|
||||||
type = lib.types.attrsOf (lib.types.submodule {
|
|
||||||
options = {
|
|
||||||
domain = lib.mkOption {
|
|
||||||
type = lib.types.str;
|
|
||||||
example = "lab.example.com";
|
|
||||||
description = ''
|
|
||||||
DNS domain of the peer hive. Used to construct the peer's
|
|
||||||
dashboard URL (`http://''${domain}/`) and for Matrix
|
|
||||||
federation auto-discovery (`matrix.''${domain}`).
|
|
||||||
Must be reachable from this host.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
tlsCertFile = lib.mkOption {
|
|
||||||
type = lib.types.nullOr lib.types.path;
|
|
||||||
default = null;
|
|
||||||
description = ''
|
|
||||||
Optional path to a PEM cert/bundle to trust for this peer's
|
|
||||||
TLS. Null = system CA bundle (for Let's Encrypt peers). Set
|
|
||||||
to the peer's self-signed cert for `selfSignedTls = true`
|
|
||||||
peers. Forward-compat slot; not yet used in v0.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
});
|
|
||||||
default = { };
|
|
||||||
example = {
|
|
||||||
lab = { domain = "lab.example.com"; };
|
|
||||||
edge = { domain = "edge.corp"; };
|
|
||||||
};
|
|
||||||
description = ''
|
|
||||||
Peer hives in the same swarm. The attrset key is a short label
|
|
||||||
used in dashboard links and log messages -- it does not need to
|
|
||||||
match the remote hive's `hiveName`. Null `tlsCertFile` uses the
|
|
||||||
system CA bundle; set it for self-signed TLS peers (forward-compat
|
|
||||||
slot, not yet used in v0).
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
options.services.hyperhive.c0re = {
|
options.services.hyperhive.c0re = {
|
||||||
enable = lib.mkOption {
|
enable = lib.mkOption {
|
||||||
type = lib.types.bool;
|
type = lib.types.bool;
|
||||||
|
|
@ -354,16 +309,6 @@ in
|
||||||
# wrong). Absent when `behindGateway = false` — dashboard
|
# wrong). Absent when `behindGateway = false` — dashboard
|
||||||
# falls back to `<hostname>:3000`.
|
# falls back to `<hostname>:3000`.
|
||||||
HIVE_FORGE_PUBLIC_URL = "https://${config.services.hyperhive.forge.domain}";
|
HIVE_FORGE_PUBLIC_URL = "https://${config.services.hyperhive.forge.domain}";
|
||||||
}
|
|
||||||
// lib.optionalAttrs (config.services.hyperhive.peers != { }) {
|
|
||||||
# Peer hives serialised as a JSON array of {label, domain} objects.
|
|
||||||
# Consumed by hive-ag3nt::identity::peers() + the dashboard's
|
|
||||||
# peer_hives StateSnapshot field (P33RS tab). tlsCertFile is
|
|
||||||
# nix-side-only (host nginx/trust config); rust never needs the path.
|
|
||||||
HYPERHIVE_PEERS = builtins.toJSON (
|
|
||||||
lib.mapAttrsToList (label: p: { inherit label; inherit (p) domain; })
|
|
||||||
config.services.hyperhive.peers
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
serviceConfig = {
|
serviceConfig = {
|
||||||
ExecStart = "${cfg.package}/bin/hive-c0re --socket /run/hyperhive/host.sock serve --hyperhive-flake ${cfg.hyperhiveFlake} --dashboard-port ${toString cfg.dashboardPort} --operator-pronouns ${lib.escapeShellArg cfg.operatorPronouns} --context-window-tokens ${lib.escapeShellArg (builtins.toJSON cfg.contextWindowTokens)}";
|
ExecStart = "${cfg.package}/bin/hive-c0re --socket /run/hyperhive/host.sock serve --hyperhive-flake ${cfg.hyperhiveFlake} --dashboard-port ${toString cfg.dashboardPort} --operator-pronouns ${lib.escapeShellArg cfg.operatorPronouns} --context-window-tokens ${lib.escapeShellArg (builtins.toJSON cfg.contextWindowTokens)}";
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue