Skip to content

vault: reshare zero-downtime PublicKey (plugin, capability, engine, relay) - #23834

Open
prashantkumar1982 wants to merge 14 commits into
developfrom
cre/vault-publickey-reshare-impl
Open

prashantkumar1982 wants to merge 14 commits into
developfrom
cre/vault-publickey-reshare-impl

Conversation

@prashantkumar1982

@prashantkumar1982 prashantkumar1982 commented Sep 27, 2026 •

Copy link
Copy Markdown
Contributor

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 raw VaultPublicKey JSON 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:

  • #2419 — CRE settings flags + GetSecretsResponse.raw_vault_public_key.
  • #2427 — Observations.raw_vault_public_key (pure StateTransition).
  • #2429 — SecretsResponseResult.raw_vault_public_key + GetRawSecretsResponse (runtime relay path).

Changes

1. Vault OCR plugin (core/services/ocr2/plugins/vault/plugin.go)

  • New VaultGetSecretsIncludePublicKey gate.
  • Each node broadcasts its DKG public key in its Observation (Observations.RawVaultPublicKey, via observedVaultPublicKey) when the gate is open.
  • StateTransition aggregates the quorum-agreed key (aggregateVaultPublicKey, ≥ F+1) and attaches it to GetSecretsResponse.RawVaultPublicKey. Sourcing the key from observations rather than node-local config keeps StateTransition a pure function of observations and tolerant of a staggered gate rollout (addresses review feedback). Warns on divergent keys; errors when advertised keys miss quorum.
  • Gate limiter closed on shutdown.

2. Vault capability (core/capabilities/vault/capability.go)

  • New encryptOnlyEnabled gate. When open, GetPublicKey returns only the stable encrypt-only sub-key {Group, G_bar, H} (HArray stripped), 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)

  • Decrypt side prefers the response's RawVaultPublicKey (parseVaultPublicKeyHex) over the CapReg-configured VaultPublicKey, falling back when absent.

4. Runtime getSecret via confidential-relay (core/capabilities/confidentialrelay/handler.go, core/services/workflows/v2/secrets.go, capability_executor.go) — addresses review feedback

  • RawSecretsFetcher/secretsFetcher gain GetRawSecretsResponse (returns the full response incl. the key); GetRawSecrets kept as a deprecated wrapper (non-breaking).
  • The relay handler calls GetRawSecretsResponse and forwards RawVaultPublicKey into SecretsResponseResult (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 — HArray stripping.
  • 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.
  • e2e system-tests/tests/smoke/cre/vault_don_test.go — ExecuteVaultReadSecretsWithReshareFlagsTest (both flags on), SkipIfMixedEnv until baseline images include this; wired in cre_suite_test.go.

Feature gating (all default OFF)

  • VaultGetSecretsIncludePublicKeyEnabled — plugin includes RawVaultPublicKey in GetSecrets responses.
  • VaultPublicKeyEncryptOnlyEnabled — GetPublicKey/CapReg return the stable encrypt-only sub-key.

Safety / rollout

  • Both gates default OFF.
  • StateTransition reads the key only from observations (pure); safe under staggered gate rollout.
  • The relay hash binds RawVaultPublicKey only when present, so responses without it hash identically to the pre-field format — backward compatible.
  • tdh2.PublicKey.Unmarshal tolerates a missing HArray and encryption uses only G_bar+H, so stripping in GetPublicKey is safe for the internal getMasterPublicKey path too.
  • SkipIfMixedEnv on the e2e until this ships in baseline images.

Related

  • chainlink-confidential-compute #103 — decrypt-side (executor + enclave dispatcher) consumers of the response key.

Consume the two cresettings flags added in chainlink-common
@github-actions

Copy link
Copy Markdown
Contributor

👋 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!

@github-actions

Copy link
Copy Markdown
Contributor

✅ No conflicts with other open PRs targeting develop

@github-actions

Copy link
Copy Markdown
Contributor

I see you updated files related to core. Please run make gocs in the root directory to add a changeset as well as in the text include at least one of the following tags:

  • #added For any new functionality added.
  • #breaking_change For any functionality that requires manual action for the node to boot.
  • #bugfix For bug fixes.
  • #changed For any change to the existing functionality.
  • #db_update For any feature that introduces updates to database schema.
  • #deprecation_notice For any upcoming deprecation functionality.
  • #internal For changesets that need to be excluded from the final changelog.
  • #nops For any feature that is NOP facing and needs to be in the official Release Notes for the release.
  • #removed For any functionality/config that is removed.
  • #updated For any functionality that is updated.
  • #wip For any change that is not ready yet and external communication about it should be held off till it is feature complete.

@trunk-io

trunk-io Bot commented Sep 27, 2026 •

Copy link
Copy Markdown

Static Badge   Static Badge   Static Badge

View Full Report ↗︎ ⋅ Docs

@github-actions

github-actions Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

CORA - Pending Reviewers

All codeowners have approved! ✅

Legend: ✅ Approved | ❌ Changes Requested | 💬 Commented | 🚫 Dismissed | ⏳ Pending | ❓ Unknown

For more details, see the full review summary.

@prashantkumar1982

Copy link
Copy Markdown
Contributor Author

/vault-audit

@app-token-issuer-foundations

app-token-issuer-foundations Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

⚠️ Vault audit complete for b158b5c5 — 5 blocking finding(s) (CRITICAL/MUST FIX) must be addressed before merge.

📋 View the full report and findings — private tracking issue, visible to chainlink team members only. Resolve blocking findings there with /resolved <FINDING-ID> <reason>.

@prashantkumar1982

Copy link
Copy Markdown
Contributor Author

/vault-audit

@app-token-issuer-foundations

app-token-issuer-foundations Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

⚠️ Vault audit complete for 13edc14b — 4 blocking finding(s) (CRITICAL/MUST FIX) must be addressed before merge.

📋 View the full report and findings — private tracking issue, visible to chainlink team members only. Resolve blocking findings there with /resolved <FINDING-ID> <reason>.

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)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Also see the audit items, there are a few blockers there)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
@prashantkumar1982

Copy link
Copy Markdown
Contributor Author

/vault-audit

@app-token-issuer-foundations

app-token-issuer-foundations Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

↩️ Superseded by a newer vault audit run.

@app-token-issuer-foundations

app-token-issuer-foundations Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

⚠️ Vault audit complete for 4905dc90 — 7 blocking finding(s) (CRITICAL/MUST FIX) must be addressed before merge.

📋 View the full report and findings — private tracking issue, visible to chainlink team members only. Resolve blocking findings there with /resolved <FINDING-ID> <reason>.

@prashantkumar1982
prashantkumar1982 requested a review from a team as a code owner September 30, 2026 18:06
@prashantkumar1982 prashantkumar1982 changed the title vault: wire reshare zero-downtime PublicKey flags vault: reshare zero-downtime PublicKey (plugin, capability, engine, relay) Sep 30, 2026
@cl-sonarqube-production

Copy link
Copy Markdown

@prashantkumar1982

Copy link
Copy Markdown
Contributor Author

/vault-audit

@app-token-issuer-foundations

app-token-issuer-foundations Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

⚠️ Vault audit complete for f9828aa4 — 2 blocking finding(s) (CRITICAL/MUST FIX) must be addressed before merge.

📋 View the full report and findings — private tracking issue, visible to chainlink team members only. Resolve blocking findings there with /resolved <FINDING-ID> <reason>.

@app-token-issuer-foundations

Copy link
Copy Markdown

✅ All vault-audit blocking findings on this PR have been resolved by reviewers. The vault-audit status check is now green.

📋 Tracking issue

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants