Repository navigation
Model org-admin credential acquisition and custody - #9778
Conversation
|
Analyst provenance ruling (relayed by proud-pike-265, 2026-08-30 side-chat consultation) — the authoritative critique of this PR's genealogy model. The two-arm CredentialProvenance shape is REJECTED as authoritative; the corrections below replace it. Ruling verbatim: Overall judgmentThe operator’s center is right: the human bootstrap must be represented, not hidden behind “credential exists.” The proposed two-arm recursive chain should not be built unchanged, however. The principal correction is:
A credential may have been acquired using another credential without continuing to depend on it. Conversely, a current credential may depend on an installation, organization membership, SSO authorization, provider policy, or revocation authority that was not itself a parent credential in the acquisition history. I would therefore reject this as the authoritative shape: and replace it with a transaction graph whose edges are typed by what they mean. The second major correction is that “well-founded means exactly one
For this GitHub program, it is reasonable to require at least one explicit human ceremony in every initial privileged bootstrap path. It is not sound to make exactly one human the universal trust-root law. Recommended core shapeDo not embed a recursively copied chain in every credential row. Give every artifact and transaction structural identity, then derive closures over the shared graph.
Then: The requirements should be a closed sum resembling: This is naturally a multi-input transaction. That matters because an installation token is not simply “derived from a private key.” It is minted by GitHub after the caller proves the app’s identity with a JWT, names an installation, and requests permissions bounded by the app installation. GitHub installation tokens then expire after one hour. citeturn907305view2turn907305view3 The human step should likewise be an executed ceremony, not a parent credential: Do not record: The model does not know or verify the password. It knows that the provider accepted an authentication ceremony using declared authenticator classes. NIST makes the same useful separation: authenticators are bound to accounts, authentication proves control of them, and binding, renewal, invalidation, and account recovery have distinct lifecycles. citeturn996558view6turn645818view0
1. The two-arm provenance shape is insufficientSSO is not either arm cleanlyAn SSO-mediated GitHub session may require: That is a composite authentication and federation ceremony, not a unary credential derivation. For classic GitHub PATs, SAML authorization is also a separately revocable authorization relation. GitHub documents that the authorization can end because an owner revokes it, the user leaves the organization, the PAT is regenerated or its scopes are edited, or the PAT expires. Fine-grained PATs are authorized during creation instead. citeturn996558view2 So represent: as distinct facts. A hardware key is an authenticator, not the human root itselfThe honest shape is: The human participates; the hardware key supplies cryptographic proof. The key has its own lifecycle—binding, loss, invalidation, replacement—and should not be collapsed into either A second human is an authorization dependencyA fine-grained PAT may require organization-owner approval before it can access non-public organization resources. The requester and approver may therefore be different humans. GitHub explicitly models such tokens as pending until an organization administrator approves them. citeturn996558view0turn996558view1 That should produce: The approver’s credential is not necessarily a parent of the resulting token. The approval is an authority-bearing fact consumed by issuance or activation. Support exact participant requirements: For the current sole-founder organization, the accepted transaction may happen to use one actor in several roles. That should be an observed identity equality, not a universal structural assumption. Device flow also disproves the binary shapeOAuth device authorization deliberately splits the process: software initiates a request, while a human uses a separate user agent to authenticate and authorize it. The eventual token is not purely programmatically derived, but neither is the whole flow a single terminal human action. citeturn996558view7 The useful decomposition is: App registration and installation are not credentialsThese are persistent authority resources: They affect what credentials can be minted and what resulting credentials can do, but they are not secret-bearing credentials themselves. Likewise, GitHub’s app private key is a proof key. GitHub stores the public portion, while the private material is downloaded and must be placed into custody. GitHub exposes a fingerprint that can be compared against the locally held key, which is a good model-compatible binding probe because no private bytes need enter the model. citeturn996558view5 A useful receipt is: Account recovery is an alternate bootstrap pathThe proposed “human with password plus 2FA” terminal omits the paths that can replace those authenticators:
NIST treats account recovery as distinct from ordinary authentication and explicitly describes several recovery methods. It also recommends multiple bound authenticators because recovery is often the weak point of an authentication system. citeturn645818view0turn645818view1 You do not need to model GitHub’s internal recovery implementation in detail. You do need an explicit boundary: Without that, “genealogy reaches the root” is an overclaim. Revised well-foundedness lawUse: Possible root classes: For this particular program, add: Do not add the generic law: 2. Acquisition history and runtime dependency must be distinctYes—this distinction is mandatory. I would derive at least three separate closures. A. Historical acquisition genealogyThis is immutable audit history. Invalidating an input later does not rewrite history and does not automatically invalidate the output. Examples:
B. Present-use dependencyThis graph answers:
Typical dependencies include: GitHub provides concrete examples of these non-genealogical dependencies:
C. Renewal or replacement dependencyThis answers:
For a GitHub App installation token: The current installation token has its own one-hour intrinsic expiry, while a valid app JWT is used to obtain its replacement. citeturn907305view3 For an expiring OAuth access token: For a PAT: Optional fourth graph: custody availabilityRemote validity and local usability are also different. A remotely valid token whose secret handle cannot be accessed is unavailable to the actuator, but it has not necessarily been revoked at GitHub. Typed invalidation effectsEvery dependency edge should declare its effect: Then event propagation is not: It is: Your app-token exampleA PAT used only to register an app should normally have: not: Revoking that PAT must therefore not transitively poison the app branch merely because the PAT appears earlier in history. By contrast: is supported by GitHub’s documented behavior. citeturn996558view3 For app-private-key deletion, do not guess a universal effect. The documented safe conclusion is that the deleted key can no longer be used as the app proof key for future JWT-based minting. Unless provider evidence establishes that already minted installation tokens are also revoked, classify the effect as: and let the current token’s own probe and expiry determine current use. GitHub documents key deletion and installation-token minting separately, which is exactly why the model should not infer transitive revocation from acquisition order. citeturn996558view5turn907305view3 Standing should be capability-specificAvoid: Use: A token proven able to list runner groups is not thereby proven able to modify runner groups, alter organization settings, or install an app. A useful total sum is: 3. Honest standing for the human bootstrapA previous successful human ceremony is evidence of a historical event. It is not evidence that:
Therefore do not produce: from a dated receipt. Split the concern into three objects. Historical ceremony evidenceA success receipt never expires as history. Planning readiness
A low-risk scheduled exercise can strengthen this standing—for example, a real login to an appropriate read-only administration surface—without trying to inspect or reproduce the authenticator secrets. Renewal obligationwith: Then: This preserves the operator’s intended distinction: But also prevents the opposite mistake: Once the renewal path can no longer complete before the credential’s required use horizon, it is a continuity incident even if the current credential has a few minutes left. Require only the horizon the operation actually needsA one-shot read does not necessarily require a healthy twelve-month renewal path. It requires credential validity for: A recurring fleet controller may additionally require a continuity standing. So consumption should be: and, where policy demands ongoing operation: Do not require every historical acquisition ancestor to be “current.” That would make an expired browser session falsely invalidate a perfectly usable app key and installation token. Recovery deserves a separate recurring standingBecause account recovery can rebind authenticators, the full control genealogy is incomplete without it. At minimum:
4. Fleet-converge is a good first consumer—with producer separationThere is nothing wrong with fleet-converge being the first policy consumer. It is probably better than building a generic The correct vertical slice is: The observer should have a total result: Only This is important: If the observer could not read the remote state, the system does not know whether desired and observed diverge. Converge must consume the standing, not mint it by assertionThe same network execution may produce both:
That is efficient and honest. But the producer identity should remain This preserves the producer/renderer/consumer distinction: A stale standing should normally trigger the probeAvoid: when the credential material and probe path are available. Prefer: Refuse only when the refresh cannot be executed or the resulting probe refuses. Read standing cannot authorize writeWhen the actuator later mutates organization settings: The mutation transaction must consume its own exact capability standing or let the attempted mutation itself produce the authoritative execution result. You may ultimately choose separate credentials:
The genealogy substrate should permit that without treating one as the parent of the other. “Read-only probe” is not necessarily lifecycle-neutralA GitHub API read does not mutate runner-group desired state, but it may still update provider-side token usage or audit metadata. GitHub automatically removes PATs that have not been used for a year, so a periodic validation request can, by inference, keep a PAT from reaching that inactivity condition. citeturn996558view0 Represent: This is another reason to use the actual needed converge observation rather than run gratuitous keepalive probes. 5. Prior art worth stealingNIST digital-identity lifecycle: steal the vocabularyThe most directly useful source is NIST SP 800-63B: Those are separate operations, and the document treats authenticator lifecycle events and recovery paths independently. That maps cleanly to your need without requiring an identity framework. citeturn645818view0turn645818view3 Steal:
Do not import NIST assurance levels unless the program has a concrete consumer for them. OAuth device flow: steal split ceremoniesRFC 8628 is a clean example of a program-initiated transaction that requires a separate human authorization ceremony. It supports the transaction-graph model and disproves the idea that every output has one unary parent. citeturn996558view7 Steal: as separate receipts. OAuth token exchange: steal delegation semantics only when neededRFC 8693 distinguishes impersonation from delegation and represents subject and actor tokens separately. That will be useful if the system later mints credentials “on behalf of” a human or another workload. citeturn996558view8 Steal: Do not implement a general token-exchange subsystem merely to model GitHub App tokens. X.509: steal path validation, not the tree metaphorRFC 5280 separates certification paths, trust, validity periods, policy constraints, and revocation processing. The useful lesson is that current acceptance is derived from a validation procedure over declared inputs—not from the historical story of how the certificate file arrived. citeturn505287view0 Steal: Do not copy: Your GitHub objects do not form an X.509 issuer chain. SPIFFE/SPIRE: steal attestation-to-short-lived-credential rotationSPIFFE/SPIRE separates workload attestation from short-lived identity issuance and automatic rotation. Its Workload API also streams current credential and trust-bundle sets; when credentials or bundles disappear from later authoritative updates, consumers are expected to stop using them. citeturn801082search8turn482072search3turn907305view4 Steal: Also steal the idea that some bootstrap roots are not secrets at all: SPIFFE’s Workload API can identify local callers through out-of-band process evidence rather than requiring a pre-shared client secret. citeturn907305view4 Do not deploy SPIFFE merely to mint a GitHub token. Its value here is conceptual. Minimal live modelFor the first build, I would keep the implementation to these authorities: with these edge types: and this total genealogy standing: Do not make Current usability is separately: Permanent witnessesThe build should carry at least these falsifiers. The last witness should be provider-profile-specific; do not admit it until the GitHub behavior is evidenced. Consolidated rulingAdopt the human-bootstrap center with this replacement construction:
The most consequential refusals are: With those corrections, the model will capture the operator’s intended recursion without turning acquisition history into a false PKI chain or paging the human merely because an obsolete historical session expired. |
Summary
No real credential is acquired, stored, or read by this change. CLI/REST transport remains outside the upstream interface model.
Verification
git diff --checkcargo test -p v1-compiler --test namespace_occurrence_serde --no-default-features— 7 passed.dagparse sweep viav1_src_dag_parse— 4,414 files parse-clean