Skip to content

feat(api-keys): surface wallet policy access#545

Merged
GuiBibeau merged 4 commits into
mainfrom
codex/pro-1364-api-key-wallet-access-flow
Jun 30, 2026
Merged

feat(api-keys): surface wallet policy access#545
GuiBibeau merged 4 commits into
mainfrom
codex/pro-1364-api-key-wallet-access-flow

Conversation

@GuiBibeau

Copy link
Copy Markdown
Collaborator

Summary

  • Add API key wallet access metadata to list/detail responses, including wallet bindings and read-only policy binding summaries with active profile revision IDs.
  • Surface API key wallet/policy access in the dashboard create review step and API keys table.
  • Make the API keys table progressively responsive and simplify the Access copy to a natural two-line summary.

Screenshots

Captured locally before opening the PR:

  • Table: /tmp/pro1364-api-keys-table-desktop.png
  • Review modal: /tmp/pro1364-api-key-review-modal.png

Note: gh gist create rejected PNG uploads as binary files, so these are recorded as local artifacts rather than embedded images.

Validation

  • pnpm exec biome check --write apps/sdp-api/src/openapi/schemas/api-keys.ts apps/sdp-api/src/routes/api-keys-wallet-scope.test.ts apps/sdp-api/src/routes/api-keys/handlers.ts apps/sdp-api/src/routes/api-keys/access-response.ts apps/sdp-api/src/routes/projects/handlers/api-keys.ts apps/sdp-api/src/services/api-key.service.ts apps/sdp-web/playwright.config.ts apps/sdp-web/playwright/tests/api-keys.e2e.spec.ts apps/sdp-web/src/app/dashboard/api-keys/api-keys-table-client.tsx apps/sdp-web/src/app/dashboard/api-keys/create-api-key-modal.tsx apps/sdp-web/src/app/dashboard/api-keys/page.tsx packages/sdp-types/src/api-keys.ts
  • pnpm --filter @sdp/types typecheck
  • pnpm --filter sdp-web typecheck
  • pnpm --filter @sdp/api typecheck
  • pnpm --filter @sdp/api test:node -- src/routes/api-keys-wallet-scope.test.ts
  • pnpm -C apps/sdp-api openapi:generate (no tracked generated diff)
  • doppler run --project solana-developer-platform --config "${DOPPLER_CONFIG:-dev_personal}" -- env DATABASE_URL="${DATABASE_URL:-postgresql://sdp:sdp@127.0.0.1:5432/sdp}" CLOUDFLARE_HYPERDRIVE_LOCAL_CONNECTION_STRING_HYPERDRIVE="${DATABASE_URL:-postgresql://sdp:sdp@127.0.0.1:5432/sdp}" PLAYWRIGHT_API_PORT=8794 PLAYWRIGHT_API_URL=http://127.0.0.1:8794 PLAYWRIGHT_API_PERSIST_PATH=.wrangler/state-playwright-pro1364-pr PLAYWRIGHT_BASE_URL=http://localhost:3114 PLAYWRIGHT_NEXT_DIST_DIR=.next-playwright-pro1364-pr SDP_API_BASE_URL=http://127.0.0.1:8794 NEXT_PUBLIC_SDP_API_BASE_URL=http://127.0.0.1:8794 pnpm -C apps/sdp-web exec playwright test --config=playwright.config.ts --project=dashboard playwright/tests/api-keys.e2e.spec.ts

Linear: PRO-1364

@linear

linear Bot commented Jun 29, 2026

Copy link
Copy Markdown

PRO-1364

@vercel

vercel Bot commented Jun 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
sdp-docs Ready Ready Preview, Comment Jun 29, 2026 7:23pm
sdp-web Ready Ready Preview, Comment Jun 29, 2026 7:23pm

Request Review

@GuiBibeau

Copy link
Copy Markdown
Collaborator Author

@greptileai review

@GuiBibeau GuiBibeau changed the title PRO-1364: Surface API key wallet policy access feat(api-keys): surface wallet policy access Jun 29, 2026
@greptile-apps

greptile-apps Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR surfaces wallet binding and policy binding metadata in the API key list and detail responses, and reflects that data in both the dashboard table and the create-key review modal. The core change replaces per-key wallet-binding lookups with a single buildApiKeyAccessSummaries helper that batches all four necessary queries into two parallel rounds.

  • Batched access queries (access-response.ts): collects all wallet bindings and policy bindings for a set of API key IDs in one query each, then resolves active profile revision IDs in a second parallel round — eliminating the previous per-key fan-out.
  • API shape expanded: ListApiKeysResponse and the detail schema now include walletScope, signingWalletId, signingWalletIds, walletBindings, and policyBindings; the @sdp/types ApiKeyWalletPolicyBindingSummary interface is introduced and imported from the package instead of being declared locally.
  • UI updates: the API keys table gains a responsive, container-query-driven layout with a consolidated "Access" column; the create-modal review step is redesigned with icon-labelled review details and a hardcoded "No API-key policy" badge (policies cannot be set at key creation time).

Confidence Score: 4/5

The PR is safe to merge; the batching improvement is correct and the new UI surfaces accurate data for the common case.

The wallet_scope field exposed in both the list and detail SQL queries is computed with a CASE WHEN EXISTS correlated subquery rather than a stored column (no such column exists in the schema). In the steady state this is correct, but if all wallet-permission rows for a key that was originally created as "selected" scope are removed, the list and detail endpoints will surface "all" scope for that key, silently broadening its access. The concern was already raised in a prior review; the current PR moves the derivation from JS into SQL but does not close the schema gap. All other changes — batched queries, type alignment, UI rework — are well-constructed.

apps/sdp-api/src/services/api-key.service.ts — the CASE WHEN EXISTS subquery is the only source of truth for wallet scope; a future migration adding a stored wallet_scope column would make both queries simpler and the scope semantics explicit.

Important Files Changed

Filename Overview
apps/sdp-api/src/routes/api-keys/access-response.ts New module that batches wallet and policy binding lookups for multiple API keys in two parallel query rounds — a sound replacement for the previous per-key fan-out pattern.
apps/sdp-api/src/services/api-key.service.ts Adds wallet_scope and signing_wallet_id to both SQL queries using a correlated CASE WHEN EXISTS subquery (no wallet_scope column exists in the schema); if all wallet-permission rows for a "selected" key are later removed the scope silently reverts to "all".
apps/sdp-api/src/db/repositories/policy.repository.postgres.ts Adds three batched repository methods for listing policy bindings and resolving active revision refs for sets of profile IDs; pattern is consistent with existing repository style.
apps/sdp-api/src/routes/api-keys/handlers.ts List and detail handlers now delegate to buildApiKeyAccessSummaries and expose wallet/policy data; the detail handler no longer derives walletScope from binding count in JS, delegating to the SQL-level derivation instead.
apps/sdp-api/src/routes/projects/handlers/api-keys.ts Project-scoped list handler updated in parallel with the user-scoped handler; same batching pattern applied consistently.
packages/sdp-types/src/api-keys.ts Introduces ApiKeyWalletPolicyBindingSummary and expands ListApiKeysResponse with required wallet/policy fields; new fields on ApiKey and CachedApiKey are correctly optional for backward-compatible access.
apps/sdp-web/src/app/dashboard/api-keys/api-keys-table-client.tsx Table client now imports types from @sdp/types (fixing the previous local-redeclaration concern), adds an AccessSummary component, and applies container-query-driven responsive visibility.
apps/sdp-web/src/app/dashboard/api-keys/create-api-key-modal.tsx Review step redesigned with icon-labelled ReviewDetail sections; the "No API-key policy" badge is correctly hardcoded since policies cannot be set at creation time.
apps/sdp-api/src/routes/api-keys-wallet-scope.test.ts New integration test covers list and detail responses for a key with a policy binding, verifying active revision ID resolution end-to-end.
apps/sdp-api/src/services/api-key-wallets.service.ts Adds listApiKeyWalletBindingsForApiKeys for bulk lookup and corrects safeParsePermissions parameter type from string to unknown.

Sequence Diagram

%%{init: {'theme': 'neutral'}}%%
sequenceDiagram
    participant C as Client
    participant H as Handler (listApiKeys / getApiKey)
    participant AKS as ApiKeyService
    participant BAS as buildApiKeyAccessSummaries
    participant WB as api_key_wallet_permissions
    participant PB as api_key_wallet_policy_bindings
    participant WR as wallet_control_profiles
    participant AR as api_key_control_profiles

    C->>H: GET /v1/api-keys (or /v1/api-keys/:id)
    H->>AKS: listForProject(projectId)
    AKS->>AKS: SQL: CASE WHEN EXISTS wallet_permissions → wallet_scope
    AKS-->>H: ApiKeyListItem[]
    H->>BAS: buildApiKeyAccessSummaries(env, db, keyIds)
    par Round 1 (parallel)
        BAS->>WB: listApiKeyWalletBindingsForApiKeys(keyIds)
        WB-->>BAS: ApiKeyWalletBindingForKey[]
        BAS->>PB: listApiKeyWalletPolicyBindingsForApiKeys(keyIds)
        PB-->>BAS: ApiKeyWalletPolicyBindingRow[]
    end
    par Round 2 (parallel, driven by profile IDs from Round 1)
        BAS->>WR: listActiveWalletControlProfileRevisionRefs(profileIds)
        WR-->>BAS: ActivePolicyProfileRevisionRefRow[]
        BAS->>AR: listActiveApiKeyControlProfileRevisionRefs(profileIds)
        AR-->>BAS: ActivePolicyProfileRevisionRefRow[]
    end
    BAS-->>H: "Map<keyId, ApiKeyAccessSummary>"
    H-->>C: Response with walletBindings + policyBindings + walletScope
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
sequenceDiagram
    participant C as Client
    participant H as Handler (listApiKeys / getApiKey)
    participant AKS as ApiKeyService
    participant BAS as buildApiKeyAccessSummaries
    participant WB as api_key_wallet_permissions
    participant PB as api_key_wallet_policy_bindings
    participant WR as wallet_control_profiles
    participant AR as api_key_control_profiles

    C->>H: GET /v1/api-keys (or /v1/api-keys/:id)
    H->>AKS: listForProject(projectId)
    AKS->>AKS: SQL: CASE WHEN EXISTS wallet_permissions → wallet_scope
    AKS-->>H: ApiKeyListItem[]
    H->>BAS: buildApiKeyAccessSummaries(env, db, keyIds)
    par Round 1 (parallel)
        BAS->>WB: listApiKeyWalletBindingsForApiKeys(keyIds)
        WB-->>BAS: ApiKeyWalletBindingForKey[]
        BAS->>PB: listApiKeyWalletPolicyBindingsForApiKeys(keyIds)
        PB-->>BAS: ApiKeyWalletPolicyBindingRow[]
    end
    par Round 2 (parallel, driven by profile IDs from Round 1)
        BAS->>WR: listActiveWalletControlProfileRevisionRefs(profileIds)
        WR-->>BAS: ActivePolicyProfileRevisionRefRow[]
        BAS->>AR: listActiveApiKeyControlProfileRevisionRefs(profileIds)
        AR-->>BAS: ActivePolicyProfileRevisionRefRow[]
    end
    BAS-->>H: "Map<keyId, ApiKeyAccessSummary>"
    H-->>C: Response with walletBindings + policyBindings + walletScope
Loading

Reviews (5): Last reviewed commit: "Merge branch 'main' into codex/pro-1364-..." | Re-trigger Greptile

Comment thread apps/sdp-api/src/routes/api-keys/access-response.ts
Comment thread apps/sdp-web/src/app/dashboard/api-keys/api-keys-table-client.tsx Outdated
@GuiBibeau

Copy link
Copy Markdown
Collaborator Author

@greptileai review

@GuiBibeau

Copy link
Copy Markdown
Collaborator Author

@greptileai review

@GuiBibeau

Copy link
Copy Markdown
Collaborator Author

Ready for QA.

Validation:

  • Greptile Review: 5/5 on latest commit
  • CI: green on latest commit, including Dashboard E2E and API/Surfpool suites
  • Local: @sdp/api typecheck, @sdp/types typecheck, sdp-web typecheck, focused API wallet-scope tests, and dashboard API-key E2E passed

Focus areas for QA:

  • Create API key review modal shows endpoint permissions, wallet access, policy status, and security note clearly
  • API keys table stays readable across smaller widths without horizontal scrolling
  • API key list/detail responses include walletBindings and policyBindings consistently

@GuiBibeau GuiBibeau merged commit ec3fc29 into main Jun 30, 2026
40 of 42 checks passed
@GuiBibeau GuiBibeau deleted the codex/pro-1364-api-key-wallet-access-flow branch June 30, 2026 15:00
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.

2 participants