feat(deployment): download attestation evidence for confidential compute leases#3359
Conversation
📝 WalkthroughWalkthroughAdds attestation quote types and nonce generation, fetches evidence through provider proxy calls authenticated with scoped provider JWTs, exposes a React Query mutation, and adds deployment UI for opening, downloading, retrying, and rendering attestation evidence. ChangesConfidential compute attestation
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
Suggested reviewers
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3359 +/- ##
==========================================
- Coverage 69.62% 68.24% -1.39%
==========================================
Files 1088 998 -90
Lines 26651 24330 -2321
Branches 6406 5949 -457
==========================================
- Hits 18555 16603 -1952
+ Misses 7105 6768 -337
+ Partials 991 959 -32
*This pull request uses carry forward flags. Click here to find out more.
🚀 New features to boost your workflow:
|
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (2)
apps/deploy-web/src/components/deployments/AttestationEvidenceModal.spec.tsx (1)
60-63: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAvoid casting the mocked hook result through
unknown.This can hide missing fields on the mutation state and makes the test less trustworthy than it looks. Use
mock<ReturnType<typeof DEPENDENCIES.useAttestationQuoteMutation>>({...})for the returned value instead. As per coding guidelines, "Use vitest-mock-extended withmock()andMockProxy<T>for mocking in tests" and "Usemock<T>()instead ofas unknown as <Type>for type casting in tests."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/deploy-web/src/components/deployments/AttestationEvidenceModal.spec.tsx` around lines 60 - 63, The mocked return value in AttestationEvidenceModal.spec.tsx is being forced through unknown, which can mask missing mutation-state fields. Update the useAttestationQuoteMutation test double to return a properly typed mock using mock<ReturnType<typeof DEPENDENCIES.useAttestationQuoteMutation>>({...}) instead of an as unknown as cast, so the mocked hook result stays fully type-checked and aligned with the real mutation shape.Source: Coding guidelines
apps/deploy-web/src/services/provider-proxy/provider-proxy.service.spec.ts (1)
1132-1144: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse a typed
MockProxy<HttpClient>here instead of casting throughunknown.The
as unknown as HttpClient/as Mockcasts switch off the type checking this test should be giving you aroundpost(). Building the client withmock<HttpClient>()and stubbingpostafterward keeps the request-shape assertions type-safe. As per coding guidelines, "Use vitest-mock-extended withmock()andMockProxy<T>for mocking in tests" and "Usemock<T>()instead ofas unknown as <Type>for type casting in tests."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/deploy-web/src/services/provider-proxy/provider-proxy.service.spec.ts` around lines 1132 - 1144, The test is bypassing type safety by casting the HttpClient mock through unknown and then treating post as an untyped Mock. Update the fetchAttestationQuote test setup to use a typed MockProxy<HttpClient> created with mock() and stub post directly on that mock, then read the call arguments from the typed mock instead of casting. Keep the assertions around service.fetchAttestationQuote and the payload shape, but remove the as unknown as HttpClient and as Mock casts so the post() request checks remain type-safe.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@apps/deploy-web/src/components/deployments/DownloadAttestationEvidence.tsx`:
- Around line 29-42: The launcher in DownloadAttestationEvidence should also be
blocked when provider is missing, since Props allows a nullable provider and
LeaseRow can pass an undefined result from providers?.find(...). Update the
early-return guard in DownloadAttestationEvidence to require provider before
rendering the button/modal, so the AttestationEvidenceModal is never opened in a
state that will immediately fail with “Provider is not available for this
lease.”
In `@apps/deploy-web/src/services/provider-proxy/provider-proxy.service.ts`:
- Around line 62-67: The attestation quote fetch in provider-proxy.service.ts is
missing a finite timeout, so a stalled provider can leave the flow pending
forever. Update the request call inside the provider proxy attestation quote
path to pass a timeout value through this request() invocation, using the
existing request timeout support, and keep the current error handling/retry
behavior intact. Locate the change around the request<AttestationQuote> call in
the provider proxy service.
- Around line 54-68: The attestation quote flow in fetchAttestationQuote
generates a nonce but does not return it, so callers cannot persist the
freshness challenge alongside the quote. Update fetchAttestationQuote in
provider-proxy.service.ts to include the generated nonce in its returned result,
and adjust the AttestationQuote shape or returned object construction as needed
so the nonce is available to the download flow together with response.data.
In `@apps/deploy-web/src/utils/confidentialCompute.ts`:
- Around line 198-210: The attestation payload types currently overstate the
provider contract: in GpuAttestationReport and AttestationQuote, fields like
report, tee_platform, index, and gpu_reports are marked required even though the
gateway boundary can omit them. Update these type definitions to reflect the raw
provider response by making the boundary fields optional, or add a
validation/narrowing step before exposing AttestationQuote so downstream code
only sees a truly strict shape. Use the existing GpuAttestationReport and
AttestationQuote symbols in confidentialCompute.ts to keep the raw input
contract aligned with the documented behavior.
---
Nitpick comments:
In
`@apps/deploy-web/src/components/deployments/AttestationEvidenceModal.spec.tsx`:
- Around line 60-63: The mocked return value in
AttestationEvidenceModal.spec.tsx is being forced through unknown, which can
mask missing mutation-state fields. Update the useAttestationQuoteMutation test
double to return a properly typed mock using mock<ReturnType<typeof
DEPENDENCIES.useAttestationQuoteMutation>>({...}) instead of an as unknown as
cast, so the mocked hook result stays fully type-checked and aligned with the
real mutation shape.
In `@apps/deploy-web/src/services/provider-proxy/provider-proxy.service.spec.ts`:
- Around line 1132-1144: The test is bypassing type safety by casting the
HttpClient mock through unknown and then treating post as an untyped Mock.
Update the fetchAttestationQuote test setup to use a typed MockProxy<HttpClient>
created with mock() and stub post directly on that mock, then read the call
arguments from the typed mock instead of casting. Keep the assertions around
service.fetchAttestationQuote and the payload shape, but remove the as unknown
as HttpClient and as Mock casts so the post() request checks remain type-safe.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 80053c99-7848-431f-9761-f968dd8eea6a
📒 Files selected for processing (12)
apps/deploy-web/src/components/deployments/AttestationEvidenceModal.spec.tsxapps/deploy-web/src/components/deployments/AttestationEvidenceModal.tsxapps/deploy-web/src/components/deployments/DownloadAttestationEvidence.spec.tsxapps/deploy-web/src/components/deployments/DownloadAttestationEvidence.tsxapps/deploy-web/src/components/deployments/LeaseRow.tsxapps/deploy-web/src/queries/useAttestationQuoteMutation.spec.tsapps/deploy-web/src/queries/useAttestationQuoteMutation.tsapps/deploy-web/src/services/provider-proxy/provider-proxy.service.spec.tsapps/deploy-web/src/services/provider-proxy/provider-proxy.service.tsapps/deploy-web/src/types/feature-flags.tsapps/deploy-web/src/utils/confidentialCompute.spec.tsapps/deploy-web/src/utils/confidentialCompute.ts
PR #3353 added "attestation" to the global managed-wallet provider JWT scope unconditionally. Field and non-TEE providers validate the scope against an enum that does not include "attestation" and reject the entire JWT with "JWT has invalid claims", so manifest sends fail for every managed deployment and the deployment never goes active. Revert the global scope back to the base set. The attestation scope is only needed to call the provider attestationQuote endpoint, which has no consumer on this token; it moves to a per-provider granular token for confidential compute attestation-evidence downloads (#3359).
…ute leases Surface a download for a running Confidential Compute lease's hardware-signed attestation evidence (AEP-83 §5). A fresh 64-byte nonce is sent per request so the returned evidence is bound to that request; the preview distinguishes the single CPU report from per-GPU reports and saves the full bundle as JSON. Fetched over the existing JWT provider-proxy path (the managed-wallet token carries the `attestation` scope) — front-end only, no backend change. Gated behind the new `ui_confidential_compute` flag and only shown for running deployments whose on-chain group declares a tee/type. Part of CON-540
… and extract report label
d0ad120 to
01b95b3
Compare
…wnload The provider attestationQuote endpoint requires the `attestation` JWT scope. #3360 removed that scope from the shared global managed-wallet token because non-TEE providers reject it. Add useProviderJwt.generateScopedProviderToken to mint an ephemeral, single-provider granular token carrying only `attestation`, and use it from useAttestationQuoteMutation instead of the global token. Only TEE-capable providers accept the scope, and the download trigger already renders solely for confidential-compute leases, so the scoped token is requested only where the provider accepts it. Ref CON-575
01b95b3 to
3feebb6
Compare
…d the fetch Return the per-request nonce alongside the quote and include it in the downloaded bundle so the evidence can be verified for freshness against the hardware-signed report_data. Give the attestation quote fetch a finite 60s timeout so a stalled provider surfaces the modal's error/retry UI instead of hanging in a pending state. Addresses CodeRabbit review feedback on PR #3359.
…ion download The download already only renders for live leases whose on-chain group declares a TEE type, so it never surfaces for non-Confidential-Compute deployments. The feature flag was redundant.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
apps/deploy-web/src/hooks/useProviderJwt/useProviderJwt.spec.tsx (1)
105-107: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAvoid
as unknown as HttpClientin mocks.These casts bypass the test mocking convention; create the mock as
mock<HttpClient>()and configureposton the mock instead. As per coding guidelines, “Usemock<T>()instead ofas unknown as <Type>for type casting in tests.”Also applies to: 137-137, 151-151
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@apps/deploy-web/src/hooks/useProviderJwt/useProviderJwt.spec.tsx` around lines 105 - 107, The test mock setup for consoleApiHttpClient in useProviderJwt.spec.tsx should avoid the unsafe `as unknown as HttpClient` cast and follow the test mocking convention instead. Update the mock creation to use `mock<HttpClient>()` directly, then configure the `post` method on that mock for the resolved token response; apply the same pattern to the other affected mock instances in this spec so the tests rely on typed mocks rather than type assertions.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@apps/deploy-web/src/hooks/useProviderJwt/useProviderJwt.spec.tsx`:
- Around line 105-107: The test mock setup for consoleApiHttpClient in
useProviderJwt.spec.tsx should avoid the unsafe `as unknown as HttpClient` cast
and follow the test mocking convention instead. Update the mock creation to use
`mock<HttpClient>()` directly, then configure the `post` method on that mock for
the resolved token response; apply the same pattern to the other affected mock
instances in this spec so the tests rely on typed mocks rather than type
assertions.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
Run ID: 9184cf18-bbc4-4c27-ad69-86ba32e85bbe
📒 Files selected for processing (13)
apps/deploy-web/src/components/deployments/AttestationEvidenceModal.spec.tsxapps/deploy-web/src/components/deployments/AttestationEvidenceModal.tsxapps/deploy-web/src/components/deployments/DownloadAttestationEvidence.spec.tsxapps/deploy-web/src/components/deployments/DownloadAttestationEvidence.tsxapps/deploy-web/src/components/deployments/LeaseRow.tsxapps/deploy-web/src/hooks/useProviderJwt/useProviderJwt.spec.tsxapps/deploy-web/src/hooks/useProviderJwt/useProviderJwt.tsapps/deploy-web/src/queries/useAttestationQuoteMutation.spec.tsapps/deploy-web/src/queries/useAttestationQuoteMutation.tsapps/deploy-web/src/services/provider-proxy/provider-proxy.service.spec.tsapps/deploy-web/src/services/provider-proxy/provider-proxy.service.tsapps/deploy-web/src/utils/confidentialCompute.spec.tsapps/deploy-web/src/utils/confidentialCompute.ts
🚧 Files skipped from review as they are similar to previous changes (7)
- apps/deploy-web/src/components/deployments/LeaseRow.tsx
- apps/deploy-web/src/utils/confidentialCompute.spec.ts
- apps/deploy-web/src/services/provider-proxy/provider-proxy.service.ts
- apps/deploy-web/src/utils/confidentialCompute.ts
- apps/deploy-web/src/components/deployments/AttestationEvidenceModal.spec.tsx
- apps/deploy-web/src/services/provider-proxy/provider-proxy.service.spec.ts
- apps/deploy-web/src/components/deployments/AttestationEvidenceModal.tsx
…ute leases Port akash-network/console#3359 (upstream CON-540) into the self-custody fork, adapted to mTLS. From a running Confidential Compute lease's detail view the tenant can fetch and download the hardware-signed attestation evidence to verify the workload independently. - generateAttestationNonce + attestation types (AttestationQuote/Evidence) in confidentialCompute, with a fresh 64-byte nonce bound to each request - provider-proxy fetchAttestationQuote: POST /lease/{dseq}/{gseq}/{oseq}/attestation/quote - useAttestationQuoteMutation, the evidence preview modal, and a self-gating trigger wired into the lease row Auth adaptation: upstream fetches the quote over a managed-wallet granular JWT (generateScopedProviderToken / /v1/create-jwt-token), which this fork has no equivalent for. Instead the call reuses useProviderCredentials().details (mTLS, JWT only when the chain is down) — the same path status/logs/shell/manifest already use. The download trigger gates on live lease + on-chain tee/type + usable credentials, matching the fork convention so it never fires an uncredentialed provider request (the deployment view shows a CreateCredentialsButton when no usable credential exists).
…ute leases (#38) Port akash-network/console#3359 (upstream CON-540) into the self-custody fork, adapted to mTLS. From a running Confidential Compute lease's detail view the tenant can fetch and download the hardware-signed attestation evidence to verify the workload independently. - generateAttestationNonce + attestation types (AttestationQuote/Evidence) in confidentialCompute, with a fresh 64-byte nonce bound to each request - provider-proxy fetchAttestationQuote: POST /lease/{dseq}/{gseq}/{oseq}/attestation/quote - useAttestationQuoteMutation, the evidence preview modal, and a self-gating trigger wired into the lease row Auth adaptation: upstream fetches the quote over a managed-wallet granular JWT (generateScopedProviderToken / /v1/create-jwt-token), which this fork has no equivalent for. Instead the call reuses useProviderCredentials().details (mTLS, JWT only when the chain is down) — the same path status/logs/shell/manifest already use. The download trigger gates on live lease + on-chain tee/type + usable credentials, matching the fork convention so it never fires an uncredentialed provider request (the deployment view shows a CreateCredentialsButton when no usable credential exists).
…kash-network#3360) PR akash-network#3353 added "attestation" to the global managed-wallet provider JWT scope unconditionally. Field and non-TEE providers validate the scope against an enum that does not include "attestation" and reject the entire JWT with "JWT has invalid claims", so manifest sends fail for every managed deployment and the deployment never goes active. Revert the global scope back to the base set. The attestation scope is only needed to call the provider attestationQuote endpoint, which has no consumer on this token; it moves to a per-provider granular token for confidential compute attestation-evidence downloads (akash-network#3359).
Why
Tenants running a Confidential Compute (TEE) workload need to obtain the hardware-signed attestation evidence for their running lease so they can independently confirm the workload is genuinely running inside a Trusted Execution Environment.
Part of CON-540
What
From the deployment detail view of a running Confidential Compute lease (and only when behind the new
ui_confidential_computeflag, and the on-chain group declares atee/type), the tenant can fetch and download the attestation evidence:POST /lease/{dseq}/{gseq}/{oseq}/attestation/quotevia the existing JWT provider-proxy path — front-end only, no API/provider-proxy/backend change.{ nonce, bind_tls: false }) so the evidence is bound to that request.Changes
ui_confidential_computeadded to theFeatureFlagunion.generateAttestationNonce()+AttestationQuote/TeePlatformtypes inconfidentialCompute.ts.ProviderProxyService.fetchAttestationQuote().useAttestationQuoteMutation(mutation, not query — evidence is never cached); mints a per-providerattestation-scoped token viauseProviderJwt.generateScopedProviderTokeninstead of using the global token.useProviderJwt.generateScopedProviderToken— ephemeral, single-provider granular token (access: "granular", not persisted to the JWT atom / managed-wallet storage).AttestationEvidenceModal+DownloadAttestationEvidence, wired intoLeaseRow.Tests cover the nonce util, the service request shape, the mutation hook, and both components (76 passing across the affected files).
Follow-ups / notes
AttestationQuoteshape (esp.gpu_reports[].index/.report) is from AEP-83 §5 and should be confirmed against the live CC provider (pro6000.hou,snp-gpu) before merge.ui_confidential_computemust be registered server-side; it defaults off. UseNEXT_PUBLIC_UNLEASH_ENABLE_ALLfor local testing.Summary by CodeRabbit
New Features
Bug Fixes