feat: machine-credential options (ApiKey/Keypair/Token) + CredentialManagerService.IssueCredential (NHI vending, additive)#941
Conversation
…anagerService.IssueCredential (NHI vending, additive)
Additive baton-sdk surface that unblocks service-account / key vending
fleet-wide for the NHI program. Grounded against the SA-vending surface
analysis (§5 R1 + R3).
R1 — machine-credential CredentialOptions arms:
- CredentialOptions.{ApiKey=104, Keypair=105, Token=106} additive oneof arms
(open oneof; existing 100..103 unchanged), mirrored on the decrypted
server-side LocalCredentialOptions.
- CapabilityDetailCredentialOption gains API_KEY/KEYPAIR/TOKEN values.
- ConvertCredentialOptions passes the new arms through verbatim (they carry
no encrypted payload, same shape as no_password/sso).
R3 — CredentialManagerService.IssueCredential:
- New additive RPC: mint a NEW secret for an EXISTING identity, returning the
secret as a first-class Resource + EncryptedData[], distinct from
rotate-in-place. Supports N coexisting keys per identity.
- CAPABILITY_CREDENTIAL_ISSUE + CredentialDetailsCredentialIssue advertisement.
- New opt-in CredentialIssuer Go interface (Issue + IssueCapabilityDetails);
existing CredentialManager implementations are untouched. Builder dispatch
reuses the existing PlaintextData -> Encrypt fan-out verbatim.
All changes are additive and wire-compatible; `buf breaking` is clean. Keypair
uses client-side generation semantics (connector generates locally, returns
private material as PlaintextData, platform sees only the encrypted form).
Tests: oneof round-trip for the new arms, IssueCredential builder dispatch
(mock issuer -> encryption fan-out + response shape), capability advertisement.
Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
General PR Review: feat: machine-credential options (ApiKey/Keypair/Token) + CredentialManagerService.IssueCredential (NHI vending, additive)Blocking Issues: 0 | Suggestions: 0 | Threads Resolved: 0 Review SummaryThe new commit adds a dedicated Security IssuesNone found. Correctness IssuesNone found. SuggestionsNone. |
… task type
IssueCredential was recording task success/failure metrics under
tasks.RotateCredentialsType, conflating issue and rotate telemetry. Add a
dedicated IssueCredentialType ("issue_credential") task type and use it so the
two operations are distinguishable in metrics and logs. Appended to the iota to
avoid renumbering existing TaskType values.
Co-authored-by: c1-squire-dev[bot] <c1-squire-dev[bot]@users.noreply.github.com>
|
Note from the vending-platform design (consumer context for reviewers): the vending lifecycle records will bind |
🔗 Preview: C1
Summary
Additive baton-sdk surface that unblocks service-account / machine-credential vending fleet-wide for the pqprime NHI program. Implements R1 (machine-credential
CredentialOptionsarms) and R3 (CredentialManagerService.IssueCredential) from the SA-vending surface grounding (§5), which was verified against SDK HEAD (v0.13.x). Everything is additive and wire-compatible —buf breakingis clean.R1 — machine-credential
CredentialOptionsarmsCredentialOptionsgains three additive oneof arms (the oneof is open; existing100..103are unchanged):ApiKey { repeated string scopes; google.protobuf.Duration ttl } = 104Keypair { string algorithm; int32 bits } = 105Token { repeated string scopes; google.protobuf.Duration ttl; string audience } = 106LocalCredentialOptions(same shape — these arms carry no encrypted payload).CapabilityDetailCredentialOptiongainsAPI_KEY/KEYPAIR/TOKENvalues so the surface is advertisable.crypto.ConvertCredentialOptionspasses the new arms through verbatim, exactly like the existingno_password/ssoarms (no change to the crypto path — encryption keys off the connector returning[]PlaintextData, not the option variant).Keypair = client-side generation semantics: the connector generates the keypair locally, registers the public key with the platform, and returns the private material as
PlaintextData, which the builder encrypts before it leaves the connector — the platform only ever sees the encrypted form (mirrors the Snowflake key-pair auth pattern).R3 —
CredentialManagerService.IssueCredentialRotateCredential's replace-in-place semantics. It returns the new secret as a first-classResource(so it can carrySecretTraitwith anidentity_idback-ref) plus the encrypted material, modeling N coexisting keys per identity (e.g. cloud SA key Update import paths to use new sdk repo #1 and Add ViewSync() to the connectorstore reader interface #2).IssueCredentialRequest { ResourceId identity_id = 1; CredentialOptions credential_options = 2; repeated EncryptionConfig encryption_configs = 3 }IssueCredentialResponse { Resource secret = 1; repeated EncryptedData encrypted_data = 2; repeated google.protobuf.Any annotations = 3 }CAPABILITY_CREDENTIAL_ISSUE+CredentialDetailsCredentialIssue { supported_credential_options[]; preferred_credential_option }onCredentialDetails, wired into the builder's capability assembly and validation.CredentialIssuerinterface (Issue+IssueCapabilityDetails) so existingCredentialManagerimplementations don't break — a connector opts in only by implementing it. Builder dispatch reuses the existing[]PlaintextData → Encryptfan-out loop verbatim.Grounding
Based on the SA-vending baton-sdk surface analysis (pqprime NHI), §5 R1 + R3. Every new field has a named consumer in that mapping; no reserved slots, no
V1Identifier. R2 (AccountInfo.account_type) was intentionally left out of scope.Tests
ApiKey/Keypair/TokenthroughConvertCredentialOptions.IssueCredentialbuilder dispatch: mock issuer returns aResource+ plaintexts → asserts the encryption fan-out and response shape (including the returned secret resource and the converted options reaching the issuer).CAPABILITY_CREDENTIAL_ISSUEsurfaced connector-wide and per-resource-type, plus the credential-issue details.Build, full test suite,
buf lint,buf format, andbuf breaking --against mainall green.Known follow-ups (not in this PR)
Two committed consumer tracks ride this surface:
plans/nhi/sa-lifecycle/layer-work-map.mdL5): an "issue credential" task that callsIssueCredential, persists the returned secret resource (emittingSecretTrait), and routesencrypted_datathrough the existing Vault decryption path.Five connectors are queued on this surface: mongo-atlas, clickhouse-cloud, aws, databricks, snowflake.
🛡️ Built with pqprime
Exposed endpoints
🏰 Squire environment: gentle-jellyfish-87640
Task: 126ac54f-6872-4833-933f-c48553c2a3ba