refactor(#1970): replace githubAccount option with hyperhive.github.enable gate (default true, github.com + x-access-token)
This commit is contained in:
parent
3e5aff39eb
commit
af9b46e80a
1 changed files with 44 additions and 90 deletions
|
|
@ -17,12 +17,12 @@ let
|
|||
# from `userName` to keep them coupled.
|
||||
userName = config.hyperhive.user.name;
|
||||
homeDir = "/home/${userName}";
|
||||
# GitHub account (hyperhive.githubAccount): a `gh` wrapper + a git
|
||||
# GitHub integration (hyperhive.github.enable): a `gh` wrapper + a git
|
||||
# credential helper, both sourcing the PAT from HIVE_GITHUB_TOKEN_FILE at
|
||||
# invocation time so a dashboard-pasted token takes effect with no rebuild.
|
||||
# The scripts are static (they read env at runtime) — the account option
|
||||
# only gates whether they're installed + the env is set, so the token
|
||||
# value never enters the nix store.
|
||||
# The scripts are static (they read env at runtime) — the enable flag only
|
||||
# gates whether they're installed + the env is set, so the token value never
|
||||
# enters the nix store. github.com only; git auths as `x-access-token` + PAT.
|
||||
ghWrapper = pkgs.writeShellScriptBin "gh" ''
|
||||
if [ -n "''${HIVE_GITHUB_TOKEN_FILE:-}" ] && [ -r "''${HIVE_GITHUB_TOKEN_FILE}" ]; then
|
||||
GH_TOKEN="$(cat "''${HIVE_GITHUB_TOKEN_FILE}")"
|
||||
|
|
@ -34,7 +34,9 @@ let
|
|||
# git credential-helper protocol: only the `get` action needs an answer.
|
||||
[ "''${1:-}" = "get" ] || exit 0
|
||||
if [ -n "''${HIVE_GITHUB_TOKEN_FILE:-}" ] && [ -r "''${HIVE_GITHUB_TOKEN_FILE}" ]; then
|
||||
printf 'username=%s\n' "''${HIVE_GITHUB_USER:-x-access-token}"
|
||||
# GitHub ignores the username for PAT auth — `x-access-token` is the
|
||||
# conventional placeholder; the PAT is the password.
|
||||
printf 'username=x-access-token\n'
|
||||
printf 'password=%s\n' "$(cat "''${HIVE_GITHUB_TOKEN_FILE}")"
|
||||
fi
|
||||
'';
|
||||
|
|
@ -645,73 +647,28 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
options.hyperhive.githubAccount = lib.mkOption {
|
||||
type = lib.types.nullOr (
|
||||
lib.types.submodule {
|
||||
options = {
|
||||
username = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "the-sword-above";
|
||||
description = ''
|
||||
GitHub login the agent acts as. Used as the username for
|
||||
`git push` over HTTPS and shown to the agent via the
|
||||
`HIVE_GITHUB_USER` environment variable. Should be a
|
||||
dedicated bot account, never a human's.
|
||||
'';
|
||||
};
|
||||
tokenFile = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
example = "/agents/damocles/state/github-token";
|
||||
description = ''
|
||||
Path to the file holding this account's personal access
|
||||
token (PAT). The token *value* is never in nix --- the
|
||||
provisioner (dashboard credentials tab, or `hivectl`)
|
||||
writes an operator-supplied PAT here (0600, agent-owned),
|
||||
the same contract as `matrixAccounts.<name>.tokenFile`.
|
||||
The `gh` wrapper and the git credential helper read the
|
||||
token from this path at invocation time, so a PAT pasted
|
||||
mid-session takes effect with no rebuild. `gh` / `git push`
|
||||
simply fail unauthenticated until the file exists.
|
||||
|
||||
Keep the PAT minimally scoped (only the repos/scopes the
|
||||
agent's workflow needs): the agent has passwordless sudo,
|
||||
so a compromised agent can act as the account within the
|
||||
token's scopes --- scope is the real blast-radius limiter.
|
||||
'';
|
||||
};
|
||||
host = lib.mkOption {
|
||||
type = lib.types.str;
|
||||
default = "github.com";
|
||||
example = "github.example.com";
|
||||
description = ''
|
||||
GitHub host. Defaults to `github.com`; set it for a GitHub
|
||||
Enterprise instance. Drives both the `gh` API host
|
||||
(`GH_HOST`) and the git credential helper's URL match.
|
||||
'';
|
||||
};
|
||||
};
|
||||
}
|
||||
);
|
||||
default = null;
|
||||
example = lib.literalExpression ''
|
||||
{
|
||||
username = "the-sword-above";
|
||||
tokenFile = "/agents/damocles/state/github-token";
|
||||
}
|
||||
'';
|
||||
options.hyperhive.github.enable = lib.mkOption {
|
||||
type = lib.types.bool;
|
||||
default = true;
|
||||
description = ''
|
||||
Give the agent a managed GitHub account: a `gh` CLI wrapper and a
|
||||
git credential helper, both authenticated from an operator-supplied
|
||||
PAT, so the agent can run `gh` API calls and `git push` to GitHub as
|
||||
the configured login without any manual `gh auth login` dance.
|
||||
Install the GitHub integration in this agent: a `gh` CLI wrapper and a
|
||||
git credential helper for `https://github.com`, both authenticated from
|
||||
an operator-supplied personal access token (PAT). The PAT is written to
|
||||
`<state>/github-token` out of band --- the dashboard credentials tab or
|
||||
`hivectl github set-token` --- so giving an agent GitHub is a runtime
|
||||
paste, no per-agent config or rebuild. The wrappers read the token file
|
||||
at invocation, so a freshly-pasted PAT takes effect immediately; until
|
||||
one exists, `gh` / `git push` just fail unauthenticated.
|
||||
|
||||
`null` (the default) leaves GitHub off entirely --- no `gh` wrapper,
|
||||
no credential helper, no env. When set, the token is supplied out of
|
||||
band (dashboard credentials tab / `hivectl`) into `tokenFile`; nix
|
||||
only carries the login + host, never the secret.
|
||||
github.com only. git authenticates as `x-access-token` + the PAT (GitHub
|
||||
ignores the username for PAT auth); `gh` derives its identity from the
|
||||
token. Keep the PAT minimally scoped: the agent has passwordless sudo, so
|
||||
a compromised agent can act within the token's scopes --- scope is the
|
||||
real blast-radius limiter.
|
||||
|
||||
Single account per agent by design (unlike `matrixAccounts`, which is
|
||||
multi-account): the GitHub workflow is "this agent is this one bot".
|
||||
On by default. Host-driven: set `services.hyperhive.github.enable = false`
|
||||
to turn the integration off hive-wide (meta.rs propagates the override
|
||||
into every agent).
|
||||
'';
|
||||
};
|
||||
|
||||
|
|
@ -1579,14 +1536,11 @@ in
|
|||
# regardless of which profile files are sourced.
|
||||
NIX_REMOTE = "daemon";
|
||||
}
|
||||
// lib.optionalAttrs (config.hyperhive.githubAccount != null) {
|
||||
# GitHub account: metadata + token-file PATH only, never the secret.
|
||||
# The `gh` wrapper + git credential helper read the PAT from the file
|
||||
# at invocation time (see hyperhive.githubAccount).
|
||||
HIVE_GITHUB_USER = config.hyperhive.githubAccount.username;
|
||||
HIVE_GITHUB_HOST = config.hyperhive.githubAccount.host;
|
||||
HIVE_GITHUB_TOKEN_FILE = config.hyperhive.githubAccount.tokenFile;
|
||||
GH_HOST = config.hyperhive.githubAccount.host;
|
||||
// lib.optionalAttrs config.hyperhive.github.enable {
|
||||
# GitHub integration: point the gh wrapper + git credential helper at the
|
||||
# agent's PAT file (written by the credentials tab / `hivectl github
|
||||
# set-token`). Only the path — never the secret (see hyperhive.github.enable).
|
||||
HIVE_GITHUB_TOKEN_FILE = "/agents/${userName}/state/github-token";
|
||||
}
|
||||
// lib.optionalAttrs (!config.hyperhive.autoCompact) {
|
||||
# Zero watermark disables proactive compaction; the reactive path
|
||||
|
|
@ -1705,25 +1659,25 @@ in
|
|||
# (view, pr, issue, comment, assign, close, labels, branches, etc.)
|
||||
(pkgs.callPackage ../packages/hive-forge-tools.nix { })
|
||||
]
|
||||
++ lib.optionals (config.hyperhive.githubAccount != null) [
|
||||
# gh wrapper + git credential helper for hyperhive.githubAccount.
|
||||
++ lib.optionals config.hyperhive.github.enable [
|
||||
# gh wrapper + git credential helper for hyperhive.github.enable.
|
||||
# (No bare pkgs.gh here — the wrapper *is* `gh` and hardcodes the real
|
||||
# binary path, so it can't be shadowed.)
|
||||
ghWrapper
|
||||
gitCredHelper
|
||||
];
|
||||
|
||||
# Wire the GitHub credential helper for `git push` over HTTPS to the
|
||||
# configured host. Host-scoped (github.com or a GHE host), so it never
|
||||
# touches the forge (localhost:3000) or any other remote. Gated on the
|
||||
# account; the helper reads the PAT from HIVE_GITHUB_TOKEN_FILE at
|
||||
# invocation (see hyperhive.githubAccount). System /etc/gitconfig merges
|
||||
# under the agent's ~/.gitconfig (safe.directory), so this is additive.
|
||||
environment.etc = lib.optionalAttrs (config.hyperhive.githubAccount != null) {
|
||||
# Wire the GitHub credential helper for `git push` over HTTPS. Host-scoped
|
||||
# to `https://github.com`, so it never touches the forge (localhost:3000)
|
||||
# or any other remote. Gated on hyperhive.github.enable; the helper reads
|
||||
# the PAT from HIVE_GITHUB_TOKEN_FILE at invocation and auths as
|
||||
# `x-access-token` + the PAT. System /etc/gitconfig merges under the agent's
|
||||
# ~/.gitconfig (safe.directory), so this is additive.
|
||||
environment.etc = lib.optionalAttrs config.hyperhive.github.enable {
|
||||
"gitconfig".text = ''
|
||||
[credential "https://${config.hyperhive.githubAccount.host}"]
|
||||
[credential "https://github.com"]
|
||||
helper = hive-github
|
||||
username = ${config.hyperhive.githubAccount.username}
|
||||
username = x-access-token
|
||||
'';
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue