diff --git a/docs/2-handbook/agents/web-page.md b/docs/2-handbook/agents/web-page.md
index 0fd2805c..bf37ec21 100644
--- a/docs/2-handbook/agents/web-page.md
+++ b/docs/2-handbook/agents/web-page.md
@@ -81,14 +81,11 @@ at a given viewport width. Two columns:
- **Links badge** (`π`): opens a popover listing `β dashboard`
(back-link to the host dashboard, `${dashboardBase}`)
followed by this agent's `agent_links()`-sourced navigation (stats,
- screen when VNC is enabled, forge profile + config-repo mirror when
- the agent has a forge account, any `services.hyperhive.agent.dashboardLinks`
+ screen when VNC is enabled, any `services.hyperhive.agent.dashboardLinks`
extras) β all as real `` elements, not a `Dropdown`-style
command list, so ctrl/middle-click and "copy link address" keep
working. Each `AgentLink.kind` resolves differently: `container` β
- same-origin path; `forge` β `state.forge_public_url + url`, and
- hive-agent drops the link entirely when that's unset (never guessed from
- `:3000`); `external` β already absolute. Same source
+ same-origin path; `external` β already absolute. Same source
(`GET /api/state`'s `links` field) also feeds
`DashboardState.links` for the dashboard card's icon strip β
`agent_links()` in hive-agent is the single source of truth for
diff --git a/docs/2-handbook/hives/README.md b/docs/2-handbook/hives/README.md
index e1ca2d04..7082ad2c 100644
--- a/docs/2-handbook/hives/README.md
+++ b/docs/2-handbook/hives/README.md
@@ -45,8 +45,8 @@ covers it.
**Check on an agent.** 4G3NTS shows every container as a row: name, whether
it's running, what it's doing (a live status pill such as `rebuildingβ¦` or
-`startingβ¦` while something's in flight), and quick links (stats, screen,
-forge profile). Select the name to open its terminal and watch it work.
+`startingβ¦` while something's in flight), and quick links (stats, screen).
+Select the name to open its terminal and watch it work.
**Review a config change.** Agent config changes (new packages, env vars, MCP
servers) are pull requests on the agent's `agent-configs` repo. Review and
diff --git a/docs/2-handbook/hives/dashboard.md b/docs/2-handbook/hives/dashboard.md
index ef6a5143..1ec23330 100644
--- a/docs/2-handbook/hives/dashboard.md
+++ b/docs/2-handbook/hives/dashboard.md
@@ -197,9 +197,8 @@ fetch entirely.
**icon-only nav strip** plus live agent-owned state, all populated
async from a single `GET /api/dashboard-state` call to the agent's
own hive-agent. The response (`DashboardState`) carries: `links` (nav
-strip entries β `π stats`, `π₯ screen` when GUI is enabled, `⬑ forge
-profile`, `β³ agent-configs mirror`, plus any agent-declared
-`dashboardLinks` extras), `status_text` / `status_set_at` (agent
+strip entries β `π stats`, `π₯ screen` when GUI is enabled, plus any
+agent-declared `dashboardLinks` extras), `status_text` / `status_set_at` (agent
self-reported status β the `(set N ago)` chip carries a `data-set-at`
stamp and ticks every 30s to stay fresh across the long-lived keyed row
cache), `rate_limited`, `ctx_tokens` / `context_window_tokens`
@@ -216,11 +215,9 @@ per-agent harness β TCP or unix-domain depending on the agent's
`docs/4-implementation/networking/gateway.md::Per-agent unix-socket upstream`).
Gateway-off is the flag-absent fallback (a dashboard served outside a
hyperhive deploy): base URL is
-`http://:` (direct TCP). Forge
-links resolve against `state.forge_public_url` (sourced from
-`services.hyperhive.swarm.forge.publicUrl`) and disappear entirely when
-that's unset β never guessed from `:3000`. External links are
-already absolute. The same base URL drives the primary agent-name
+`http://:` (direct TCP). External links are
+already absolute. The row has no forge links: an agent's forge profile
+and config repo are on the forge itself. The same base URL drives the primary agent-name
link + favicon fetch, so the whole row routes through the gateway
as a unit.
diff --git a/docs/4-implementation/networking/gateway.md b/docs/4-implementation/networking/gateway.md
index 833730a3..0a153759 100644
--- a/docs/4-implementation/networking/gateway.md
+++ b/docs/4-implementation/networking/gateway.md
@@ -431,9 +431,7 @@ when `StateSnapshot.gateway_enabled` is true, and direct
every hive, so the direct shape is a fallback for the variable being unset.
Three render sites follow it: the agent-name link, the favicon fetch
(`/icon`), and the nav-strip `container`-kind links from
-`DashboardState.links` (`GET /api/dashboard-state`). Forge links come from
-`swarm.forge.publicUrl`; with it unset the dashboard hides them rather than
-guess. See `docs/2-handbook/hives/dashboard.md::Container row` for the frontend side.
+`DashboardState.links` (`GET /api/dashboard-state`). See `docs/2-handbook/hives/dashboard.md::Container row` for the frontend side.
### Dialing another vhost by name (`verifiedProxyTo`)
diff --git a/frontend/packages/agent/src/Root.tsx b/frontend/packages/agent/src/Root.tsx
index 61266b49..50e6a907 100644
--- a/frontend/packages/agent/src/Root.tsx
+++ b/frontend/packages/agent/src/Root.tsx
@@ -141,7 +141,7 @@ export function Root() {
count={todos.length}
onClick={() => setOpenPanel("todos")}
/>
- {/* Needs a loaded snapshot for its data (links/forge_public_url,
+ {/* Needs a loaded snapshot for its data (links,
* dashboard_port) β omitted until the first /api/state resolves,
* same as the old page's populateOverflowMenu (now deleted β
* dashboard-link moved here, rebuild removed outright, see
@@ -149,7 +149,6 @@ export function Root() {
{state ? (
) : null}
diff --git a/frontend/packages/agent/src/agent.css b/frontend/packages/agent/src/agent.css
index 3094417e..6e471724 100644
--- a/frontend/packages/agent/src/agent.css
+++ b/frontend/packages/agent/src/agent.css
@@ -196,7 +196,7 @@ h3 {
object-fit: cover;
}
-/* Meta-nav links (stats / screen / forge / dashboard / extras) β
+/* Meta-nav links (stats / screen / dashboard / extras) β
no underline; hover lights with cyan glow + subtle background
tint. Reads as a row of soft tabs rather than default-styled
inline anchors. */
diff --git a/frontend/packages/agent/src/components/MetaNav.tsx b/frontend/packages/agent/src/components/MetaNav.tsx
index 585d5acd..23b8cf0c 100644
--- a/frontend/packages/agent/src/components/MetaNav.tsx
+++ b/frontend/packages/agent/src/components/MetaNav.tsx
@@ -2,7 +2,7 @@
// (`@hive/shared/icons.js`'s `LinkIcon` β an emoji glyph can't match a
// neighbouring icon's size/weight on any platform, see that file's
// comment) in the pills cluster that opens a popover listing
-// stats/screen/forge/config + any `services.hyperhive.agent.dashboardLinks` extras,
+// stats/screen + any `services.hyperhive.agent.dashboardLinks` extras,
// sourced from the backend's `agent_links()` (the single source of
// truth β same list also feeds the dashboard card's icon strip) β plus
// a `π§ dashboard` back-link.
@@ -28,17 +28,12 @@ import "./MetaNav.css";
export interface MetaNavProps {
links: AgentLink[];
- forgePublicUrl: string | null;
/** Absolute base URL of the host dashboard (`resolveDashboardBase`)
* β the `π§ dashboard` item links to `${dashboardBase}`. */
dashboardBase: string;
}
-export function MetaNav({
- links,
- forgePublicUrl,
- dashboardBase,
-}: MetaNavProps) {
+export function MetaNav({ links, dashboardBase }: MetaNavProps) {
const [open, setOpen] = useState(false);
const rootRef = useRef(null);
@@ -64,11 +59,7 @@ export function MetaNav({
};
}, [open]);
- // Same kind -> URL resolution as app.js's old refreshState meta-links
- // loop: `forge` needs `forgePublicUrl` set or the link is dropped
- // entirely (never guessed from `:3000`); `external` is already
- // absolute; `container` is a same-origin path.
- const visible = links.filter((lnk) => lnk.kind !== "forge" || forgePublicUrl);
+ // `external` is already absolute; `container` is a same-origin path.
// No early-return-on-empty: the dashboard link below is always
// present, so the trigger always has at least one item.
@@ -93,24 +84,20 @@ export function MetaNav({
>
π§ dashboard
- {visible.map((lnk) => {
- const href =
- lnk.kind === "forge" ? `${forgePublicUrl}${lnk.url}` : lnk.url;
- return (
- setOpen(false)}
- >
- {lnk.icon ? {lnk.icon} : null}
- {lnk.label}
-
- );
- })}
+ {links.map((lnk) => (
+ setOpen(false)}
+ >
+ {lnk.icon ? {lnk.icon} : null}
+ {lnk.label}
+
+ ))}
) : null}
diff --git a/frontend/packages/agent/src/types.ts b/frontend/packages/agent/src/types.ts
index e5487757..390fee76 100644
--- a/frontend/packages/agent/src/types.ts
+++ b/frontend/packages/agent/src/types.ts
@@ -28,11 +28,10 @@ export interface AgentState {
paused: boolean;
inbox: InboxRow[];
links: AgentLink[];
- forge_public_url: string | null;
}
// Mirrors `hive_agent::web_ui::state::AgentLink` β one meta-nav entry
-// in the agent page header (stats/screen/forge/config + any
+// in the agent page header (stats/screen + any
// `services.hyperhive.agent.dashboardLinks` extras). `agent_links()` on the backend
// is the single source of truth for what links an agent exposes; see
// docs/2-handbook/agents/web-page.md::Header for the kind β URL resolution table.
@@ -40,7 +39,7 @@ export interface AgentLink {
url: string;
icon: string;
label: string;
- kind: "container" | "forge" | "external";
+ kind: "container" | "external";
}
export interface TokenUsage {
diff --git a/frontend/packages/dashboard/src/agents.js b/frontend/packages/dashboard/src/agents.js
index 33da89a9..9344ba65 100644
--- a/frontend/packages/dashboard/src/agents.js
+++ b/frontend/packages/dashboard/src/agents.js
@@ -270,8 +270,7 @@ function containerRowFingerprint(
// changed (keyed cache), skipping the build + async dashboard-state
// fetch for stable rows.
function buildContainerLi(c, opts) {
- const { pending, opRunning, selected, url, containerBase, forgeBase, s } =
- opts;
+ const { pending, opRunning, selected, url, containerBase, s } = opts;
// A single `pending-running` class covers any active transient
// badge state (see the pending-badge derivation comment in
// renderContainers).
@@ -342,8 +341,7 @@ function buildContainerLi(c, opts) {
// `GET /api/dashboard-state` (via gateway when enabled, direct
// TCP otherwise). The agent is the single source of truth for its
// link list: stats / screen (GUI agents only β c0re's disk-based
- // fallback cannot detect this) / forge profile / agent-configs /
- // extras. DOM-built β link strings come from the agent's process
+ // fallback cannot detect this) / extras. DOM-built β link strings come from the agent's process
// and must never reach the HTML parser.
const navStrip = el("span", { class: "nav-strip" });
head.append(navStrip);
@@ -363,15 +361,13 @@ function buildContainerLi(c, opts) {
// -- nav strip -----------------------------------------------
if (Array.isArray(ds.links)) {
for (const lnk of ds.links) {
- // No public forge URL to link to β hide rather than build
- // a broken/guessed href (see forgeBase above).
- if (lnk.kind === "forge" && !forgeBase) continue;
+ // An agent not yet rebuilt still sends forge links, which no
+ // page links to any more.
+ if (lnk.kind === "forge") continue;
const href =
- lnk.kind === "forge"
- ? forgeBase + (lnk.url || "")
- : lnk.kind === "external"
- ? lnk.url || ""
- : /* container */ containerBase + "/" + (lnk.url || "");
+ lnk.kind === "external"
+ ? lnk.url || ""
+ : /* container */ containerBase + "/" + (lnk.url || "");
const a = el("a", {
class: "nav-link",
href,
@@ -634,12 +630,6 @@ export function renderContainers(s) {
// loopback port while the socket marker is absent). See
// `docs/2-handbook/hives/dashboard.md::Container row` + `docs/4-implementation/networking/gateway.md::Vhost map`.
const gatewayLinks = !!(s && s.gateway_enabled);
- // Forge public URL: state.forge_public_url (set by the NixOS module
- // from services.hyperhive.swarm.forge.publicUrl) or null β never guessed
- // from ":3000". `null` propagates down to
- // buildContainerLi/buildAgentMenu, which already hide forge links
- // rather than emit a possibly-wrong one.
- const forgeBase = (s && s.forge_public_url) || null;
const ul = existingUl ?? el("ul", { class: "containers" });
const rows = sortedContainerRows(containers);
@@ -691,7 +681,6 @@ export function renderContainers(s) {
selected,
url,
containerBase,
- forgeBase,
s,
});
containerRowCache.set(c.name, { el: li, fingerprint: fp });
diff --git a/hive-agent/src/web_ui/state.rs b/hive-agent/src/web_ui/state.rs
index e9daa204..7713d448 100644
--- a/hive-agent/src/web_ui/state.rs
+++ b/hive-agent/src/web_ui/state.rs
@@ -47,10 +47,7 @@ pub(super) async fn api_state(State(state): State) -> axum::Json,
- /// Public URL of the forge (e.g.
- /// `"https://forge.pr1ma.darkest.space"`). Sourced from
- /// `HIVE_FORGE_PUBLIC_URL` (set from
- /// `services.hyperhive.swarm.forge.publicUrl`); `None` when unset. The frontend uses this to build
- /// forge nav-strip links, and **hides** the forge link entirely
- /// when absent rather than guessing `:3000`.
- forge_public_url: Option,
/// Human name of this hive instance (e.g. `"pr1ma"`). Sourced
/// from `HYPERHIVE_HIVE_NAME`; `None` when unset. The frontend
/// uses this for the page `` and header label so browser
@@ -190,7 +180,7 @@ pub(super) struct StateSnapshot {
/// of truth for what links an agent exposes.
#[derive(Serialize)]
struct AgentLink {
- /// `kind = Container | Forge` β path; `kind = External` β full URL.
+ /// `kind = Container` β path; `kind = External` β full URL.
/// The frontend prepends the right base before rendering.
url: String,
icon: String,
@@ -209,10 +199,6 @@ enum AgentLinkKind {
/// `/screen`). Agent page: same-origin path. Dashboard:
/// `http://:`.
Container,
- /// `url` is a path on the local Forgejo (`/