Conversation
Instantiate the confidential-compute relay handler as a CRE subservice. The handler validates Nitro attestation and dispatches secrets.get / capability.execute messages to VaultDON and capability DONs via the gateway connector. Gated by CL_CONFIDENTIAL_RELAY_TRUSTED_PCRS env var (JSON with PCR0-2 hex values). When unset, no relay service is started.
|
👋 nadahalli, 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 |
There was a problem hiding this comment.
Pull request overview
Wires the confidential-compute relay handler into the CRE service graph as an optional subservice, gated by CL_CONFIDENTIAL_RELAY_TRUSTED_PCRS, and updates dependencies to pull in required relay/confidentialrelay types.
Changes:
- Add a new
core/capabilities/confidentialrelaylifecycle wrapper that defers handler construction toStart(). - Wire the confidential relay service into
core/services/crewhenCL_CONFIDENTIAL_RELAY_TRUSTED_PCRSis set. - Bump
chainlink-commonand add confidential-compute relay/attestation dependencies.
Reviewed changes
Copilot reviewed 3 out of 4 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| go.mod | Adds confidential-compute relay dependency and bumps chainlink-common. |
| go.sum | Updates module sums for new/bumped dependencies. |
| core/services/cre/cre.go | Conditionally registers the confidential relay service behind an env var gate. |
| core/capabilities/confidentialrelay/service.go | Introduces a thin service wrapper around the confidential relay handler lifecycle. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if trustedPCRs := os.Getenv("CL_CONFIDENTIAL_RELAY_TRUSTED_PCRS"); trustedPCRs != "" { | ||
| relayService := confidentialrelay.NewService( | ||
| gatewayConnectorWrapper, | ||
| opts.CapabilitiesRegistry, | ||
| []byte(trustedPCRs), | ||
| lggr, | ||
| ) | ||
| srvs = append(srvs, relayService) | ||
| } |
There was a problem hiding this comment.
The relay service is conditionally enabled via CL_CONFIDENTIAL_RELAY_TRUSTED_PCRS, but there’s no log signal when it is enabled/disabled. This makes it hard to diagnose why the relay handler isn’t running (especially since other conditional subservices here log when they are skipped/created). Consider adding a Debug/Info log when the env var is set (service enabled) and when it’s unset (service disabled).
| lggr, | ||
| ) | ||
| srvs = append(srvs, relayService) | ||
| } |
There was a problem hiding this comment.
If CL_CONFIDENTIAL_RELAY_TRUSTED_PCRS is set but capCfg.GatewayConnector().DonID() is empty, the relay will silently not start because the gateway connector wrapper isn’t created. Consider logging a warning in that case (env var set but gateway connector not configured), since it’s a misconfiguration that’s otherwise non-obvious.
| } | |
| } | |
| } else { | |
| if trustedPCRs := os.Getenv("CL_CONFIDENTIAL_RELAY_TRUSTED_PCRS"); trustedPCRs != "" { | |
| lggr.Warn("CL_CONFIDENTIAL_RELAY_TRUSTED_PCRS is set but GatewayConnector DonID is empty; confidential relay will not start") | |
| } |
| h, err := relay.NewHandler(s.capRegistry, conn, s.trustedPCRs, s.lggr) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| s.handler = h | ||
| return h.Start(ctx) |
There was a problem hiding this comment.
start returns raw errors from relay.NewHandler/h.Start without adding context. To make startup failures actionable in logs, wrap these errors with service-specific context (e.g., include that this is the confidential relay handler startup and whether it was due to handler construction vs start).
| func (s *Service) start(ctx context.Context) error { | ||
| conn := s.wrapper.GetGatewayConnector() | ||
| if conn == nil { | ||
| return errors.New("gateway connector not available") | ||
| } | ||
| h, err := relay.NewHandler(s.capRegistry, conn, s.trustedPCRs, s.lggr) | ||
| if err != nil { | ||
| return err | ||
| } | ||
| s.handler = h | ||
| return h.Start(ctx) | ||
| } | ||
|
|
||
| func (s *Service) close() error { | ||
| if s.handler != nil { | ||
| return s.handler.Close() | ||
| } | ||
| return nil | ||
| } |
There was a problem hiding this comment.
There are no unit tests covering the new lifecycle wrapper behavior (e.g., that it errors when the gateway connector isn’t available, and that it closes the underlying handler when started). Since the repo has extensive Go service tests elsewhere, consider adding a small test for Service.start/close using a fake ServiceWrapper/connector to prevent regressions.
|
Gateway-side handler that receives JSON-RPC requests from the enclave, fans them out to relay DON nodes, and aggregates 2F+1 quorum responses. Follows the vault handler pattern but simplified: no authorization, no caching, no OCR3 signatures, no owner-prefixed request IDs.
Add gateway handler type, capability flag, and Feature implementation so the CRE test framework can spin up a relay DON for remote-mode E2E tests.
Pass CL_CONFIDENTIAL_RELAY_CA_ROOTS_PEM env var to relay handler for custom attestation certificate validation. Update go.mod to use remote confidential-compute module commits.




Summary
Wire the confidential-compute relay handler (confidential-compute#265) into chainlink as a CRE subservice. The relay handler validates Nitro attestation and dispatches
confidential.secrets.get/confidential.capability.executemessages from the enclave to VaultDON and capability DONs via the gateway connector.The handler code lives in
confidential-compute/capabilities/relayas a standalone Go module. This PR imports it and wires it into the node's lifecycle.Changes
core/capabilities/confidentialrelay/service.go(new): Thin lifecycle wrapper around the relay handler. The relay handler needs the gateway connector at construction time, but the connector isn't available untilServiceWrapper.Start(). This wrapper defers handler creation to its ownStart(), bridging the gap.core/services/cre/cre.go: Instantiate the relay service inside the gateway connector block, gated byCL_CONFIDENTIAL_RELAY_TRUSTED_PCRSenv var.go.mod: Bumpchainlink-commonto3e3f9545d607(addsconfidentialrelaytypes), addconfidential-compute/capabilities/relayand transitive depconfidential-compute/attestation.Config: env var for now
Trusted PCR measurements are passed via
CL_CONFIDENTIAL_RELAY_TRUSTED_PCRSas a JSON string:When unset, no relay service is started (zero impact on nodes that don't use confidential workflows). Proper TOML config (
Capabilities.ConfidentialRelaysection) can be added in a follow-up; PCR values change rarely (only when the enclave binary is rebuilt), so an env var is sufficient for now and avoids config interface boilerplate.Dependency chain
Related PRs