feat(github): gist scope + github.identityProof.create/delete - #1965
Merged
panghy merged 3 commits intoSep 21, 2026
Merged
Conversation
This was referenced Sep 17, 2026
Merged
panghy
force-pushed
the
feat/github-identity-proof
branch
from
September 18, 2026 01:24
0142efa to
ef0fe05
Compare
panghy
force-pushed
the
feat/github-identity-proof
branch
2 times, most recently
from
September 19, 2026 10:53
40ac2d2 to
ac2bfbf
Compare
panghy
marked this pull request as ready for review
September 19, 2026 11:17
🤖 Augment PR SummarySummary: Adds the guest-side GitHub gist identity-proof flow for multiplayer joins. Changes:
Technical Notes: Proof creation verifies 🤖 Was this summary useful? React with 👍 or 👎 |
panghy
force-pushed
the
feat/github-identity-proof
branch
from
September 19, 2026 14:35
ac2bfbf to
054acb0
Compare
panghy
force-pushed
the
feat/github-identity-proof
branch
from
September 19, 2026 18:17
054acb0 to
d0a52c4
Compare
panghy
force-pushed
the
feat/github-identity-proof
branch
from
September 19, 2026 19:01
d0a52c4 to
3144f45
Compare
panghy
added a commit
that referenced
this pull request
Sep 20, 2026
…gist scope
`delete_proof_gist` now reads the gist back first (`GET /gists/{id}`):
a 404 stays the idempotent `ok`; otherwise the response's `X-OAuth-Scopes`
must grant `gist` (`ScopeMissing`, like create, instead of a 403 misreported
as a rejected token or a silent success) and the gist must be an Intent
proof gist — exactly one file, `intent-join-proof.txt` — before any DELETE
is sent. Any other gist of the account is refused with the new
`IdentityProofError::NotProofGist`, mapped to `-32602` ("nothing deleted"),
so the RPC can no longer be turned against arbitrary gists. Unit tests pin
the read-back / scope / shape guards; the services and WSS mocks serve the
read-back and the e2e covers the refused unrelated gist and the delete-side
scope check.
Addresses the Deep Code Review threads on #1965.
panghy
force-pushed
the
feat/github-identity-proof
branch
from
September 20, 2026 09:06
3144f45 to
1873395
Compare
panghy
added this pull request to stack #2028
September 20, 2026 12:50
panghy
added a commit
that referenced
this pull request
Sep 20, 2026
…gist scope
`delete_proof_gist` now reads the gist back first (`GET /gists/{id}`):
a 404 stays the idempotent `ok`; otherwise the response's `X-OAuth-Scopes`
must grant `gist` (`ScopeMissing`, like create, instead of a 403 misreported
as a rejected token or a silent success) and the gist must be an Intent
proof gist — exactly one file, `intent-join-proof.txt` — before any DELETE
is sent. Any other gist of the account is refused with the new
`IdentityProofError::NotProofGist`, mapped to `-32602` ("nothing deleted"),
so the RPC can no longer be turned against arbitrary gists. Unit tests pin
the read-back / scope / shape guards; the services and WSS mocks serve the
read-back and the e2e covers the refused unrelated gist and the delete-side
scope check.
Addresses the Deep Code Review threads on #1965.
panghy
force-pushed
the
feat/github-identity-proof
branch
from
September 20, 2026 14:10
1873395 to
08a82bf
Compare
github-merge-queue
Bot
removed this pull request from the merge queue because a pull request earlier in the stack was removed
Sep 20, 2026
panghy
force-pushed
the
feat/github-identity-proof
branch
from
September 20, 2026 15:29
08a82bf to
299c22b
Compare
github-merge-queue
Bot
removed this pull request from the merge queue because a pull request earlier in the stack was removed
Sep 20, 2026
github-merge-queue
Bot
removed this pull request from the merge queue because a pull request earlier in the stack was removed
Sep 20, 2026
Guest half of the gist identity-proof join flow: the local daemon creates
and deletes a secret proof gist with the user's own stored GitHub token.
- device_flow: add `gist` to DEFAULT_SCOPES
- sourcecontrol: identity_proof module — GET /user (X-OAuth-Scopes check),
POST /gists public:false, idempotent DELETE /gists/{id}
- services/transport: github.identityProof.create { nonce, hostLabel } ->
{ gistId, login }; github.identityProof.delete { gistId } -> { ok: true };
owner-client only; bounded -32603 error.data.code
(github-not-connected / github-scope-missing / github-unreachable)
- tests: engine mock-server unit tests, services + router tests, WSS e2e
Agent-Id: agent-c6117b95-ceb4-438e-93c1-2cbe4c0d5853
…gist scope
`delete_proof_gist` now reads the gist back first (`GET /gists/{id}`):
a 404 stays the idempotent `ok`; otherwise the response's `X-OAuth-Scopes`
must grant `gist` (`ScopeMissing`, like create, instead of a 403 misreported
as a rejected token or a silent success) and the gist must be an Intent
proof gist — exactly one file, `intent-join-proof.txt` — before any DELETE
is sent. Any other gist of the account is refused with the new
`IdentityProofError::NotProofGist`, mapped to `-32602` ("nothing deleted"),
so the RPC can no longer be turned against arbitrary gists. Unit tests pin
the read-back / scope / shape guards; the services and WSS mocks serve the
read-back and the e2e covers the refused unrelated gist and the delete-side
scope check.
Addresses the Deep Code Review threads on #1965.
…tityProof.delete
`delete_proof_gist` read `GET /gists/{id}` first and mapped its status before
looking at `X-OAuth-Scopes`, so a token without the `gist` scope got the wrong
answer whenever GitHub would not show it the gist: a 404 read as "already
deleted" (false `Ok`) and a 403 as a rejected token (`Unauthorized`).
Delete now runs the same authenticated `GET /user` scope preflight as create
before touching the gist, so the matrix is: missing scope + 404/403 →
`ScopeMissing`; scoped + 404 → `Ok` (idempotent); rejected token →
`Unauthorized`. The read-back stays for the proof-gist guard only.
Regression tests: missing scope on a 404 gist is not success, missing scope
on a 403 gist is `ScopeMissing` (from the independent verifier), the scoped
404 idempotence case, and a WSS assertion that an unknown gist without the
scope is `github-scope-missing`, not `{ ok: true }`.
panghy
force-pushed
the
feat/github-identity-proof
branch
from
September 20, 2026 18:21
299c22b to
041edca
Compare
An error occurred while trying to automatically change base from
feat/invite-accept-returning-guest
to
main
September 20, 2026 23:54
Merged
panghy
added a commit
that referenced
this pull request
Sep 21, 2026
Resolves adjacent-insertion conflicts against the gist identity-proof series (#1965, #1967, #1969): main's github.identityProof.* router arms, identity_proof module and invite doc comment are kept byte-identical with our sourceControl.* / gitlab_* insertions after them, and the totals are recounted to 332 router / 56 fast-path / 390 client-callable (main 327/56/385 + our 5 router methods) in catalog/tests.rs and the protocol.rs 10.5 paragraph.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Guest half of the gist identity-proof join flow: the local daemon can create and delete a secret proof gist with the user's own stored GitHub token, so a host can verify that the joining guest controls the GitHub account.
Changes
intent-sourcecontroldevice_flow::DEFAULT_SCOPESgainsgist(repo, read:org, workflow, gist). Tokens granted before keep working; only the proof needs a re-authorization.identity_proofmodule:create_proof_gist(token, api_base, nonce, host_label) -> { gist_id, login }—GET /userfirst, checksX-OAuth-Scopesforgist(typedScopeMissing), thenPOST /gistswithpublic: falseand a singleintent-join-proof.txt(<nonce>\n<explanatory line naming the host>\n);delete_proof_gist(token, api_base, gist_id)—DELETE /gists/{id}, 404 treated as success (idempotent). TypedIdentityProofError { ScopeMissing, Unauthorized, Unreachable, Other }.intent-core:Error::IdentityProof(IdentityProofErrorKind)with the bounded wire codesgithub-not-connected/github-scope-missing/github-unreachable(numeric-32603,error.data = { code }); two newWorkspaceApitrait methods.intent-services:github_identity_proof_create(nonce, hostLabel)/github_identity_proof_delete(gistId)— administrator-gated (refused on the collaborator surfaces), storedsourceControl.github.tokenonly (env /ghfallbacks never consulted), params validated as trimmed single lines / alphanumeric gist id (-32602), API base via the existingINTENTD_GITHUB_API_BASE_URIseam. A token GitHub rejects maps togithub-not-connected(same remedy).intent-transport:github.identityProof.create/github.identityProof.deleteregistered in the catalog + router next togithub.getUser; identity-proof errors carryerror.data.code.Wire surface
github.identityProof.createnonce(req),hostLabel(req){ gistId, login }github.identityProof.deletegistId(req){ ok: true }(idempotent)Refusals:
-32603witherror.data.code∈github-not-connected|github-scope-missing|github-unreachable;-32602for bad params.Protocol docs (
docs/protocol/methods/integrations.md§5.27,09-error-codes.md) are updated on the monorepo side alongside this PR.Tests
intent-sourcecontrol: mock-server unit tests for create (scopes ok), scope-missing, unauthorized, delete live / 404 / unreachable, proof content format.intent-services: services-layer tests (stored token happy path create+delete, no token → not-connected, missing scope, dead host → unreachable, param validation before token load) + error-mapping unit tests.intent-transport: router param/dispatch tests +error.data.codeshape; catalog test updated.intentdWSS e2e (e2e_wss_github_device_flow.rs::github_identity_proof_create_and_delete_over_wss): seeded secrets file + mock API host → create returns{ gistId, login }, delete idempotent,-32602on missing params,github-scope-missingwithout the scope,github-not-connectedaftergithub.revoke.Gates
cargo fmt --check✅,cargo clippy --workspace --all-targets -- -D warnings✅,cargo test --workspace --test '*_lint'✅cargo nextest run -p intent-core -p intent-sourcecontrol -p intent-services -p intent-transport→ 6063 passedcargo nextest run -p intentd -E 'binary(e2e_wss_github_device_flow) | binary(wss_integration) | test(catalog)'→ 174 passedNot in scope: host-side challenge/prove, client changes,
github.authStatussemantics.