Repository · workstreams
Workstream: GithubProxy
Status: Planned · Component: Maximize developer productivity · Parent: Git workstream
A deep-dive on one sub-area of the Git workstream, and a deliberately service-minimizing one. The starting question is not "what should a GitHub proxy service do?" but "how much of a GitHub proxy even needs to be a service, once W3Wallet can hold a secret and expose it as a use-without-disclose function?" The answer is: almost none of it. A GitHub token becomes a capability held in a wallet; "using GitHub" becomes invoking that capability; granting, attenuating, revoking, and auditing access become wallet operations. What remains service-shaped is a small, mostly generic residue. Like the rest of the plan, this describes the target state; the Current state section keeps it honest about what exists today.
How to read this document
- Goal · Motivating scenario — what we are building and the one example that defines "done."
- Current state — the wallet primitives that already exist, and what is left.
- How much of this even needs to be a service? — the core of the workstream: the collapse into the wallet, and the residue that survives it.
- The capability model — the add/view/use/remove split, as capabilities over a wallet-held key.
- Authority home · Design · What's left to build · Roadmap · Relationships · Graduation.
Goal
Make GitHub access a W3Wallet capability, not a hosted credential store:
- A GitHub API key lives in a wallet as the secret part of a capability — a personal access token, a fine-grained token, or a GitHub App's signing key. It is never an ambient credential baked into a server.
- "Using GitHub" is invoking a function the wallet runs with that secret embedded — open an issue, merge a PR — and getting back only the result. The key is asserted, never disclosed.
- Granting, attenuating, revoking, and auditing access are wallet operations, expressed as the four capabilities add / view / use / remove (plus a
managebundle) over that key. - The "service" is reduced to its irreducible residue — always-on hosting so a wallet-held key is reachable 24/7, aggregate metering for the one case the wallet can't see, and webhook ingress — and that residue is, deliberately, generic W3Wallet infrastructure plus GithubWatchman, not GitHub-specific server code.
- The
usepath is guarded against self-undermining actions. Beyond scoping which repositories and operations a grant may touch, the capability forbids by default the operation-classes that would let a holder defeat the controls that bound it. Two are first-class, and both matter most once an agent is the holder: ausegrant can never mutate a repository's CI configuration or weaken its required-status-check / branch-protection settings (so an agent can never disable CI to force-merge an otherwise-unmergeable change), and it can never open or merge a pull request that targets any branch other than the repository's default branch (always a mistake). Lifting either prohibition takes an explicit, single-conversation step-up grant authorized through the wallet — there is no other path, because every GitHub call flows through the gate and the holder cannot grant itself the exception. - The consumption surface is a drop-in
gh. The proxy is invoked through a CLI deliberately indistinguishable from GitHub'sgh— the same command and subcommand names, flags,--jsonfield names, exit codes, and human-readable output — so that an agent (or person) already fluent inghuses the proxy transparently, with no retraining and no awareness that it is the proxy: drop the binary onto the agent'sPATHasgh, and its existinggh issue create/gh pr mergemuscle memory now flows through the capability-gated, guardrailed proxy instead of holding a raw token. That CLI is its own repository (GithubProxyCli) per the standard layered architecture — a distinct client layer over the Api, never bundled into the server.
This delivers the Git workstream's "Why the GitHub proxy is a control point" properties — credential containment, fine-grained permissions, rate-limit enforcement, W3Wallet compatibility — by inheriting them from the wallet rather than re-implementing them in a bespoke service, and adds two agent-safety guardrails the single chokepoint makes structurally enforceable: CI-integrity protection (CI cannot be bypassed without approval) and default-branch-only PRs.
Motivating scenario — "manage in one wallet, use in another, never disclose"
The example that defines the workstream:
I have two wallets. Wallet A manages a GitHub API key — it can create the registration, view the secret, edit its scopes, and remove it. From Wallet A I grant Wallet B (or a separate profile) permission to use that key — open issues and merge PRs through it — but not to view the key. Wallet B acts as the key, sees the results of its GitHub calls, and never learns the token's bytes. Wallet A sees every use Wallet B makes and can revoke that grant at any time without rotating the key.
This is a pure wallet operation. It is W3Wallet's already-built Proxy capability (injects Authorization: Bearer <secret> inside the daemon — the caller never sees the key) or capability-JAR runtime (named GitHub functions wrapping an embedded secret), with the use authority handed from Wallet A to Wallet B over a delegation-by-reference chain so the secret stays home. GithubProxy is the canonical first consumer of that model with a real, valuable secret behind it.
The same grant also defines what Wallet B cannot do, no matter how it asks — the half of the scenario that matters most when B is an autonomous agent. B cannot touch the repository's CI configuration or its required checks, and cannot open a pull request against anything but the default branch. So if an agent holding B's grant tries to disable a failing check so its PR can merge, or to target a side branch, the authority home refuses; the only way past either refusal is for Wallet A to mint a one-time, this-conversation-only step-up grant — the agent cannot grant it to itself. "It must be impossible to bypass CI without approval, and a PR must never silently target a non-default branch" are therefore properties of the capability, not conventions an agent is trusted to honor.
Motivating incident — version-skewed gh surfaces fail silently (2026-07-05)
A second, field-proven motivation for the drop-in CLI, from the ContainerNursery-outage response (UrlResolver PR #695): an agent's CI monitor polled gh pr checks 695 --json name,bucket every 60 seconds — a surface every modern-gh-trained agent reaches for. The host's Debian-packaged gh 2.23.0 (2023-02-27) predates --json on pr checks (added upstream in v2.37.0, 2023-10), so every poll exited 1 with unknown flag: --json on stderr and empty stdout. The monitor's defensive wrapper — 2>/dev/null || echo "[]" — converted that hard contract mismatch into a well-formed empty result, so the loop emitted zero events for ~5 hours while both PR checks sat red; the human noticed the red PR before the agent did. Full forensics, exact reproduction, required drop-in behavior, and the acceptance tests that guarantee GithubProxyCli does not have the same problem are in GithubProxyCli #12.
The incident sharpens two requirements already implied by Goal 6:
- The drop-in CLI pins the modern
ghcontract, independent of host packaging. The whole failure class exists because "whichghsurface am I speaking?" varies by machine; a proxy CLI mounted asghanswers it once, everywhere —pr checks --json(fields andbucketvalues), exit-code semantics (0 all-passed / 8 pending / 1 otherwise), and tab-separated plain output must all match upstreamgh, verified by tests against fixture PRs (see the issue's test list). - Contract mismatches must fail loudly and identically to real
gh(exit 1,unknown flag:+ usage on stderr, empty stdout) — plausible-but-empty output is what turned a one-line error into five silent hours. This is the CLI-surface counterpart of the workstream's guardrail philosophy: make the wrong thing impossible to miss, not merely discouraged.
Current state — built vs. aspirational
Already built — and most of it is in the wallet, not the proxy:
- The use-without-disclose primitive exists in W3Wallet. The daemon's Proxy capability injects an API key into an outbound HTTP call inside the daemon, and the capability-JAR runtime (
installCapability(jar, factoryClass, secretData)→ aCapabilityRuntimewith namedinvoke(functionName, params)functions) lets arbitrary secret-wrapping code be exposed as a typed function surface — exactly the "helper functions that proxy an operation without revealing the key" this workstream needs. Owned-capability secrets already live in the issuer's daemon, encrypted at rest, exercised in place. - The GitHub-operation surface exists in the proxy.
url://githubproxy/is a three-repo service — GithubProxyApi (typed RPC), GithubProxyServerService (theurl://service), GithubProxyCli (agh-like CLI in its own repository, per the layered architecture) — exposing a focused subset (getMyself,listMyRepositories,getRepository,listIssues/getIssue/createIssue,listPullRequests/getPullRequest/closePullRequest/mergePullRequest,getUser,isConnected) — the starting point, not the target; full coverage is below. See the GithubProxy project doc.
Aspirational / not yet built (the substance of this workstream):
- Package the GitHub surface as a capability JAR — the focused GitHub logic, secret-free, runnable inside a wallet daemon's capability runtime, with the PAT (or App key) supplied as
secretData. The proxy's GitHub semantics become a wallet capability, not a server endpoint. - Host that capability always-on. Today the proxy holds GitHub credentials centrally and ambiently; the wallet-native model needs an always-on authority-home daemon so a wallet-held key is reachable 24/7 by CI and agents — a W3Wallet helper-daemon concern, not a bespoke vault.
- The add/view/use/remove capability split with
usedisjoint fromview(below) — partly expressible today (intra-wallet, on the Proxy cap) and partly gated on W3Wallet's cross-wallet delegation, which is itself aspirational (today's sharing is intra-wallet, cross-domain only). - Aggregate metering and audit for the cases the wallet can't see on its own (below).
- Full GitHub coverage, not a curated subset — today the proxy implements a focused set of operations; the target is complete
gh/GitHub-API coverage: the common operations typed for ergonomics plus a gatedgh api-style passthrough (raw REST/GraphQL) for everything else, so nothing an agent might call is missing. The blast radius is held by per-grant attenuation and the deny-by-default guardrails — every operation, passthrough included, runs the same gate and forbidden-class classifier — not by keeping the implemented surface small (below). - A drop-in
gh-indistinguishable CLI — today's GithubProxyCli mirrors a focused subset ofgh; the goal is a CLI an agent already trained onghcannot tell apart from the real thing (matching command/flag/--json/exit-code/output surface, and — with full coverage above —gh apitoo), so it can be mounted as the agent'sghand route every call through the proxy transparently (below).
The transport, the GitHub-operation surface, and the use-without-disclose primitive all exist. What is missing is gluing them together — GitHub-as-a-capability-JAR, hosted always-on, with the capability split and the residual aggregate concerns — not a new credential-storage subsystem.
How much of this even needs to be a service?
This is the workstream's defining analysis. Start by giving the wallet everything it can already do, and see what is left.
What collapses into the wallet
Hold the GitHub token as the secret part of a capability JAR (or Proxy cap) in a daemon, and the following stop being "service features" entirely — they are wallet primitives:
| Concern | In the wallet, it is… |
|---|---|
| Secret storage | the capability's private part, encrypted at rest in the owner's daemon |
| Use without disclosure | invoke(functionName, params) runs the GitHub call with the embedded secret; the caller gets only the result |
| Granting usage rights | minting/delegating the capability — intra-wallet today, cross-wallet via reference chains |
| Attenuation (this repo, this operation only) | per-hop attenuate-only restriction / macaroon caveats the capability JAR binds to GitHub semantics |
| Revocation, expiry, leases | reference caps revoke instantly; token caps use lease + blacklist |
| Audit | the authority home records every invocation; optionally streamed to the Event Log |
So GithubProxy reduces to two things: (1) a GitHub capability JAR — the only GitHub-specific code — and (2) an always-on wallet daemon to host it so the key is reachable when its holders (CI, agents) need it.
Even rate-limiting collapses — for reference capabilities
The one concern that seems to require a service is fair-sharing GitHub's per-token API budget across many independent users of the same key. The decisive observation: the boundary is not service-vs-wallet, it is reference-cap vs. token-cap.
- Reference capabilities route every invocation back through the single authority-home daemon. That daemon therefore sees every use of the token and can meter the aggregate against GitHub's per-token budget right there — modeled as a W3Wallet asset pool drained by per-grant rate-limited allowances. Even if fifty agents hold reference caps to one PAT, all fifty calls flow through one daemon that fair-shares them. No external coordinator is needed.
- Token capabilities are offline, bearer-presented — nobody sees the aggregate. This is the only case where metering a shared token must escape the wallet to a coordinator (the W3Wallet PMS).
Since the reference form is the right form for "use my GitHub key" anyway (live audit, instant revoke, secret-stays-home), rate-limiting lives in the wallet too for the common case.
What genuinely survives as a service — and it is generic, not GitHub-specific
What is left after the collapse is real but small, and notably none of it is GitHub-specific:
- Always-on hosting / managed multi-tenancy. Reference caps need the authority home reachable at use time; a user without their own 24/7 daemon needs someone to host one — W3Wallet's helper-daemon / hosted-daemon concern, offered as a managed option by HostedW3WalletService — identical for any secret, not a GitHub feature.
- Token-cap aggregate metering at scale — the W3Wallet PMS, again generic.
- Webhook ingress — a stable public HTTPS endpoint for GitHub to POST events to. That is GithubWatchman's job and is separable from "proxy outbound API calls."
Conclusion: GithubProxy = a GitHub capability JAR + a tenant of generic always-on wallet hosting (+ webhooks via GithubWatchman). The bespoke "GitHub proxy server with its own token vault and ACL system" largely ceases to exist; its security-sensitive parts become wallet capabilities, and its operational parts become generic hosting.
One honest caveat — this is reuse, not a security upgrade
Putting the PAT in an embedded, shared, always-on daemon is not more secure than a bespoke vault would be: a multi-tenant always-on home holding everyone's real GitHub tokens is still a high-value target, and "the secret stays on the user's own device" is exactly what is surrendered when the authority home is a hosted service. The win is not reinventing grant/revoke/audit/use-without-view and getting a uniform model across the platform — not stronger isolation per se. The strongest-isolation deployment is still "the key lives in the user's own daemon and the hosted side holds only a reference" — at the cost of 24/7 availability unless the user runs their own helper. The workstream should make that trade-off explicit to operators rather than implying the wallet makes the secret safer by magic.
The capability model
The unit of authority is a key registration: a named record binding a GitHub credential (held as a capability's secret part) to a set of allowed GitHub scopes. It has a public part (id, name, owner principal, reachable account/scopes, timestamps, status) and a private part (the token), held only at the registration's authority home.
Authority over it is split into four independent W3Wallet capabilities, each held, delegated, attenuated, and revoked on its own:
| Capability | What it confers | Sees the token? | Typical holder |
|---|---|---|---|
Add (register) |
Create a registration — supply a token, name it, declare allowed scopes. A service-level authority to mint registrations. | Supplied once at registration; never returned | The managing wallet |
View (reveal) |
Read a registration's plaintext token and secret metadata. The sensitive one — segregated precisely so it can be withheld. | Yes | The managing wallet only |
Use (invoke) |
Invoke the GitHub functions through the registration. Attenuable to repos, operations, a branch target, a rate allowance, and a lease — and deny-listing CI-configuration mutations and non-default-branch PR targets by default (see Design → forbidden-by-default classes). | No — never | Any delegate (other wallet / profile / agent) |
Remove (delete) |
Delete or disable a registration (cascade-revoking grants minted from it). | No | The managing wallet |
Plus manage (administer) — the owner's governance bundle: rotate the token, edit allowed scopes, list and revoke outstanding use grants, read the audit log (the "edit" in "create/view/edit"). Manage implies neither disclosure (view) nor the ability to act as the key (use).
The load-bearing invariant: use is disjoint from view. Holding use lets you exercise the token; it grants no path to read it — because the use-path is a capability invoke that returns only results and never serializes the secret. "Grant a wallet permission to use a key but not view it" is therefore a structural property of two different capabilities over the same registration, not a policy convention.
Walking the scenario through the model
- Wallet A holds
add; it callsregister("ci-bot", token="ghp_…", scopes=[repo, workflow]). A now holdsview+manage+remove+use. - Wallet A grants Wallet B a
usecapability, attenuated torepo = CodexCoder21Organization/*,operations = {createIssue, mergePullRequest},rate ≤ 60/hr,lease = 1w. By default the grant also carries the two standing guardrails everyusegrant has — no CI-configuration mutation and PR target = the repo's default branch only — neither of which A bothered to spell out because they are on unless explicitly lifted. The grant carries no token — it is a reference into the registration's authority home. - Wallet B invokes
mergePullRequest("CodexCoder21Organization/repo", 42). The authority home verifies the capability, checks attenuation (repo? op? rate left? lease live? not a forbidden class?), runs the GitHub call with the embedded secret, and returns only the result. - Wallet A sees the use and can revoke B's grant instantly — without rotating the key, without affecting other delegates.
- Wallet B tries to defeat its own bounds — disable a red required check so PR 42 will merge, or open a PR with
base = release/9. Both are refused at the gate before the token is touched, because each falls in a deny-listed class. The only way through is for Wallet A to authorize a one-time step-up grant scoped to that single conversation; absent it, B simply cannot — the prohibition is not B's to relax.
Near-term, steps 2–4 work within one wallet across two profiles on today's Proxy capability; the two-distinct-wallets form lights up when W3Wallet's cross-wallet delegation lands. The model is identical either way — only the holder of use moves from a sibling profile to a separate wallet.
Authority home — where the key actually lives
A registration's authority home holds the token, runs the GitHub call, logs it, and can revoke. Following the self-hosted-first principle, the placements are ordered by preference: the GitHub key should live on infrastructure the user already controls, and reach a centralized service only as a last resort.
- Embedded / nested in infrastructure you already run (preferred — and the agent-swarm default). Because the GitHub logic is an Embedded (the capability JAR) with the ServiceServer only a thin
url://shell over it, a parent the user controls can stand up the proxy as a nested child and supply the key locally — the "a parent can spin up a child and control its access" pattern. The canonical case is AiCliHostSupervisor: the agent-hosting infrastructure the user controls holds the GitHub key (in the supervisor-owner's own daemon), embeds GithubProxy as a nested service, mounts the drop-inghinto the guest conversation, and provisions the guest only an instance-scopedusecapability that auto-revokes on reap. The key never leaves the user's infrastructure and no third party sits in the path — the supervisor manages the key, the agent only ever gets results. - Your own standalone Subservice. Run your own GithubProxy ServiceServer standalone (P2P, no coordinator required) as a long-lived authority home on your own box/VPS, and grant
useto your wallets and agents. Keys stay on your hardware; you own its uptime. This is the self-hosted Subservice form. - Centralized / hosted GithubProxy (convenience fallback — the honest caveat). A managed instance holds the real GitHub keys so consumers can be granted access without ever holding a token — which is exactly what makes it useful and exactly what makes it a high-value target: centralized credential storage whose breach exposes many users' tokens at once. It is for users who cannot run an always-on home of their own, documented as a fallback rather than a goal, and hardened by holding references into the user's own daemon wherever the user has one (the hosted side then holds no token), and by preferring GitHub App keys so even a resident secret only yields short-lived installation tokens. The residual exposure — the operator's own infrastructure is inside the trust boundary — is the W3Wallet hosted-authority-home trade-off, hardened there (e.g. confidential computing), not re-solved here.
In every placement the use-path contract is identical: results in, token never out.
Design — enforcement, attenuation, metering, audit, revocation
Almost all of this runs inside the daemon; the proxy/hosting layer mostly forwards:
-
Turnkey enforcement. Every GitHub function invocation is gated by W3Wallet's reusable gate: identify the principal, obtain its capability for the registration, verify it is the required one (
useto invoke,viewto reveal,manageto govern,removeto delete), enforce the allowance, emit audit. GithubProxy consumes the gate — it does not hand-roll auth — making it a reference adoption of W3Wallet's "auth is inherited." -
Attenuation. A
usegrant is restricted as it is delegated — by repository, operation, branch target (defaulting to the repo's default branch), rate, and lease — and sub-delegation may only narrow, never amplify. The wallet enforces this generically while staying domain-blind (the GitHub-specific structure lives only in the JAR). Two constraints matter: a grant can only ever attenuate below what the registered credential actually permits (never imply more), and thegh apipassthrough is attenuated exactly like a typed operation — there is no less-scrutinized path to GitHub. -
Forbidden-by-default classes and step-up grants. Two operation-classes are denied to every
usegrant unless explicitly unlocked, because they would let a holder defeat the controls that bound it — exactly the moves an agent reaches for when its change won't merge:- CI integrity. Mutating a repository's CI configuration or weakening its required-status-check / branch-protection settings — whether expressed as editing
.github/workflows/*through a commit/PR, as repo-settings / ruleset / branch-protection API calls, or as an admin/bypass merge that lands a PR over a red or pending required check. The standing rule is CI cannot be bypassed; the testing standard already enforces the consuming side of this ("never request removal of the check from branch protection"), and this makes it a capability boundary rather than a request an agent could simply not honor. - Non-default-branch PR targets. Opening or merging a pull request whose
baseis anything other than the repository's default branch — always a mistake absent a deliberate reason, so denied unless the grant'sbasecaveat was explicitly widened.
The authority home refuses both for an ordinary
usegrant. Lifting a prohibition requires a step-up grant — a one-time, single-conversation capability the managing wallet authorizes through W3Wallet; AiCliHostSupervisor provisions precisely this kind of just-in-time, per-conversation capability into a live agent session. Because all GitHub access flows through the gate and an agent holds only the capabilities it was handed, it cannot grant itself the missing authority — so "CI cannot be bypassed, and a PR cannot target a non-default branch, without explicit per-conversation approval" is a structural property of the capability, not a lint rule a misbehaving agent could route around. This holds for thegh apipassthrough too — a raw request that would weaken CI is recognized and refused exactly like a typed call; full API coverage is safe only because the passthrough cannot dodge it. - CI integrity. Mutating a repository's CI configuration or weakening its required-status-check / branch-protection settings — whether expressed as editing
-
Metering. GitHub's per-token budget is a W3Wallet asset pool drained by per-grant rate-limited allowances. For reference caps the authority-home daemon is the single meter (it sees all use); for offline token caps the PMS coordinates.
-
Audit. The authority home records each invocation (who, registration, operation, when, outcome), readable by
manageand optionally streamed to the Event Log. -
Revocation & rotation. Reference grants revoke instantly; token grants use lease + bounded blacklist.
removecascades to grants; rotation invalidates the old token without disturbing liveusegrants — they keep working against the new token because they never held the old one. Leases/expiry assume a reasonably-synced clock — see HierarchicalClock.
What's left to build
The wallet-native framing changes what the existing repos become:
- The GitHub capability JAR (new, the only GitHub-specific deliverable) — today's GithubProxyApi operations, secret-free, packaged for
installCapability(jar, factoryClass, secretData). It is a plugin for a generic, GitHub-blind wallet runtime: the wallet loads and runs it under a SandboxJVM (SJVM) sandbox so the plugin sees only its own token and reaches onlygithub.com— never another capability's secret in the same daemon, nor the host. It ships a typed catalog of its functions and caveat dimensions (repo,op,base, …) so the wallet attenuates and the coordinator UI renders the permission list with no GitHub-specific code anywhere but the JAR. It grows toward full GitHub coverage — typed common operations plus a genericgh apipassthrough that forwards arbitrary REST/GraphQL under the embedded token (within the JAR'sgithub.com-only egress), every such request run through the same attenuation and forbidden-class classifier before the token is touched. - GithubProxyServerService — reframed from "token vault + GitHub server" to an always-on wallet-daemon host that installs the GitHub capability JAR (running it sandboxed under SJVM) and resolves references; the only authority-home it needs to hold is references, not raw tokens (raw tokens are resident only in the centralized hosted instance, and even there as sandboxed capability JARs — never a bespoke token vault).
- GithubProxyCli — kept as its own repository (the layered architecture's distinct CLI layer: a client over GithubProxyApi that connects to the host over
url://and presents the user's W3Wallet capabilities, never folded into the Api or server), with two faces. (1) A drop-inghface: commands, subcommands, flags,--jsonfield names, exit codes, and human-readable output matched to GitHub'sghclosely enough that an agent trained onghcannot tell the difference — so it can be installed asghon the agent'sPATH(mounted into the conversation container as AiCliHostSupervisor mounts a granted tool) and the agent's existingghusage routes transparently through the capability-gated, guardrailed proxy. The binary holds no token and has no direct GitHub egress — everything, includinggh api, is the proxy's gated passthrough, so with full coverage the "unsupported command" case is rare; where one still arises the CLI fails the wayghfails rather than silently degrading or finding an ungated path to GitHub. (2) Wallet-management subcommands —register,grant,revoke,list-keys,list-grants— for the human operatingmanage/add, kept under a dedicated namespace (e.g.gh wallet …) so they can never collide with a current or future realghcommand. Authentication needs nogh auth login— a principal is pre-granted its capability, so auth just works; that is the one place the drop-in deliberately (and invisibly) diverges from realgh. - WUI (candidate) — a "Sign in with W3Wallet" surface to register keys, see grants and live usage, and revoke: the human face of
manage. - Reuse, not rebuild — hosting always-on, PMS metering, and recovery come from the W3Wallet workstream; webhook ingress from GithubWatchman. This workstream should resist re-implementing any of them GitHub-specifically.
Plan / roadmap
These are proposed and need sharpening before work starts.
- [ ] Package GitHub as a capability JAR. Move the GithubProxyApi surface into a secret-free capability JAR exposing named GitHub functions, taking the token as
secretData; proveinvokeruns the call without disclosing the key. - [ ] Host it in an always-on daemon. Stand up the authority-home daemon (the reframed GithubProxyServerService) that installs the JAR and serves invocations 24/7 — reusing W3Wallet helper/hosted-daemon hosting, not a bespoke store.
- [ ] The four-capability split, intra-wallet first. Implement
add/view/use/remove(+manage) over a registration, demonstrating the two-profiles-one-wallet "use but not view" on today's Proxy capability and provinguse⊥viewend-to-end. - [ ] Reference-form delegation + in-daemon metering. Cross-wallet
usegrants via reference chains (when W3Wallet delegation lands), with the authority home metering GitHub's budget as an asset pool — the full two-distinct-wallets scenario, rate-limited in-wallet. - [ ] Token caps + PMS for scale/offline. Where offline or high-fan-out use is needed, issue token caps and coordinate aggregate metering/blacklist via the W3Wallet PMS.
- [ ] Attenuation, audit, rotation. Repo/operation/branch caveats bound to GitHub semantics; per-registration audit readable by
manage; rotate-without-disrupting-use-grants. - [ ] Self-undermining-operation guardrails + step-up grants. Deny CI-configuration / required-check / branch-protection mutations and non-default-branch PR targets to every
usegrant by default; make lifting either a one-time, single-conversation step-up capability the managing wallet authorizes through W3Wallet (the form AiCliHostSupervisor provisions into a conversation). Settle how the daemon recognizes the full "CI integrity" class through the proxy (workflow-file edits, settings/ruleset API calls, bypass merges — including via the rawgh apipassthrough) and keeps that recognition exhaustive as GitHub adds new mechanisms, and how a step-up grant is bound to exactly the conversation that requested it. - [ ] Full GitHub coverage behind the gate. Take the proxy from a focused subset to complete coverage — common operations typed, plus a generic
gh api-style passthrough (raw REST/GraphQL) for the rest — and prove the safety property that matters: every operation, passthrough included, is decomposed into caveat dimensions and run through the forbidden-class classifier, so there is no ungated or unclassified path to GitHub. Safety rests on attenuation + the deny-by-default guardrails, not on a small surface. - [ ] Drop-in
gh-compatible CLI. Grow GithubProxyCli'sgh-mirroring face until it matchesgh's command/flag/--json/exit-code/output surface closely enough to substitute forghon an agent'sPATH(includinggh api, given full coverage above) — kept as its own CLI-layer repository per the layered architecture. Prioritize the commands agents actually emit, make any still-unsupported command fail the wayghfails rather than degrading or finding an ungated path to GitHub, and keep the wallet-management verbs under a dedicated namespace (e.g.gh wallet …) so they can't collide withgh. - [ ] End-to-end tests per the testing standards: a real always-on daemon hosting the GitHub capability JAR and a real W3Wallet daemon in-process; register a (fixture) token, grant
useto a second principal, prove the GitHub call succeeds while aviewattempt by that principal is denied — and that the sameuse-only principal cannot disable a required check to force a merge nor open a PR against a non-default branch, while a scoped step-up grant lets it do exactly that one thing and nothing more — against a controlled GitHub fixture, not live GitHub. Cover the CLI too: drive the drop-inghagainst the fixture and assert its command/flag/output/exit-code behavior matchesghfor the supported surface.
Relationship to other workstreams
- W3Wallet — the substrate this workstream is. The
use-without-viewproperty is the Proxy capability / capability-JAR runtime; cross-wallet grants are delegation-by-reference chains; the gate is turnkey enforcement; rate limits are asset pools/allowances; always-on hosting and the PMS are W3Wallet hosting/validation concerns; and the GitHub logic ships as a sandboxed capability-JAR plugin the GitHub-blind wallet runs under SJVM. GithubProxy is the canonical first consumer with a real GitHub token as the protected secret — and a worked example of how much of a "service" dissolves into the wallet. - Git — the parent workstream; this is the deep-dive behind its "GitHub API proxy" sub-area, its "W3Wallet-gated access" roadmap item, and its "credential model" open question. The same register →
use-without-viewpattern extends to the self-hosted Git server (javagitsshserver), but as its own authority home (a different secret, git-over-SSH not the REST API) — owned and tracked in the Git workstream, not here. - GithubWatchman — owns the inbound half (webhook ingress) that this workstream deliberately does not absorb into the wallet; the change-detected → act loop pairs Watchman events with capability-gated GitHub invocations.
- Agentic swarms — the payoff: a swarm opens/merges PRs through
use-only grants, so a misbehaving agent has a bounded, revocable, audited GitHub blast radius and never holds a raw token. Crucially, that blast radius also excludes the agent's own escape hatches: ause-only grant cannot disable CI to force-merge a red PR, nor open a PR against a non-default branch, and the agent cannot grant itself those exceptions — only a human-authorized, per-conversation step-up grant can, which is exactly the just-in-time capability the AiCliHostSupervisor capability-provisioning pattern provisions into an agent conversation. The delivery vehicle is the drop-ingh: the supervisor mounts the proxy CLI as theghexecutable in the conversation's container, so an agent already trained onghexercises its grant through the proxy with zero retraining and no awareness it is not talking to GitHub directly — containment, attenuation, and the guardrails all apply underneath its ordinaryghcalls. - GithubCI — a first-party service consumer: all of the githubci service's outbound GitHub calls route through this proxy, with a direct-call path on its own configurable API key kept only as an explicitly-enabled emergency configuration mode (see Outbound GitHub calls go through the GithubProxy).
- GithubProxy Freshness — the child deep-dive on the read path, orthogonal to this workstream's authorization axis: on-disk caching, staleness driven by the required-freshness
ClockPadambient (with a loud algebraic-effect complaint when no pad is specified), and delivery of reads as live-projection observables — the ambients plan's reference adoption, running through this proxy. - NamecheapProxy — a fellow third-party service proxy built on the same credential-containment and attenuated-
use-grant model (per-repo here becomes per-subdomain there), and a reminder that a proxy can carry value beyond containment: NamecheapProxy exists chiefly to be a stable-IP whitelisted egress for an IP-restricted API. - Event Log — the natural sink for the per-registration invocation audit stream.
- HierarchicalClock — leases, grant expiry, and rate-allowance windows depend on a time source robust to skew.
Graduation
GithubProxy already has a Documentation Repository project page. This workstream's plan graduates — and its row folds back into the Git workstream — when the GitHub surface is a secret-free capability JAR hosted in an always-on authority-home daemon, a use grant demonstrably exercises a registered key without disclosing it across two principals, GitHub's budget is metered in-daemon as an asset pool, and the project page is updated to document the wallet-native, capability-governed model. Sequencing within the broader Git capability is tracked in the Git workstream.