Git smart-HTTP proxy and artifact routes for token-authenticated agents (SPEC §7.1.1) #11

Manually merged
krisbuild merged 1 commit from git-proxy into main 2026-09-27 19:23:29 +02:00
Owner

Agents on other networks no longer need the forge token or any /api route beyond the artifacts they are owed (SPEC §7.1.1: "routes authenticated by an agent token").

Git proxy (src/git_proxy.rs)

  • Read-only smart-HTTP proxy at /git/{owner}/{repo}/info/refs and .../git-upload-pack. It forwards to the forge with the control plane's own token and streams both ways. Protocol v2 passes through.
  • Refused: receive-pack, dumb HTTP, other paths, and requests without a valid agent token.
  • Authorization: the repo must be managed (match a repos pattern), and the token's node must hold an assigned or running instance of a graph for that repo.

Agent side

  • The assignment gains an optional git_proxy. It is wire-compatible and leaves def_hash unchanged.
  • The agent's mirror fetch goes through <control plane>/git/.... The bearer token is passed only through GIT_CONFIG_* env on that one fetch. The mirror key and the checkout's origin stay the real clone URL.
  • Option --git-proxy (NixOS gitProxy) defaults to on when a token is configured.

Artifacts on the agent listener

  • GET /api/artifacts/{hash}: only for a hash among the resolved inputs of the node's live instances.
  • HEAD and PUT: only while the node holds live work.
  • Nothing else under /api is served there (tested).

Also

  • The control-plane URL prefix is now kept when deriving the HTTP base, e.g. wss://h/p/agent → https://h/p.

Known gaps are in TODO: private forge-hosted flake inputs fetched by nix during eval, a job's own git fetch origin, uploads not bound to a specific instance, and streams outliving revocation.

🤖 Generated with Claude Code

Agents on other networks no longer need the forge token or any `/api` route beyond the artifacts they are owed (SPEC §7.1.1: "routes authenticated by an agent token"). **Git proxy** (`src/git_proxy.rs`) - Read-only smart-HTTP proxy at `/git/{owner}/{repo}/info/refs` and `.../git-upload-pack`. It forwards to the forge with the control plane's own token and streams both ways. Protocol v2 passes through. - Refused: `receive-pack`, dumb HTTP, other paths, and requests without a valid agent token. - Authorization: the repo must be managed (match a `repos` pattern), and the token's node must hold an `assigned` or `running` instance of a graph for that repo. **Agent side** - The assignment gains an optional `git_proxy`. It is wire-compatible and leaves `def_hash` unchanged. - The agent's mirror fetch goes through `<control plane>/git/...`. The bearer token is passed only through `GIT_CONFIG_*` env on that one fetch. The mirror key and the checkout's `origin` stay the real clone URL. - Option `--git-proxy` (NixOS `gitProxy`) defaults to on when a token is configured. **Artifacts on the agent listener** - `GET /api/artifacts/{hash}`: only for a hash among the resolved inputs of the node's live instances. - `HEAD` and `PUT`: only while the node holds live work. - Nothing else under `/api` is served there (tested). **Also** - The control-plane URL prefix is now kept when deriving the HTTP base, e.g. `wss://h/p/agent` → `https://h/p`. Known gaps are in TODO: private forge-hosted flake inputs fetched by nix during eval, a job's own `git fetch origin`, uploads not bound to a specific instance, and streams outliving revocation. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Agent-token routes: read-only git proxy and artifacts on the agent listener
All checks were successful
krisbuild/kris/krisbuild/nix/workspace-deps cached
krisbuild/kris/krisbuild/nix/hello cached
krisbuild/kris/krisbuild/nix/world cached
krisbuild/kris/krisbuild/nix/clippy succeeded
krisbuild/kris/krisbuild/nix/build succeeded
krisbuild/kris/krisbuild/nix/kb-check succeeded
krisbuild/kris/krisbuild/nix/test succeeded
krisbuild/kris/krisbuild krisbuild kris/krisbuild: all tasks succeeded
krisbuild/queue merged
1ef9486ba5
A remote node now needs exactly one credential, its agent token (SPEC §7.1.1).

Control plane:
- /git/<owner>/<repo>.git serves git smart HTTP upload-pack only (info/refs
  and the RPC, protocol v0 and v2), relayed to the forge under the control
  plane's own forge token. Bodies stream both ways; no total deadline, a
  600s read timeout, redirects off. receive-pack, dumb HTTP and anything
  else is a 403. Served on both listeners, a token always required.
- A repo is served only if it matches a `repos` policy and the token's node
  holds an assigned/running instance of a graph of that repo, so the proxy
  is no read oracle for what the bot can see.
- Assignments carry `git_proxy = "<owner>/<repo>"` (serde default, not in
  def_hash) when KB_CLONE_URL is exactly that repo on the configured forge.
- /api/artifacts/{hash} on the agent listener: token required; GET only for
  resolved inputs of the node's live instances, HEAD/PUT only while it holds
  one. The main listener keeps its LAN rules and also accepts an agent token
  for PUT.

Agent:
- With a token (default on, `--git-proxy false` opts out) the mirror fetch
  goes through the proxy; the bearer rides GIT_CONFIG_* http.extraHeader on
  that one git process only. Mirror key and `origin` stay the real clone
  URL; ssh clones are untouched.
- Artifact requests send the agent token when there is one.
- The control-plane HTTP base keeps a proxy prefix in front of `/agent`.

NixOS module: services.krisbuild.agent.gitProxy.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Author
Owner

Reviewed: the proxy only serves upload-pack, validates repo names before building the upstream URL, never forwards the agent bearer upstream, and gates on live work for that repo. Artifact routes on the agent listener are scoped to live assignments. CI green.

@krisbuild r+

Reviewed: the proxy only serves upload-pack, validates repo names before building the upstream URL, never forwards the agent bearer upstream, and gates on live work for that repo. Artifact routes on the agent listener are scoped to live assignments. CI green. @krisbuild r+
krisbuild manually merged commit e720a5b5d6 into main 2026-09-27 19:23:29 +02:00
Collaborator

Merged as e720a5b5d6.

Merged as e720a5b5d60e.
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
kris/krisbuild!11
No description provided.