vault: reshare zero-downtime PublicKey (plugin, capability, engine, relay) - #23834
prashantkumar1982 wants to merge 14 commits into
Conversation
Consume the two cresettings flags added in chainlink-common
|
👋 prashantkumar1982, thanks for creating this pull request! To help reviewers, please consider creating future PRs as drafts first. This allows you to self-review and make any final changes before notifying the team. Once you're ready, you can mark it as "Ready for review" to request feedback. Thanks! |
|
✅ No conflicts with other open PRs targeting |
|
I see you updated files related to
|
CORA - Pending ReviewersAll codeowners have approved! ✅ Legend: ✅ Approved | ❌ Changes Requested | 💬 Commented | 🚫 Dismissed | ⏳ Pending | ❓ Unknown For more details, see the full review summary. |
|
/vault-audit |
|
📋 View the full report and findings — private tracking issue, visible to chainlink team members only. Resolve blocking findings there with |
… into cre/vault-publickey-reshare-impl
|
/vault-audit |
|
📋 View the full report and findings — private tracking issue, visible to chainlink team members only. Resolve blocking findings there with |
| if pkb, mErr := r.cfg.PublicKey.Marshal(); mErr != nil { | ||
| r.lggr.Errorw("could not marshal vault public key for GetSecrets response", "error", mErr) | ||
| } else { | ||
| resp.RawVaultPublicKey = hex.EncodeToString(pkb) |
There was a problem hiding this comment.
Any reason not to add this to the message outside of the plugin, in Execute? This would also reduce the impact on the StateTransition max size
There was a problem hiding this comment.
(Also see the audit items, there are a few blockers there)
There was a problem hiding this comment.
Any reason not to add this to the message outside of the plugin, in Execute? This would also reduce the impact on the StateTransition max size
We discussed on slack thread that to ensure no downtime when publicKey is changing, we have to include in the OCR response so all nodes agree on a certain publicKey.
Further, since we are soon moving reads out of OCR, this is only temporary for a few weeks, so the increase in StateTransition is ok for short term.
There was a problem hiding this comment.
For Vault audit, i did mark them as resolved.
But ran a new audit right now.
| // produced these shares so decrypt-side callers read the matching key live from | ||
| // the response instead of from CapReg / static config. Deterministic across | ||
| // nodes: the gate is config-sourced and PublicKey is the shared instance key. | ||
| if open, err := r.cfg.VaultGetSecretsIncludePublicKey.IsOpen(ctx); err != nil { |
There was a problem hiding this comment.
This should be in Observation, not StateTransition. Each node should broadcast what they think the public key is (could be a new field on Observation instead of per secret request) and StateTransition to aggregate those so it stays as a Pure function
There was a problem hiding this comment.
yes, makes sense. Done
…y-reshare-impl # Conflicts: # core/scripts/go.mod # core/scripts/go.sum # deployment/go.mod # deployment/go.sum # go.mod # go.sum # integration-tests/go.mod # integration-tests/go.sum # integration-tests/load/go.mod # integration-tests/load/go.sum # system-tests/lib/go.mod # system-tests/lib/go.sum # system-tests/tests/go.mod # system-tests/tests/go.sum
|
/vault-audit |
|
↩️ Superseded by a newer vault audit run. |
|
📋 View the full report and findings — private tracking issue, visible to chainlink team members only. Resolve blocking findings there with |
… into cre/vault-publickey-reshare-impl
… into cre/vault-publickey-reshare-impl
|
|
/vault-audit |
|
📋 View the full report and findings — private tracking issue, visible to chainlink team members only. Resolve blocking findings there with |
|
✅ All vault-audit blocking findings on this PR have been resolved by reviewers. The |




What
Server-side (chainlink node) implementation of vault reshare zero-downtime PublicKey handling. A DKG reshare rotates the per-recipient verification shares (
HArray) so the rawVaultPublicKeyJSON changes every reshare. This wires every consumer to source the key from something either always-live or truly immutable, so reshares need no manual PublicKey updates and cause no downtime.chainlink-common dependency
Bumped to
v0.11.2-0.20260930165848-901616407c2b, which includes:GetSecretsResponse.raw_vault_public_key.Observations.raw_vault_public_key(pureStateTransition).SecretsResponseResult.raw_vault_public_key+GetRawSecretsResponse(runtime relay path).Changes
1. Vault OCR plugin (
core/services/ocr2/plugins/vault/plugin.go)VaultGetSecretsIncludePublicKeygate.Observations.RawVaultPublicKey, viaobservedVaultPublicKey) when the gate is open.StateTransitionaggregates the quorum-agreed key (aggregateVaultPublicKey, ≥ F+1) and attaches it toGetSecretsResponse.RawVaultPublicKey. Sourcing the key from observations rather than node-local config keepsStateTransitiona pure function of observations and tolerant of a staggered gate rollout (addresses review feedback). Warns on divergent keys; errors when advertised keys miss quorum.2. Vault capability (
core/capabilities/vault/capability.go)encryptOnlyEnabledgate. When open,GetPublicKeyreturns only the stable encrypt-only sub-key{Group, G_bar, H}(HArraystripped), so encrypt-only consumers (CRE CLI) and the CapReg value are unaffected by reshares. Gate limiter closed on shutdown.3. Workflow Engine
GetSecret()(core/services/workflows/v2/secrets.go)RawVaultPublicKey(parseVaultPublicKeyHex) over the CapReg-configuredVaultPublicKey, falling back when absent.4. Runtime
getSecretvia confidential-relay (core/capabilities/confidentialrelay/handler.go,core/services/workflows/v2/secrets.go,capability_executor.go) — addresses review feedbackRawSecretsFetcher/secretsFetchergainGetRawSecretsResponse(returns the full response incl. the key);GetRawSecretskept as a deprecated wrapper (non-breaking).GetRawSecretsResponseand forwardsRawVaultPublicKeyintoSecretsResponseResult(bound into the signed response hash in chainlink-common), so the enclave verifies runtime secret shares against the live DKG key across reshares.Tests
core/services/ocr2/plugins/vault/get_secrets_public_key_test.go— attach / aggregate / observe, F+1 quorum, gate-off exclusion.core/capabilities/vault/public_key_encrypt_only_test.go—HArraystripping.core/services/workflows/v2/secrets_encrypt_only_test.go— response-key preference + fallback.core/capabilities/confidentialrelay/handler_test.go,confidential_module_test.go— relay forwarding + stubs.system-tests/tests/smoke/cre/vault_don_test.go—ExecuteVaultReadSecretsWithReshareFlagsTest(both flags on),SkipIfMixedEnvuntil baseline images include this; wired incre_suite_test.go.Feature gating (all default OFF)
VaultGetSecretsIncludePublicKeyEnabled— plugin includesRawVaultPublicKeyinGetSecretsresponses.VaultPublicKeyEncryptOnlyEnabled—GetPublicKey/CapReg return the stable encrypt-only sub-key.Safety / rollout
StateTransitionreads the key only from observations (pure); safe under staggered gate rollout.RawVaultPublicKeyonly when present, so responses without it hash identically to the pre-field format — backward compatible.tdh2.PublicKey.Unmarshaltolerates a missingHArrayand encryption uses onlyG_bar+H, so stripping inGetPublicKeyis safe for the internalgetMasterPublicKeypath too.SkipIfMixedEnvon the e2e until this ships in baseline images.Related