How GrantTrace works
Understand scenario coverage, evidence sources, and deterministic solving.
The claim
GrantTrace answers a narrow question:
Which GitHub App REST permissions do these named, recorded scenarios demonstrably require?
That boundary matters. GrantTrace does not infer unexecuted paths, intercept
arbitrary HTTP traffic, inspect GraphQL, or prove that production behavior is
fully covered. Automatic recording observes supported Node.js global fetch
calls to exactly https://api.github.com, including standard Octokit traffic.
Off-origin requests are ignored even when a response imitates GitHub's
permission header. Requests that bypass the global-fetch path are outside the
result unless they use GrantTrace's explicit adapter.
Scenarios make coverage explicit
Each recording has a stable, lowercase scenario name such as issue-triage.
Routes in the contract retain the names of the scenarios that exercised them.
This lets a reviewer tell the difference between a permission change and a
coverage change.
Local recordings are kept under .granttrace/observations/. They are input to
check, not an append-only history. Recording the same name replaces its
previous successful observation set.
record then solves the aggregate contract and presents the same diff as
check. An interactive terminal can explicitly accept it in place. A
noninteractive run never accepts and exits 6 when review is required.
Two evidence sources
For every recognized canonical route, GrantTrace can use:
- GitHub's runtime
X-Accepted-GitHub-Permissionsresponse header, accepted by automatic capture only fromhttps://api.github.com. - A pinned offline catalog reviewed against official GitHub documentation for
API version
2026-03-10.
When both sources exist, they must agree after canonicalization. Agreement keeps both provenance labels. Disagreement, malformed runtime evidence, an unknown route, or an unsupported API blocks the contract.
A missing runtime header may fall back only to a known catalog entry. Missing evidence never means “no permission required.”
Off-origin traffic does not create an automatic observation and cannot force a runtime/catalog contradiction. This origin boundary prevents a local or third-party server from manufacturing GitHub runtime evidence.
Permission alternatives stay exact
GitHub expresses requirements as disjunctive normal form:
issues=write; pull_requests=writeThe semicolon means OR. A comma means AND:
contents=read, pull_requests=readWithin a permission, write satisfies read. Different permission names are
not interchangeable.
GrantTrace retains every nondominated sufficient assignment in
permissionFrontier. For a new contract, it selects one deterministic default
assignment by preferring:
- fewer write permissions;
- lower total access weight;
- fewer distinct permissions; and
- lexical order.
This is a stable default review policy, not a universal definition of optimal
access. granttrace frontier list displays every complete candidate, and
granttrace frontier select NUMBER can explicitly persist another candidate
as selectedPermissions. Checks retain that choice while it remains in the
recomputed frontier. If it becomes invalid, the deterministic default appears
as an ordinary blocking contract diff and still requires explicit acceptance.
What gets committed
granttrace.lock.json contains:
- tool, API, schema, and catalog identity;
- scenario names and canonical route templates;
- canonical permission requirements and evidence provenance;
- route-to-scenario attribution;
- selected permissions and the full nondominated frontier;
- separately documented manual keeps; and
- safe blocking findings.
The mandatory metadata:read baseline participates in solving and live-token
validation but is not presented as observed selected access.
What stays local
GrantTrace does not intentionally store raw URLs, query strings, request or response bodies, commands, tokens, private keys, owners, repositories, or resource identifiers in observations, contracts, or proof reports.
See the protocol for the normative model and security and privacy for the trust boundary.