Watch
0
0
Fork
You've already forked hyperhive
0

github: PATs live in swarm bao; the agent fetches them itself

An operator links an agent's GitHub personal access token in the swarm UI
(LinkGithubAccountForm, "link github account" on /agents). swarm-controller's
PUT /api/hives/{hive}/agents/{agent}/github-account stores it at
swarm/agents/<agent>/github-token (swarm_secret_client::github), a flat leaf
under the agent's prefix that the agent's existing read grant already covers:
no policy change, and no list grant, since there is one token per agent.

In the agent, hive-agent-github-token (oneshot + 2-minute timer, as the agent
user, under its own store certificate, ordered before hive-github-notify)
reads that path and writes <state>/github-token, 0600 and agent-owned, the
file the gh wrapper, git credential helper and hive-github-notify already
read. It replaces the file by rename only when the bytes changed and never
deletes it: a hive-written github-token stays until a token is linked in the
swarm UI. It is installed only with a store address and
services.hyperhive.agent.github.enable.

Removed: the dashboard's CR3D3NTIALS page (credentials.html/js/css, its
build entries and H0M3 tile; GITHUB was its only tab), hive-c0re's
dashboard/matrix_accounts.rs with GET/POST /api/github-account,
priv_client::write_agent_github_token, the host socket's
SetAgentGithubToken and `hivectl github set-token`, and hive-priv's
WriteAgentGithubToken with write_agent_state_file, its only caller gone.

Docs: integrations/github.md and swarm/ui.md describe the swarm path,
swarm/credentials.md gains the store-path row, and the hive UI docs,
hivectl docs and security.md's hive-priv table drop the removed pieces.

Closes #4347
This commit is contained in:
atlas 2026-10-02 17:48:27 +02:00
commit 8e23feb01b
41 changed files with 804 additions and 846 deletions

View file

@ -49,6 +49,7 @@ import { type TableColumn } from "../../ui/table/Table.js";
import { CreateAgentForm } from "../CreateAgentForm.js";
import { LinkMatrixAccountForm } from "../LinkMatrixAccountForm.js";
import { LinkForgeAccountForm } from "../LinkForgeAccountForm.js";
import { LinkGithubAccountForm } from "../LinkGithubAccountForm.js";
import { WantedMenu } from "./WantedMenu.js";
import "./AgentsPage.css";
@ -98,6 +99,8 @@ const VIEW_MODE_KEY = "swarm-ui:agents:view-mode";
export function AgentsPage() {
const [rows, setRows] = useState<AgentRow[] | null>(null);
// The row showing the "link a github account" dialog, same shape.
const [githubTarget, setGithubTarget] = useState<AgentRow | null>(null);
const [error, setError] = useState<ProblemDetails | null>(null);
const [intervalMs, setIntervalMs] =
useState<RefreshIntervalMs>(DEFAULT_INTERVAL_MS);
@ -600,6 +603,22 @@ export function AgentsPage() {
: "no hive on record for this agent — nothing to link against"
}
/>
<Badge
variant="quiet"
icon={<LinkIcon />}
value="link github account"
onClick={
detailTarget.hive
? () => setGithubTarget(detailTarget)
: undefined
}
disabled={!detailTarget.hive}
title={
detailTarget.hive
? `link a github account to ${detailTarget.name}`
: "no hive on record for this agent — nothing to link against"
}
/>
</div>
{/* MVP scope per mara's own ruling: a small read-only
preview, no header/no input — the full terminal
@ -651,6 +670,20 @@ export function AgentsPage() {
/>
) : null}
</Dialog>
<Dialog
open={githubTarget !== null}
onClose={() => setGithubTarget(null)}
label="link a github account"
plain
>
{githubTarget?.hive ? (
<LinkGithubAccountForm
hive={githubTarget.hive}
agent={githubTarget.name}
onClose={() => setGithubTarget(null)}
/>
) : null}
</Dialog>
<ConfirmDialog
open={confirmTarget !== null}
label={confirmTarget ? CONFIRM_COPY[confirmTarget.state].label : ""}