feat: add workspace admin API foundations - #3314
Conversation
Add /admin/v1 workspace-admin endpoints (project, member, limits, user purge) to the dashboard OpenAPI contract with 501 stub handlers. Introduce a generic pkg/auth/jwks package (issuer config, OIDC discovery, JWKS-backed JWT verification) extracted from the oidc package, which is now a thin identity-resolution layer. The new AdminJWTAuth security scheme verifies EdDSA service JWTs configured via ADMIN_AUTH_CONFIG, sharing the auth.ProviderConfig shape with AUTH_PROVIDER_CONFIG.
- Move jwks, oidc, provider config parsing, admin verifier, and provider verifier from packages/auth/pkg/auth to packages/auth/pkg/token. - Rename Verifier/AdminJWTVerifier types to Provider/AdminVerifier. - Update auth package service, middleware, and identity lookup to consume the new token package. - Update api and dashboard-api config/model imports to use token package.
PR SummaryMedium Risk Overview Reviewed by Cursor Bugbot for commit b1f4a77. Bugbot is set up for automated code reviews on this repo. Configure here. |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Code Review
This pull request introduces the AdminJWTAuth security scheme to secure workspace-agnostic /admin/v1 operations in the dashboard API. It refactors the authentication package by separating generic JWKS token verification into a new jwks package and OIDC-specific identity lookup into an oidc package. Additionally, it defines the new admin endpoints in the OpenAPI specification, registers the corresponding middleware, and adds placeholder handlers. Feedback is provided to add a defensive nil check in the OIDC verifier's Verify method to prevent potential nil pointer dereferences.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
There was a problem hiding this comment.
No bugs found, but I'd recommend a human review given this touches auth/JWT verification code and introduces a new admin API surface.
Extended reasoning...
Overview
This PR does two things: (1) extracts JWT/OIDC verification code from packages/auth/pkg/auth into a new packages/auth/pkg/token package (with jwks and oidc sub-packages), shared between api and dashboard-api; and (2) adds a new /admin/v1 OpenAPI surface to dashboard-api for workspace/project control-plane administration, authenticated via a new AdminJWTAuth scheme backed by token.AdminVerifier (multi-issuer EdDSA service JWTs). All six new admin handlers are stubbed to return 501 Not Implemented, so no real admin logic ships yet.
Security risks
This is squarely auth-adjacent code: JWT signature verification, audience validation, and a new bearer-auth scheme protecting future workspace-admin write operations (project upsert/delete, membership, limits, user purge). The refactor itself appears to preserve behavior (the old Verifier.Verify logic was split into a claims-only jwks.Verifier plus an OIDC identity-resolution wrapper, and tests were moved/updated accordingly), and AdminVerifier correctly restricts to EdDSA and forces expiration/leeway. Since the concrete handlers are all not-implemented stubs, there's no immediate authz-bypass surface live yet, but the shape of AdminJWTAuth (accepting any of a list of configured issuers, verified but with no further scoping shown in this PR) is worth a human's attention before real handler logic lands on top of it.
Level of scrutiny
Given this touches shared authentication/verification code used by both api and dashboard-api, and lands the auth scheme for a new administrative API, I'd apply high scrutiny — this is exactly the kind of change (auth, JWT handling) called out as warranting human review rather than automated approval, even though the bug-hunting pass found nothing and the moved code is well covered by existing/updated unit tests.
Other factors
No CODEOWNERS conflicts were surfaced, and the diff is large (28 files) mixing a mechanical rename/move with genuinely new security-relevant surface (new security scheme, new config env var ADMIN_AUTH_CONFIG). The bug-hunting system found no issues, and one candidate doc-consistency concern in ARCHITECTURE.md was already investigated and ruled out. Test coverage for the moved/new verifier code looks solid (audience matching, expiry, wrong-signing-method rejection, disabled-config nil-verifier handling).
…startup warning - Add optional algorithm enum (EdDSA/ES256) to jwks.Issuer. - Validate supported algorithms during config validation. - Enforce the configured algorithm in jwks.Verifier. - Default admin JWT issuers to EdDSA when no algorithm is set. - Log a startup warning when dashboard-api ADMIN_AUTH_CONFIG is empty while /admin/v1 routes are compiled in. - Add tests for algorithm validation and ES256 admin JWT verification.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit b1f4a77. Configure here.
…3323) ## Problem `Build and upload images` has been failing on main for every environment since #3314 merged (first red run: [29827113375](https://github.com/e2b-dev/infra/actions/runs/29827113375), still red on the next push: [29827995997](https://github.com/e2b-dev/infra/actions/runs/29827995997)). No images have shipped since ~07:04 UTC and the staging envd promote hasn't run. #3314 refactored `packages/auth` so the public `pkg/auth` package re-exports from new `packages/auth/internal/*` packages (`service`, `team`, `token`, `token/jwks`, `middleware`, `authcontext`). The api image build copies the auth module selectively — `COPY ./auth/pkg ./auth/pkg` only — so `make build` inside the container fails: ``` ../auth/pkg/auth/service.go:9:2: no required module provides package github.com/e2b-dev/infra/packages/auth/internal/service ``` PR CI can't catch this class of breakage: unit/integration tests build from a full checkout, and the image Dockerfiles only build post-merge in `build-and-upload-images.yml`. ## Fix Add `COPY ./auth/internal ./auth/internal` next to the existing `auth/pkg` copy in: - `packages/api/Dockerfile` (actively failing) - `packages/dashboard-api/Dockerfile` (same bug, latent — fails as soon as that image is built) `auth/internal` only imports `db/pkg` and `shared/pkg`, which both Dockerfiles already copy, so nothing else is needed. ## Verification Replicated the exact Docker build context (only the `COPY`'d paths, no `go.work`) in a clean directory with go 1.26.5 and `CGO_ENABLED=0 GOOS=linux`: - **With this fix**: `go build` succeeds for both api and dashboard-api - **Without `auth/internal`** (negative control): reproduces the exact CI error 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- codesmith:footer --> --- <a href="https://app.blacksmith.sh/e2b-dev/codesmith/infra/pr/3323"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img alt="View with Codesmith" src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a> <a href="https://backend.blacksmith.sh/track/enable-autofix?expires=1787227846&installation_model_id=14389&pr_number=3323&repository=e2b-dev%2Finfra&return_to=https%3A%2F%2Fgithub.com%2Fe2b-dev%2Finfra%2Fpull%2F3323&signature=64edf20a2f3d46ecdbaf5674ca6dcc1d5ebd7d5c5ea1ad87289c0dc652162483"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img alt="Autofix with Codesmith" src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a> <sup>Need help on this PR? Tag <code>/codesmith</code> with what you need. Autofix is disabled.</sup> <!-- codesmith:autofix:disabled --> <!-- /codesmith:footer --> Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
…facade External consumers (belt) previously imported pkg/auth/oidc directly (oidc.NewVerifier, oidc.IdentityLookup, oidc.ErrIdentityNotFound) and used auth.Verifier/auth.NewVerifier from the old facade. Both surfaces were removed in #3314, which breaks belt's daily infra sync at go mod tidy (module found, but does not contain package .../pkg/auth/oidc) and at compile time in argus-api. Expose the equivalents on the auth facade so all consumers use auth.*: - IdentityLookup, ErrIdentityNotFound - OIDCVerifier, NewOIDCVerifier (single issuer) - ProviderVerifier, NewProviderVerifier (multi-issuer, keeps the (nil, nil) no-provider semantics of the old auth.NewVerifier) Config/issuer types were already re-exported as JWTConfig/JWTIssuer.
Move packages/auth/internal/* to packages/auth/pkg/auth/internal/* so the implementation lives inside the consumer-facing pkg tree. Consumers keep importing the stable facade at .../packages/auth/pkg/auth. Go's internal visibility now scopes the implementation to pkg/auth alone (pkg/types and pkg/tests can no longer reach it), and image builds that COPY ./auth/pkg pick up the implementation for free — drop the separate ./auth/internal COPY that #3323 added to unbreak image builds after #3314.
…facade (#3339) ## Summary Restores the auth surface external consumers lost in #3314, without touching the package layout. Belt imports this module and used `pkg/auth/oidc` (`oidc.NewVerifier`, `oidc.IdentityLookup`, `oidc.ErrIdentityNotFound`) plus the old facade's `auth.Verifier`/`auth.NewVerifier`. #3314 moved both behind Go `internal` packages, so belt's daily `sync-infra-repo` workflow fails at `go mod tidy`: > module …/packages/auth found, but does not contain package …/packages/auth/pkg/auth/oidc This PR re-exports the equivalents through the `auth.*` facade with identical signatures: - `auth.IdentityLookup`, `auth.ErrIdentityNotFound` - `auth.OIDCVerifier` / `auth.NewOIDCVerifier` (single issuer) - `auth.ProviderVerifier` / `auth.NewProviderVerifier` (multi-issuer; keeps the `(nil, nil)` no-provider semantics of the old `auth.NewVerifier`) Config/issuer types were already exposed as `auth.JWTConfig`/`auth.JWTIssuer`. Companion belt PR migrating its imports to the facade: e2b-dev/belt#1145. A follow-up PR (#3338) restructures the auth package layout separately. ## Testing - `go build ./… && go vet ./… && go test ./…` in `packages/auth` - `golangci-lint run ./packages/auth/…` — 0 issues - Belt compiled and tested against this commit (all 16 workspace modules build; `shared/pkg/auth`, `billing-server/internal/auth`, `argus-api/internal/handlers` green with `-race`) <!-- codesmith:footer --> --- <a href="https://app.blacksmith.sh/e2b-dev/codesmith/infra/pr/3339"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-light-v2.svg"><img alt="View with Codesmith" src="https://pr-comments-assets.blacksmith.sh/codesmith/view-with-codesmith-dark-v2.svg"></picture></a> <a href="https://backend.blacksmith.sh/track/enable-autofix?expires=1787336953&installation_model_id=14389&pr_number=3339&repository=e2b-dev%2Finfra&return_to=https%3A%2F%2Fgithub.com%2Fe2b-dev%2Finfra%2Fpull%2F3339&signature=5551f98aca8e66952d8160dd7d79e3502f3e6cd0cc1c464e571035de4efef169"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"><source media="(prefers-color-scheme: light)" srcset="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-light.svg"><img alt="Autofix with Codesmith" src="https://pr-comments-assets.blacksmith.sh/codesmith/autofix-with-codesmith-dark.svg"></picture></a> <sup>Need help on this PR? Tag <code>/codesmith</code> with what you need. Autofix is disabled.</sup> <!-- codesmith:autofix:disabled --> <!-- /codesmith:footer -->
## Summary Stacked on #3339 (facade re-exports — the belt unblocker); this PR is the layout change only. Retarget to `main` after #3339 merges. Moves `packages/auth/internal/*` → `packages/auth/pkg/auth/internal/*` (history-preserving renames; import paths and otel tracer names updated). Consumers keep using the stable facade at `github.com/e2b-dev/infra/packages/auth/pkg/auth` — nothing outside the auth module changes except two Dockerfile lines. Also removes the `COPY ./auth/internal ./auth/internal` lines from the api and dashboard-api Dockerfiles that #3323 added: with the implementation nested under `pkg/auth`, the existing `COPY ./auth/pkg` carries it, and the stale COPY would fail on a now-missing path. ## Motivation #3314 placed implementation packages at `packages/auth/internal`, outside `pkg/`. The api/dashboard-api image builds copy the auth module selectively (`COPY ./auth/pkg`), so post-merge image builds broke and #3323 hot-fixed them with an extra COPY. This class of breakage is only detectable post-merge (PR CI builds from a full checkout; Dockerfiles build only in `build-and-upload-images.yml`), so every current and future `pkg`-only copier must remember the extra line. Nesting internals under `pkg/auth` makes any `pkg`-only copy self-contained and deletes the failure mode structurally. It also tightens Go's internal boundary: only the `pkg/auth` facade can reach the implementation now (previously any package under `packages/auth/`, e.g. `pkg/types`/`pkg/tests`, could). ## Testing - `go build ./… && go vet ./… && go test ./…` in `packages/auth`; `golangci-lint run ./packages/auth/…` — 0 issues - `go build ./packages/api/… ./packages/dashboard-api/…` - Replicated both Docker build contexts (only COPY'd paths, no `go.work`, `CGO_ENABLED=0 GOOS=linux`): api and dashboard-api build OK — the exact scenario that broke after #3314 - Simulated an external module consumer (`GOWORK=off`, path replaces): facade import builds; direct import of `pkg/auth/internal/…` is rejected by the compiler as intended
…_type (#3427) Completes the `/v1/management` contract against its one caller, `belt`'s `workspace-api`. Every operation it invokes now has a definition here. Two did not line up. ## 1. The missing route `POST /v1/management/projects/{teamID}/members/batch` — the bulk path behind group and directory fan-outs, where the per-member routes cost one request each. The caller already sends this. Its client is hand-written precisely because there was nothing here to generate from, so the schema is taken from the bytes it puts on the wire rather than designed fresh: ```json [{"user_id": "…", "present": true}, {"user_id": "…", "present": false}] ``` Presence is **stated** rather than implied by inclusion, so one request carries both additions and removals and entries converge in any order. `maxItems: 1024` matches the chunk size the caller already uses. Stubbed `501` like its five siblings — serving it is separate work. ## 2. The mismatch that was breaking provisioning `AdminControlPlaneProjectType` enumerated `development | staging | production`, from the scaffolding in #3314 — written before any caller existed. The caller sends tier names (`base_v1`, `pro_v1`) and gates on the generated `Valid()` *before opening a connection*: ```go projectType := managementapi.AdminControlPlaneProjectType(project.ProjectType) if !projectType.Valid() { return fmt.Errorf("%q: %w", project.ProjectType, ErrInvalidProjectType) } ``` So **every project upsert failed client-side and no request ever arrived here.** Signup's default type is `base_v1`, so this was every project. The enum is now gone. Nothing on this side reads the value — there is no column for it, and limits arrive in full and explicitly through `upsertProjectLimits`. Enumerating a field we only record would make adding a tier a cross-repo change to a value we never interpret, so the caller keeps its vocabulary and this keeps a `string` with `minLength: 1`. ## Tests The batch caller is hand-written, so nothing but a test keeps the two sides in step: - The batch request decodes from the literal JSON that caller emits. - An upsert decodes with real tier names, which fails if someone reintroduces a closed set guessing at the caller's vocabulary. - `/members/batch` is not swallowed by the `/members/{userId}` parameter beside it — driven through the real router, asserting which handler ran. The first is mutation-verified: renaming the property in the spec breaks the build. ## Coordination **This needs a companion `belt` change before the spec syncs.** `Valid()` does not exist on a plain string, which is one compile error at `controlplanes/management.go:76`. Verified by regenerating belt's client against this spec: ``` internal/controlplanes/management.go:76:18: projectType.Valid undefined ``` Loud and single-line, which is the right failure mode — but it should land alongside. ## Verification Builds, vets, tests and `golangci-lint` clean in `dashboard-api`. Contract diff confirms 1:1 coverage: seven caller operations, seven definitions, no orphans either direction. No `ARCHITECTURE.md` change — it describes the `/v1/management` surface and its auth without enumerating operations, and neither is altered.
🤖 I have created a release *beep* *boop* --- ## 0.0.1 (2026-07-30) ### Features * add workspace admin API foundations ([#3314](#3314)) ([0f72030](0f72030)) * **api:** add admin team API key routes ([#2825](#2825)) ([4a1e083](4a1e083)) * **api:** add feature flag to stop accepting E2B access tokens ([#3240](#3240)) ([2cf489b](2cf489b)) * **api:** add sandbox fork endpoint ([#3202](#3202)) ([643d726](643d726)) * **api:** add sandbox IAM workload token configuration ([13ddb3d](13ddb3d)) * **api:** add sandbox workload identity permission ([#3319](#3319)) ([13ddb3d](13ddb3d)) * **api:** add user agent integration attribution to PostHog events ([#3303](#3303)) ([d83be18](d83be18)) * **api:** discover orchestrators via nomad service ([#3176](#3176)) ([32af250](32af250)) * **api:** e2b access token deprecation feature flag rename ([#3110](#3110)) ([ebc2daa](ebc2daa)) * **api:** enforce blocked-team restrictions at mutating API endpoints ([#2659](#2659)) ([db848ab](db848ab)) * **api:** filter snapshots by name ([#3184](#3184)) ([6fa1bc7](6fa1bc7)) * **api:** gate access token issuance behind feature flag ([#3101](#3101)) ([2f7811e](2f7811e)) * **api:** LD-gated ClickHouse read switcher ([#3061](#3061)) ([29e74ca](29e74ca)) * **api:** limit template build name to 128 characters ([#3109](#3109)) ([84aa186](84aa186)) * **api:** paginated GET /v2/templates (EN-603) ([#3059](#3059)) ([91e02e4](91e02e4)) * **api:** per-region volume type defaults from node-derived region ([#3435](#3435)) ([baf5559](baf5559)) * **api:** pin resume retries to the node a previous resume timed out on ([#3066](#3066)) ([a4fd0f2](a4fd0f2)) * **api:** SOCKS5 egress proxy on sandbox network config (BYOP) ([#2642](#2642)) ([1fc3820](1fc3820)) * **api:** soft-delete build layers in DB on user delete ([#3121](#3121)) ([ee88776](ee88776)) * **auth:** support admin token team auth ([#2934](#2934)) ([5496666](5496666)) * dynamic sandbox log routing and ClickHouse-backed log reads ([#3236](#3236)) ([1b19a3b](1b19a3b)) * **evictor:** make max concurrent evictions a feature flag ([#2727](#2727)) ([0b33013](0b33013)) * **metrics:** distinguish joined from regular requests (ENG-4072) ([#2699](#2699)) ([390e296](390e296)) * **observability:** add kill_reason to sandbox.lifecycle.killed ([#2833](#2833)) ([e45418f](e45418f)) * **observability:** include kill_reason in kill-path structured logs ([#2846](#2846)) ([33c49f7](33c49f7)) * **orchestrator:** add dummy orchestrator binary for local API dev ([#2744](#2744)) ([ab56e25](ab56e25)) * **orchestrator:** report hugepage metrics to API ([#3182](#3182)) ([7735bae](7735bae)) * **orchestrator:** track and report last status change timestamp ([#2980](#2980)) ([f79be77](f79be77)) * **otel:** instrument auth service HTTP client with otelhttp ([#2722](#2722)) ([69b085d](69b085d)) * per-team events TTL limit (tier + addons) ([#3181](#3181)) ([f76b2cb](f76b2cb)) * **storage:** stamp provenance custom metadata on uploaded objects (incl. headers) ([#3033](#3033)) ([ba8604e](ba8604e)) ### Bug Fixes * added api and orch ([#3454](#3454)) ([fda5e45](fda5e45)) * **api:** check template alias tags in exists endpoint ([#2916](#2916)) ([9574cdf](9574cdf)) * **api:** copy auth/internal into api and dashboard-api image builds ([#3323](#3323)) ([bda1fee](bda1fee)) * **api:** discover the local orchestrator as a template builder ([#3386](#3386)) ([9ea005a](9ea005a)) * **api:** expose pagination headers via CORS ([#3388](#3388)) ([e832b1e](e832b1e)) * **api:** handle corrupted data in sandbox stop time ([#3203](#3203)) ([a98a178](a98a178)) * **api:** include exhaustion reason in "Node exhausted" placement warning ([#3279](#3279)) ([eb3797b](eb3797b)) * **api:** invalidate auth cache on API key deletion ([#3324](#3324)) ([8b02910](8b02910)) * **api:** keep API alive until in-flight requests finish ([#2708](#2708)) ([06378c7](06378c7)) * **api:** let the analytics collector address carry a port ([#3394](#3394)) ([6d41cb5](6d41cb5)) * **api:** parse the pause body regardless of Content-Length ([#3056](#3056)) ([d66aab8](d66aab8)) * **api:** prevent uint64 underflow in node allocated metrics ([#3216](#3216)) ([fed38e1](fed38e1)) * **api:** push api and db-migrator images to both latest and commit S… ([#2951](#2951)) ([6f010fc](6f010fc)) * **api:** reject non-positive timeout on sandbox create, resume, and fork ([#3419](#3419)) ([b672bd1](b672bd1)) * **api:** report invalid tag errors as bad requests ([#2799](#2799)) ([10085a1](10085a1)) * **api:** stop evicting the local node during sync ([#2881](#2881)) ([5455905](5455905)) * **api:** use correct error variable in processCustomErrors ([#3135](#3135)) ([a131a00](a131a00)) * **auth:** rename X-Team-Id header to X-Team-ID ([#2723](#2723)) ([f92ecc0](f92ecc0)) * correct 3 CVES ([#3218](#3218)) ([076823b](076823b)) * **orchestrator:** reject standby while draining ([#3325](#3325)) ([475a7ee](475a7ee)) * Support snapshots for non-default clusters ([#2947](#2947)) ([28eeb72](28eeb72)) ### Performance Improvements * **api:** wake reservation waiters via pub/sub instead of 20ms polling [ENG-4070] ([#2729](#2729)) ([2944d06](2944d06)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: e2b-release-please[bot] <298072688+e2b-release-please[bot]@users.noreply.github.com>
🤖 I have created a release *beep* *boop* --- ## 0.0.1 (2026-07-30) ### Features * add workspace admin API foundations ([#3314](#3314)) ([0f72030](0f72030)) * **api:** LD-gated ClickHouse read switcher ([#3061](#3061)) ([29e74ca](29e74ca)) * **api:** soft-delete build layers in DB on user delete ([#3121](#3121)) ([ee88776](ee88776)) * **auth:** support admin token team auth ([#2934](#2934)) ([5496666](5496666)) * **auth:** verifiers on one axis, and a reusable authenticator constructor ([#3423](#3423)) ([923b99b](923b99b)) * **dashboard-api:** add internal admin route for deleting a user ([#2986](#2986)) ([ecc1291](ecc1291)) * **dashboard-api:** add internal team creation ([#2824](#2824)) ([375051b](375051b)) * **dashboard-api:** add OIDC admin user bootstrap endpoint ([#2841](#2841)) ([6a7a59e](6a7a59e)) * **dashboard-api:** add Ory user profile provider and auth middleware fix ([#2840](#2840)) ([30d40d2](30d40d2)) * **dashboard-api:** add template tags handlers ([#2885](#2885)) ([bf52a4b](bf52a4b)) * **dashboard-api:** batch member sync route, and unenumerate project_type ([#3427](#3427)) ([cc16acf](cc16acf)) * **dashboard-api:** expose auth profile admin routes ([#2743](#2743)) ([b673a10](b673a10)) * **dashboard-api:** flag sandboxes past data retention ([#3102](#3102)) ([9b162bf](9b162bf)) * **dashboard-api:** implement upsertProjectLimits ([#3438](#3438)) ([ec1ed29](ec1ed29)) * **dashboard-api:** include build resources in /builds response ([#3009](#3009)) ([bf49c32](bf49c32)) * **dashboard-api:** map Ory SSO organizations to E2B teams ([#3094](#3094)) ([dbd098f](dbd098f)) * **dashboard-api:** populate Ory identity external_id on admin bootstrap ([#3062](#3062)) ([6c51232](6c51232)) * **dashboard-api:** project upsert, member sync and user purge ([#3442](#3442)) ([f997c39](f997c39)) * **dashboard-api:** templates list pagination ([#2904](#2904)) ([6882463](6882463)) * **db:** add project_limits, an override the limits owner can write ([#3429](#3429)) ([021c2a4](021c2a4)) * improve templates list sorting ([#2983](#2983)) ([51ad7ff](51ad7ff)) * **otel:** instrument auth service HTTP client with otelhttp ([#2722](#2722)) ([69b085d](69b085d)) * per-team events TTL limit (tier + addons) ([#3181](#3181)) ([f76b2cb](f76b2cb)) ### Bug Fixes * added api and orch ([#3454](#3454)) ([fda5e45](fda5e45)) * **api:** copy auth/internal into api and dashboard-api image builds ([#3323](#3323)) ([bda1fee](bda1fee)) * **api:** invalidate auth cache on API key deletion ([#3324](#3324)) ([8b02910](8b02910)) * correct 3 CVES ([#3218](#3218)) ([076823b](076823b)) * **dashboard-api:** avoid repeated Ory bootstrap provisioning ([#2940](#2940)) ([da5ce59](da5ce59)) * **dashboard-api:** drop removed read-replica accessor in provisioning tests ([#3340](#3340)) ([6addc91](6addc91)) * **dashboard-api:** pass signup metadata to billing provisioning ([#2978](#2978)) ([d0ea5b4](d0ea5b4)) * **dashboard-api:** set Ory external_id only after the bootstrap commit ([#3133](#3133)) ([00ad04b](00ad04b)) * push client-proxy, dashboard-api, and docker-reverse-proxy image… ([#2953](#2953)) ([1d930ee](1d930ee)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: e2b-release-please[bot] <298072688+e2b-release-please[bot]@users.noreply.github.com> Co-authored-by: Charlie Wyse <charlie.wyse@e2b.dev>
🤖 I have created a release *beep* *boop* --- ## [0.1.0](dashboard-api-v0.0.1...dashboard-api-v0.1.0) (2026-07-31) ### Features * add workspace admin API foundations ([#3314](#3314)) ([0f72030](0f72030)) * **api:** LD-gated ClickHouse read switcher ([#3061](#3061)) ([29e74ca](29e74ca)) * **api:** soft-delete build layers in DB on user delete ([#3121](#3121)) ([ee88776](ee88776)) * **auth:** support admin token team auth ([#2934](#2934)) ([5496666](5496666)) * **auth:** verifiers on one axis, and a reusable authenticator constructor ([#3423](#3423)) ([923b99b](923b99b)) * **dashboard-api:** add internal admin route for deleting a user ([#2986](#2986)) ([ecc1291](ecc1291)) * **dashboard-api:** add internal team creation ([#2824](#2824)) ([375051b](375051b)) * **dashboard-api:** add OIDC admin user bootstrap endpoint ([#2841](#2841)) ([6a7a59e](6a7a59e)) * **dashboard-api:** add Ory user profile provider and auth middleware fix ([#2840](#2840)) ([30d40d2](30d40d2)) * **dashboard-api:** add template tags handlers ([#2885](#2885)) ([bf52a4b](bf52a4b)) * **dashboard-api:** batch member sync route, and unenumerate project_type ([#3427](#3427)) ([cc16acf](cc16acf)) * **dashboard-api:** expose auth profile admin routes ([#2743](#2743)) ([b673a10](b673a10)) * **dashboard-api:** flag sandboxes past data retention ([#3102](#3102)) ([9b162bf](9b162bf)) * **dashboard-api:** implement upsertProjectLimits ([#3438](#3438)) ([ec1ed29](ec1ed29)) * **dashboard-api:** include build resources in /builds response ([#3009](#3009)) ([bf49c32](bf49c32)) * **dashboard-api:** map Ory SSO organizations to E2B teams ([#3094](#3094)) ([dbd098f](dbd098f)) * **dashboard-api:** populate Ory identity external_id on admin bootstrap ([#3062](#3062)) ([6c51232](6c51232)) * **dashboard-api:** project upsert, member sync and user purge ([#3442](#3442)) ([f997c39](f997c39)) * **dashboard-api:** templates list pagination ([#2904](#2904)) ([6882463](6882463)) * **db:** add project_limits, an override the limits owner can write ([#3429](#3429)) ([021c2a4](021c2a4)) * improve templates list sorting ([#2983](#2983)) ([51ad7ff](51ad7ff)) * **otel:** instrument auth service HTTP client with otelhttp ([#2722](#2722)) ([69b085d](69b085d)) * per-team events TTL limit (tier + addons) ([#3181](#3181)) ([f76b2cb](f76b2cb)) ### Bug Fixes * added api and orch ([#3454](#3454)) ([fda5e45](fda5e45)) * **api:** copy auth/internal into api and dashboard-api image builds ([#3323](#3323)) ([bda1fee](bda1fee)) * **api:** invalidate auth cache on API key deletion ([#3324](#3324)) ([8b02910](8b02910)) * correct 3 CVES ([#3218](#3218)) ([076823b](076823b)) * creating whitespace to test publish ([#3476](#3476)) ([5158cc9](5158cc9)) * **dashboard-api:** avoid repeated Ory bootstrap provisioning ([#2940](#2940)) ([da5ce59](da5ce59)) * **dashboard-api:** drop removed read-replica accessor in provisioning tests ([#3340](#3340)) ([6addc91](6addc91)) * **dashboard-api:** pass signup metadata to billing provisioning ([#2978](#2978)) ([d0ea5b4](d0ea5b4)) * **dashboard-api:** set Ory external_id only after the bootstrap commit ([#3133](#3133)) ([00ad04b](00ad04b)) * push client-proxy, dashboard-api, and docker-reverse-proxy image… ([#2953](#2953)) ([1d930ee](1d930ee)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: e2b-release-please[bot] <298072688+e2b-release-please[bot]@users.noreply.github.com>
…_type (#3427) Completes the `/v1/management` contract against its one caller, `belt`'s `workspace-api`. Every operation it invokes now has a definition here. Two did not line up. ## 1. The missing route `POST /v1/management/projects/{teamID}/members/batch` — the bulk path behind group and directory fan-outs, where the per-member routes cost one request each. The caller already sends this. Its client is hand-written precisely because there was nothing here to generate from, so the schema is taken from the bytes it puts on the wire rather than designed fresh: ```json [{"user_id": "…", "present": true}, {"user_id": "…", "present": false}] ``` Presence is **stated** rather than implied by inclusion, so one request carries both additions and removals and entries converge in any order. `maxItems: 1024` matches the chunk size the caller already uses. Stubbed `501` like its five siblings — serving it is separate work. ## 2. The mismatch that was breaking provisioning `AdminControlPlaneProjectType` enumerated `development | staging | production`, from the scaffolding in #3314 — written before any caller existed. The caller sends tier names (`base_v1`, `pro_v1`) and gates on the generated `Valid()` *before opening a connection*: ```go projectType := managementapi.AdminControlPlaneProjectType(project.ProjectType) if !projectType.Valid() { return fmt.Errorf("%q: %w", project.ProjectType, ErrInvalidProjectType) } ``` So **every project upsert failed client-side and no request ever arrived here.** Signup's default type is `base_v1`, so this was every project. The enum is now gone. Nothing on this side reads the value — there is no column for it, and limits arrive in full and explicitly through `upsertProjectLimits`. Enumerating a field we only record would make adding a tier a cross-repo change to a value we never interpret, so the caller keeps its vocabulary and this keeps a `string` with `minLength: 1`. ## Tests The batch caller is hand-written, so nothing but a test keeps the two sides in step: - The batch request decodes from the literal JSON that caller emits. - An upsert decodes with real tier names, which fails if someone reintroduces a closed set guessing at the caller's vocabulary. - `/members/batch` is not swallowed by the `/members/{userId}` parameter beside it — driven through the real router, asserting which handler ran. The first is mutation-verified: renaming the property in the spec breaks the build. ## Coordination **This needs a companion `belt` change before the spec syncs.** `Valid()` does not exist on a plain string, which is one compile error at `controlplanes/management.go:76`. Verified by regenerating belt's client against this spec: ``` internal/controlplanes/management.go:76:18: projectType.Valid undefined ``` Loud and single-line, which is the right failure mode — but it should land alongside. ## Verification Builds, vets, tests and `golangci-lint` clean in `dashboard-api`. Contract diff confirms 1:1 coverage: seven caller operations, seven definitions, no orphans either direction. No `ARCHITECTURE.md` change — it describes the `/v1/management` surface and its auth without enumerating operations, and neither is altered.
🤖 I have created a release *beep* *boop* --- ## 0.0.1 (2026-07-30) ### Features * add workspace admin API foundations ([#3314](#3314)) ([0f72030](0f72030)) * **api:** add admin team API key routes ([#2825](#2825)) ([4a1e083](4a1e083)) * **api:** add feature flag to stop accepting E2B access tokens ([#3240](#3240)) ([2cf489b](2cf489b)) * **api:** add sandbox fork endpoint ([#3202](#3202)) ([643d726](643d726)) * **api:** add sandbox IAM workload token configuration ([13ddb3d](13ddb3d)) * **api:** add sandbox workload identity permission ([#3319](#3319)) ([13ddb3d](13ddb3d)) * **api:** add user agent integration attribution to PostHog events ([#3303](#3303)) ([d83be18](d83be18)) * **api:** discover orchestrators via nomad service ([#3176](#3176)) ([32af250](32af250)) * **api:** e2b access token deprecation feature flag rename ([#3110](#3110)) ([ebc2daa](ebc2daa)) * **api:** enforce blocked-team restrictions at mutating API endpoints ([#2659](#2659)) ([db848ab](db848ab)) * **api:** filter snapshots by name ([#3184](#3184)) ([6fa1bc7](6fa1bc7)) * **api:** gate access token issuance behind feature flag ([#3101](#3101)) ([2f7811e](2f7811e)) * **api:** LD-gated ClickHouse read switcher ([#3061](#3061)) ([29e74ca](29e74ca)) * **api:** limit template build name to 128 characters ([#3109](#3109)) ([84aa186](84aa186)) * **api:** paginated GET /v2/templates (EN-603) ([#3059](#3059)) ([91e02e4](91e02e4)) * **api:** per-region volume type defaults from node-derived region ([#3435](#3435)) ([1bded44](1bded44)) * **api:** pin resume retries to the node a previous resume timed out on ([#3066](#3066)) ([a4fd0f2](a4fd0f2)) * **api:** SOCKS5 egress proxy on sandbox network config (BYOP) ([#2642](#2642)) ([1fc3820](1fc3820)) * **api:** soft-delete build layers in DB on user delete ([#3121](#3121)) ([ee88776](ee88776)) * **auth:** support admin token team auth ([#2934](#2934)) ([5496666](5496666)) * dynamic sandbox log routing and ClickHouse-backed log reads ([#3236](#3236)) ([1b19a3b](1b19a3b)) * **evictor:** make max concurrent evictions a feature flag ([#2727](#2727)) ([0b33013](0b33013)) * **metrics:** distinguish joined from regular requests (ENG-4072) ([#2699](#2699)) ([390e296](390e296)) * **observability:** add kill_reason to sandbox.lifecycle.killed ([#2833](#2833)) ([e45418f](e45418f)) * **observability:** include kill_reason in kill-path structured logs ([#2846](#2846)) ([33c49f7](33c49f7)) * **orchestrator:** add dummy orchestrator binary for local API dev ([#2744](#2744)) ([ab56e25](ab56e25)) * **orchestrator:** report hugepage metrics to API ([#3182](#3182)) ([7735bae](7735bae)) * **orchestrator:** track and report last status change timestamp ([#2980](#2980)) ([f79be77](f79be77)) * **otel:** instrument auth service HTTP client with otelhttp ([#2722](#2722)) ([69b085d](69b085d)) * per-team events TTL limit (tier + addons) ([#3181](#3181)) ([f76b2cb](f76b2cb)) * **storage:** stamp provenance custom metadata on uploaded objects (incl. headers) ([#3033](#3033)) ([ba8604e](ba8604e)) ### Bug Fixes * added api and orch ([#3454](#3454)) ([d56e0a8](d56e0a8)) * **api:** check template alias tags in exists endpoint ([#2916](#2916)) ([9574cdf](9574cdf)) * **api:** copy auth/internal into api and dashboard-api image builds ([#3323](#3323)) ([bda1fee](bda1fee)) * **api:** discover the local orchestrator as a template builder ([#3386](#3386)) ([9ea005a](9ea005a)) * **api:** expose pagination headers via CORS ([#3388](#3388)) ([e832b1e](e832b1e)) * **api:** handle corrupted data in sandbox stop time ([#3203](#3203)) ([a98a178](a98a178)) * **api:** include exhaustion reason in "Node exhausted" placement warning ([#3279](#3279)) ([eb3797b](eb3797b)) * **api:** invalidate auth cache on API key deletion ([#3324](#3324)) ([8b02910](8b02910)) * **api:** keep API alive until in-flight requests finish ([#2708](#2708)) ([06378c7](06378c7)) * **api:** let the analytics collector address carry a port ([#3394](#3394)) ([6d41cb5](6d41cb5)) * **api:** parse the pause body regardless of Content-Length ([#3056](#3056)) ([d66aab8](d66aab8)) * **api:** prevent uint64 underflow in node allocated metrics ([#3216](#3216)) ([fed38e1](fed38e1)) * **api:** push api and db-migrator images to both latest and commit S… ([#2951](#2951)) ([6f010fc](6f010fc)) * **api:** reject non-positive timeout on sandbox create, resume, and fork ([#3419](#3419)) ([5a4b631](5a4b631)) * **api:** report invalid tag errors as bad requests ([#2799](#2799)) ([10085a1](10085a1)) * **api:** stop evicting the local node during sync ([#2881](#2881)) ([5455905](5455905)) * **api:** use correct error variable in processCustomErrors ([#3135](#3135)) ([a131a00](a131a00)) * **auth:** rename X-Team-Id header to X-Team-ID ([#2723](#2723)) ([f92ecc0](f92ecc0)) * correct 3 CVES ([#3218](#3218)) ([076823b](076823b)) * **orchestrator:** reject standby while draining ([#3325](#3325)) ([475a7ee](475a7ee)) * Support snapshots for non-default clusters ([#2947](#2947)) ([28eeb72](28eeb72)) ### Performance Improvements * **api:** wake reservation waiters via pub/sub instead of 20ms polling [ENG-4070] ([#2729](#2729)) ([2944d06](2944d06)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: e2b-release-please[bot] <298072688+e2b-release-please[bot]@users.noreply.github.com>
🤖 I have created a release *beep* *boop* --- ## 0.0.1 (2026-07-30) ### Features * add workspace admin API foundations ([#3314](#3314)) ([0f72030](0f72030)) * **api:** LD-gated ClickHouse read switcher ([#3061](#3061)) ([29e74ca](29e74ca)) * **api:** soft-delete build layers in DB on user delete ([#3121](#3121)) ([ee88776](ee88776)) * **auth:** support admin token team auth ([#2934](#2934)) ([5496666](5496666)) * **auth:** verifiers on one axis, and a reusable authenticator constructor ([#3423](#3423)) ([f68e713](f68e713)) * **dashboard-api:** add internal admin route for deleting a user ([#2986](#2986)) ([ecc1291](ecc1291)) * **dashboard-api:** add internal team creation ([#2824](#2824)) ([375051b](375051b)) * **dashboard-api:** add OIDC admin user bootstrap endpoint ([#2841](#2841)) ([6a7a59e](6a7a59e)) * **dashboard-api:** add Ory user profile provider and auth middleware fix ([#2840](#2840)) ([30d40d2](30d40d2)) * **dashboard-api:** add template tags handlers ([#2885](#2885)) ([bf52a4b](bf52a4b)) * **dashboard-api:** batch member sync route, and unenumerate project_type ([#3427](#3427)) ([6d8dc38](6d8dc38)) * **dashboard-api:** expose auth profile admin routes ([#2743](#2743)) ([b673a10](b673a10)) * **dashboard-api:** flag sandboxes past data retention ([#3102](#3102)) ([9b162bf](9b162bf)) * **dashboard-api:** implement upsertProjectLimits ([#3438](#3438)) ([f4ee390](f4ee390)) * **dashboard-api:** include build resources in /builds response ([#3009](#3009)) ([bf49c32](bf49c32)) * **dashboard-api:** map Ory SSO organizations to E2B teams ([#3094](#3094)) ([dbd098f](dbd098f)) * **dashboard-api:** populate Ory identity external_id on admin bootstrap ([#3062](#3062)) ([6c51232](6c51232)) * **dashboard-api:** project upsert, member sync and user purge ([#3442](#3442)) ([8c90702](8c90702)) * **dashboard-api:** templates list pagination ([#2904](#2904)) ([6882463](6882463)) * **db:** add project_limits, an override the limits owner can write ([#3429](#3429)) ([5ab6259](5ab6259)) * improve templates list sorting ([#2983](#2983)) ([51ad7ff](51ad7ff)) * **otel:** instrument auth service HTTP client with otelhttp ([#2722](#2722)) ([69b085d](69b085d)) * per-team events TTL limit (tier + addons) ([#3181](#3181)) ([f76b2cb](f76b2cb)) ### Bug Fixes * added api and orch ([#3454](#3454)) ([d56e0a8](d56e0a8)) * **api:** copy auth/internal into api and dashboard-api image builds ([#3323](#3323)) ([bda1fee](bda1fee)) * **api:** invalidate auth cache on API key deletion ([#3324](#3324)) ([8b02910](8b02910)) * correct 3 CVES ([#3218](#3218)) ([076823b](076823b)) * **dashboard-api:** avoid repeated Ory bootstrap provisioning ([#2940](#2940)) ([da5ce59](da5ce59)) * **dashboard-api:** drop removed read-replica accessor in provisioning tests ([#3340](#3340)) ([6addc91](6addc91)) * **dashboard-api:** pass signup metadata to billing provisioning ([#2978](#2978)) ([d0ea5b4](d0ea5b4)) * **dashboard-api:** set Ory external_id only after the bootstrap commit ([#3133](#3133)) ([00ad04b](00ad04b)) * push client-proxy, dashboard-api, and docker-reverse-proxy image… ([#2953](#2953)) ([1d930ee](1d930ee)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: e2b-release-please[bot] <298072688+e2b-release-please[bot]@users.noreply.github.com> Co-authored-by: Charlie Wyse <charlie.wyse@e2b.dev>
🤖 I have created a release *beep* *boop* --- ## [0.1.0](dashboard-api-v0.0.1...dashboard-api-v0.1.0) (2026-07-31) ### Features * add workspace admin API foundations ([#3314](#3314)) ([0f72030](0f72030)) * **api:** LD-gated ClickHouse read switcher ([#3061](#3061)) ([29e74ca](29e74ca)) * **api:** soft-delete build layers in DB on user delete ([#3121](#3121)) ([ee88776](ee88776)) * **auth:** support admin token team auth ([#2934](#2934)) ([5496666](5496666)) * **auth:** verifiers on one axis, and a reusable authenticator constructor ([#3423](#3423)) ([f68e713](f68e713)) * **dashboard-api:** add internal admin route for deleting a user ([#2986](#2986)) ([ecc1291](ecc1291)) * **dashboard-api:** add internal team creation ([#2824](#2824)) ([375051b](375051b)) * **dashboard-api:** add OIDC admin user bootstrap endpoint ([#2841](#2841)) ([6a7a59e](6a7a59e)) * **dashboard-api:** add Ory user profile provider and auth middleware fix ([#2840](#2840)) ([30d40d2](30d40d2)) * **dashboard-api:** add template tags handlers ([#2885](#2885)) ([bf52a4b](bf52a4b)) * **dashboard-api:** batch member sync route, and unenumerate project_type ([#3427](#3427)) ([6d8dc38](6d8dc38)) * **dashboard-api:** expose auth profile admin routes ([#2743](#2743)) ([b673a10](b673a10)) * **dashboard-api:** flag sandboxes past data retention ([#3102](#3102)) ([9b162bf](9b162bf)) * **dashboard-api:** implement upsertProjectLimits ([#3438](#3438)) ([f4ee390](f4ee390)) * **dashboard-api:** include build resources in /builds response ([#3009](#3009)) ([bf49c32](bf49c32)) * **dashboard-api:** map Ory SSO organizations to E2B teams ([#3094](#3094)) ([dbd098f](dbd098f)) * **dashboard-api:** populate Ory identity external_id on admin bootstrap ([#3062](#3062)) ([6c51232](6c51232)) * **dashboard-api:** project upsert, member sync and user purge ([#3442](#3442)) ([8c90702](8c90702)) * **dashboard-api:** templates list pagination ([#2904](#2904)) ([6882463](6882463)) * **db:** add project_limits, an override the limits owner can write ([#3429](#3429)) ([5ab6259](5ab6259)) * improve templates list sorting ([#2983](#2983)) ([51ad7ff](51ad7ff)) * **otel:** instrument auth service HTTP client with otelhttp ([#2722](#2722)) ([69b085d](69b085d)) * per-team events TTL limit (tier + addons) ([#3181](#3181)) ([f76b2cb](f76b2cb)) ### Bug Fixes * added api and orch ([#3454](#3454)) ([d56e0a8](d56e0a8)) * **api:** copy auth/internal into api and dashboard-api image builds ([#3323](#3323)) ([bda1fee](bda1fee)) * **api:** invalidate auth cache on API key deletion ([#3324](#3324)) ([8b02910](8b02910)) * correct 3 CVES ([#3218](#3218)) ([076823b](076823b)) * creating whitespace to test publish ([#3476](#3476)) ([6b4177f](6b4177f)) * **dashboard-api:** avoid repeated Ory bootstrap provisioning ([#2940](#2940)) ([da5ce59](da5ce59)) * **dashboard-api:** drop removed read-replica accessor in provisioning tests ([#3340](#3340)) ([6addc91](6addc91)) * **dashboard-api:** pass signup metadata to billing provisioning ([#2978](#2978)) ([d0ea5b4](d0ea5b4)) * **dashboard-api:** set Ory external_id only after the bootstrap commit ([#3133](#3133)) ([00ad04b](00ad04b)) * push client-proxy, dashboard-api, and docker-reverse-proxy image… ([#2953](#2953)) ([1d930ee](1d930ee)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: e2b-release-please[bot] <298072688+e2b-release-please[bot]@users.noreply.github.com>

Summary
This PR lays the groundwork for the Workspaces & Projects v1 admin surface and restructures the shared auth package behind a stable public facade.
/admin/v1OpenAPI surface and generated handlers for workspace administration.AdminJWTAuthfor short-lived service JWTs configured throughADMIN_AUTH_PROVIDER_CONFIG.packages/auth/pkg/authwhile moving implementation into focused Gointernalpackages.ProviderConfigshape betweenAUTH_PROVIDER_CONFIGandADMIN_AUTH_PROVIDER_CONFIG.Motivation
The dashboard-api needs to verify service-to-service JWTs for workspace admin endpoints, while the API already verifies auth-provider tokens. Both flows need shared configuration and JWKS validation without exposing implementation packages or forcing consumers to import token internals.
The public
authpackage is now a facade, so callers continue using APIs such asauth.NewAuthService,auth.NewAdminVerifier, and the existing middleware constructors while implementation details remain protected by Go'sinternalboundary.Changes
Auth package
packages/auth/pkg/authas the stable consumer-facing facade.internal/authcontext,internal/middleware,internal/service,internal/team,internal/token,internal/token/jwks, andinternal/token/oidc.auth.JWT and JWKS verification
auth.NewAdminVerifierfor multi-issuer admin service JWT verification./.well-known/jwks.jsonendpoint.algand derives accepted JWT signing methods from current JWKS storage.Configuration
AUTH_PROVIDER_CONFIGconfigures user auth-provider JWTs.ADMIN_AUTH_PROVIDER_CONFIGconfigures admin service JWTs using the sameProviderConfigJSON shape.AdminJWTAuthrequests when no admin provider is configured.Dashboard API
/admin/v1OpenAPI operations andAdminJWTAuthsecurity scheme.Usage example
{ "jwt": [ { "issuer": { "url": "https://workspace-api.example.com", "audiences": ["e2b-dashboard-api"] }, "cacheDuration": "5m" } ] }Set this JSON as
ADMIN_AUTH_PROVIDER_CONFIGfor admin service JWTs or asAUTH_PROVIDER_CONFIGfor auth-provider JWTs.Testing
go test ./packages/auth/...go test ./packages/api/internal/cfg/...go test ./packages/dashboard-api/...golangci-lint run ./packages/auth/... ./packages/api/... ./packages/dashboard-api/...go build ./packages/api/... ./packages/dashboard-api/...