Skip to content

Add a completeness fence over the GraphQL mutation surface#219

Open
beardthelion wants to merge 2 commits into
mainfrom
fix/graphql-mutation-fence
Open

Add a completeness fence over the GraphQL mutation surface#219
beardthelion wants to merge 2 commits into
mainfrom
fix/graphql-mutation-fence

Conversation

@beardthelion

@beardthelion beardthelion commented Jul 18, 2026

Copy link
Copy Markdown
Collaborator

GraphQL mutations have required a verified signer since #87, but nothing forced future mutations to stay gated, and README / OSS-readiness / roadmap still listed the auth as an open item. This adds the missing guard and reconciles the claims.

Fence

every_graphql_mutation_has_its_gate (crates/gitlawb-node/src/api/mod.rs) gates the GraphQL mutation surface the way REST is gated: a separate guard with its own bucket table (a guard over a heterogeneous set classifies per member, INV-1). It asserts the mutation set is complete (every mutation classified) and that each carries its gate marker.

Discovery comes from async-graphql's own introspection, so the required set is derived from the mounted schema, not a hand-list: no source-text or SDL-format shape can hide a live mutation, and a new unregistered mutation (including a repo-write) fails the build. RED-verified both ways: an unregistered mutation and a removed did_matches gate each redden the guard; clean tree green. No DB, no feature flag.

Scope and limits

The marker check (does each resolver body call its gate) is a best-effort static backstop, not the completeness guarantee and not load-bearingness: a marker inside a string literal is a documented vacuity residual, and the adversarial #[sqlx::test]s in graphql/mutation.rs are the real proof the gates reject.

Docs

README known-limitations, the OSS-readiness live-network blockers, and the maintainer roadmap are reconciled to the shipped state: mutations require a signer; the GraphQL surface is the agent-task queue; repo writes stay REST-only; no owner-gated repo-write mutation is exposed over GraphQL.

Test-only plus docs; no request-path change.

Summary by CodeRabbit

  • Security

    • GraphQL mutations now require a verified signer bound to the acting DID.
    • GraphQL write operations remain limited to the agent-task queue; repository writes continue through REST only.
    • Added safeguards to ensure all GraphQL mutations remain covered by authorization checks.
  • Documentation

    • Updated project status, security roadmap, and readiness documentation to reflect the GraphQL authorization and scope.

t added 2 commits July 18, 2026 17:22
Gate the GraphQL mutation surface the way REST is gated, as a SEPARATE guard with
its own bucket table (a guard over a heterogeneous set must classify per member,
INV-1). every_graphql_mutation_has_its_gate asserts the mutation set is complete
(every mutation classified) and that each carries its gate marker.

Completeness is derived from async-graphql's own introspection: the field names
come from the engine as structured data, so no source-text or SDL-format shape
(a brace or async fn in a comment or string, a whitespace/generic/skip variant, a
brace in a rustdoc description) can hide a live mutation. A new unregistered
mutation, including a repo-write, fails the build.

The marker check (does each resolver body call its gate) is a best-effort static
backstop, bounded to each resolver's own comment-masked, string-aware body. It is
explicitly not the completeness guarantee and not load-bearingness: a marker inside
a string literal is a documented vacuity residual, and the adversarial sqlx tests in
graphql/mutation.rs are the real proof the gates reject.

Verified by execution: an unregistered mutation reddens (unclassified), a removed
did_matches gate reddens (missing marker), the clean tree is green; no DB, no
feature flag.
…tate

README's known limitations, the OSS-readiness live-network blockers, and the
maintainer roadmap still listed GraphQL mutation auth as an open item, which
contradicts #87: GraphQL mutations already require a verified signer bound to the
acting DID. Reconcile each to the shipped reality while keeping the genuinely-open
nuance: the GraphQL mutation surface is the agent-task queue, repo writes stay
REST-only, and no owner-gated repo-write mutation is exposed over GraphQL. The
every_graphql_mutation_has_its_gate fence forces any new mutation to be classified
in its table; owner-gate and REST parity stay the INV-1 review check.
@coderabbitai

coderabbitai Bot commented Jul 18, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f72dd4e2-0d7b-4880-baac-67fe6ace1144

📥 Commits

Reviewing files that changed from the base of the PR and between ad7c2b2 and c3fee1e.

📒 Files selected for processing (4)
  • README.md
  • crates/gitlawb-node/src/api/mod.rs
  • docs/MAINTAINER-ROADMAP.md
  • docs/OSS-READINESS-AUDIT.md

📝 Walkthrough

Walkthrough

GraphQL authorization coverage now verifies the exact mutation schema and required resolver gates, with comment- and string-aware source inspection. README and security documentation now describe verified signer requirements, agent-task-only mutations, and REST-only repository writes.

Changes

GraphQL authorization fence

Layer / File(s) Summary
GraphQL mutation completeness and gate checks
crates/gitlawb-node/src/api/mod.rs
Adds schema introspection, resolver-body extraction, comment masking, exact mutation-set checks, and authorization-marker tests for task mutations.
Documented GraphQL write boundary
README.md, docs/MAINTAINER-ROADMAP.md, docs/OSS-READINESS-AUDIT.md
Documents verified signer binding, agent-task-only GraphQL mutations, REST-only repository writes, and the related review checks.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

  • Gitlawb/node#87: Related GraphQL authorization enforcement and DID-based gate work in the same API module.

Suggested labels: kind:test, crate:node, subsystem:api

Suggested reviewers: kevincodex1, jatmn

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the change well, but it omits several required template sections like Kind of change, What changed bullets, and reviewer verification. Add the missing template sections, especially Closes #, Kind of change, What changed bullets, and How a reviewer can verify with commands or steps.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: adding a GraphQL mutation completeness fence.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/graphql-mutation-fence

Comment @coderabbitai help to get the list of available commands.

@beardthelion beardthelion added crate:node gitlawb-node — the serving node and REST API kind:docs Docs and comments only labels Jul 18, 2026
beardthelion pushed a commit that referenced this pull request Jul 19, 2026
…ber (#195)

Resolve the three review findings on head 07abcc6.

Bound the two new real-node probes (protected-branch push, PR-diff) on a
timeout-bearing client, so a wedged git subprocess fails the suite rather than
hanging it until CI kills the job.

Withhold the pushed secret blob's OID (full and [..12] short form) in the PR-diff
denial. The old branch was dead code: the OID was never in the fixture seed map,
so build_branch_push_body now returns the pushed blob OIDs.

Drive every caller-self runtime deny the registry claimed but only source-marker
checked. create_task, claim_task, and register are a new SignerSelfGate class (a
body DID field bound to the authenticated signer); complete_task and fail_task
are a MultiPrincipalGate assignee arm (the caller must equal the task's stored
assignee). Each runs hostile-then-authorized against a real node. A completeness
guard, derived from a scan of src/api, now requires every did_matches(caller, X)
handler to be a driven registry row, and the module doc is reconciled to the REST
surface (the parallel GraphQL mutation gates are fenced separately, #219).

Every gate is mutation-verified load-bearing: reverting a production did_matches
line drives its hostile probe RED, and the completeness guard goes RED when a
driven row is dropped or the classifier is starved. No production code changed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

crate:node gitlawb-node — the serving node and REST API kind:docs Docs and comments only

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant