move avatar-PNG helpers to hive-core, configurable org avatar

core_avatar_png() and config_org_avatar_png() were hive-sh4re
functions used only by hive-c0re::forge::users. Move them there as
private path-resolution helpers (core_avatar_png_path,
config_org_avatar_png_path), following the existing
std::env::var(...).expect(...) required-env-var style used elsewhere
in hive-c0re.

The org avatar is now independently configurable:
services.hyperhive.c0re.orgAvatarPng (nullable path, default null)
lets an operator override just the agent-configs org avatar PNG
without replacing the whole assets package. Wired via a new
HIVE_ORG_AVATAR_PNG env var that falls back to the bundled PNG when
the option is unset. The core avatar stays under HIVE_ASSETS_DIR,
unchanged.
This commit is contained in:
iris 2026-08-09 23:04:18 +02:00 committed by mara
commit 3e388d2d46
4 changed files with 55 additions and 25 deletions

View file

@ -24,11 +24,36 @@ use crate::paths::FORGE_CORE_TOKEN as CORE_TOKEN_PATH;
// in `crate::paths` — one-shot guards: the upload/align runs once, the
// marker is written, subsequent startups skip. Delete one to force its
// step to re-run.
// Avatar PNGs are loaded at runtime from
// `$HIVE_ASSETS_DIR/branding/{hyperhive,agent-configs}.png` via the
// helpers in `hive_sh4re::assets`. The `agent-configs.png` is
// rendered from its SVG during the `hyperhive-assets` derivation's
// build.
// Avatar PNG paths are resolved by `core_avatar_png_path` /
// `config_org_avatar_png_path` below — only-used-here, so they live
// in this module rather than the shared `hive_sh4re::assets` helpers
// (which stay for paths every crate needs, e.g. `branding_svg`).
/// `$HIVE_ASSETS_DIR/branding/hyperhive.png` — the core mark,
/// rasterised. Not independently configurable (unlike the org avatar
/// below) — override the whole `services.hyperhive.c0re.assets`
/// package to change it.
fn core_avatar_png_path() -> std::path::PathBuf {
let dir = std::env::var("HIVE_ASSETS_DIR").expect(
"HIVE_ASSETS_DIR is unset — hive-c0re.nix sets it unconditionally, \
so this process was started outside the NixOS module",
);
std::path::PathBuf::from(dir).join("branding/hyperhive.png")
}
/// Path to the `agent-configs` org's avatar PNG. Independently
/// configurable via `services.hyperhive.c0re.orgAvatarPng` — the nix
/// module resolves `HIVE_ORG_AVATAR_PNG` to that option's value, or
/// the bundled `agent-configs.png` from `assets` when unset, so an
/// operator can override just this image without replacing the whole
/// `assets` package.
fn config_org_avatar_png_path() -> std::path::PathBuf {
std::path::PathBuf::from(std::env::var("HIVE_ORG_AVATAR_PNG").expect(
"HIVE_ORG_AVATAR_PNG is unset — hive-c0re.nix sets it unconditionally \
(from services.hyperhive.c0re.orgAvatarPng or the bundled default), \
so this process was started outside the NixOS module",
))
}
/// Per-agent token scopes (broad-but-not-admin) for tokens hive-c0re
/// mints itself on the **internal** forge. See `docs/forge.md::Token
/// scopes` for the per-scope rationale. Not `pub(super)` — external
@ -400,7 +425,7 @@ pub(super) async fn ensure_core_avatar(token: &str) -> Result<()> {
if marker.exists() {
return Ok(());
}
let png_path = hive_sh4re::assets::core_avatar_png();
let png_path = core_avatar_png_path();
let png_bytes = tokio::fs::read(&png_path)
.await
.with_context(|| format!("read core avatar PNG from {}", png_path.display()))?;
@ -432,7 +457,7 @@ pub(super) async fn ensure_config_org_avatar(token: &str) -> Result<()> {
if marker.exists() {
return Ok(());
}
let png_path = hive_sh4re::assets::config_org_avatar_png();
let png_path = config_org_avatar_png_path();
let png_bytes = tokio::fs::read(&png_path)
.await
.with_context(|| format!("read {CONFIG_ORG} avatar PNG from {}", png_path.display()))?;