Skip to content

chore: use neutral token prefixes with legacy support#3719

Merged
joeyorlando merged 2 commits intomainfrom
codex/token-prefixes
Apr 11, 2026
Merged

chore: use neutral token prefixes with legacy support#3719
joeyorlando merged 2 commits intomainfrom
codex/token-prefixes

Conversation

@joeyorlando
Copy link
Copy Markdown
Contributor

@joeyorlando joeyorlando commented Apr 11, 2026

What changed

  • switched newly generated platform-managed API, MCP gateway, A2A, and virtual-key token prefixes from archestra_ to the neutral arch_ prefix
  • centralized token-prefix matching in shared helpers and updated the gateway and LLM proxy auth paths to accept both current and legacy prefixes
  • updated token-related UI/tests and the main authentication docs to reflect the new default prefix and explicit backwards compatibility

Why

  • white-label deployments should not expose an archestra_ token prefix in newly issued credentials
  • making the prefix follow mutable organization app names would create long-term compatibility and rename-migration problems
  • this keeps the rollout simple: new tokens are neutral, old archestra_ tokens continue to work unchanged

Impact

  • newly created user tokens, team tokens, virtual API keys, and Better Auth API keys now default to arch_
  • existing archestra_ tokens remain valid for MCP Gateway, A2A, and LLM proxy authentication
  • docs now describe arch_ as the default and call out legacy support

Root cause

The platform treated archestra_ as both the branding choice and the token-type discriminator. That hardcoded product name leaked into issued credentials and into auth routing logic.

@joeyorlando joeyorlando changed the title [codex] use neutral token prefixes with legacy support chore: use neutral token prefixes with legacy support Apr 11, 2026
@joeyorlando joeyorlando marked this pull request as ready for review April 11, 2026 03:55
@claude
Copy link
Copy Markdown
Contributor

claude bot commented Apr 11, 2026

Claude finished @joeyorlando's task in 5m 12s —— View job


Claude PR Review

  • Read CLAUDE.md for repo-specific guidelines
  • Examine PR diff and changed files
  • Review shared token prefix helpers
  • Review backend auth/routing changes
  • Review tests coverage
  • Review docs updates
  • Post review feedback

Overall: Solid, minimal, well-tested refactor. The approach — keep ARCHESTRA_TOKEN_PREFIX as the constant name but change its value to arch_, and add LEGACY_ARCHESTRA_TOKEN_PREFIXES for backward compat — is the right call. Token storage/lookup semantics are preserved for both new and legacy tokens because the stored tokenStart (first 14 chars) is captured at creation time and used verbatim at lookup.

Correctness

  1. Lookup path is safe for both prefixesTOKEN_START_LENGTH = 14 is unchanged in team-token.ts:38, user-token.ts, and virtual-api-key.ts. For new tokens, tokenStart is arch_ (5) + 9 random hex chars; for legacy tokens it's archestra_ (10) + 4 random hex chars. Both are stored at creation and exact-matched on lookup, so validateToken() remains correct across old/new tokens. Actually a minor improvement: 9 random chars gives ~68B distinct buckets vs. ~65K buckets previously, reducing candidate secret lookups per validate. Nice side-effect.

  2. getArchestraTokenPrefix ordering is safeALL_ARCHESTRA_TOKEN_PREFIXES = ['arch_', 'archestra_'] iterates in order, but 'archestra_xxx'.startsWith('arch_') is false (the 5th char is e, not _), so there's no classification ambiguity. Still, consider adding a test that asserts getArchestraTokenPrefix('archestra_xxx') === 'archestra_' explicitly to lock this invariant in — the current consts.test.ts:30-37 only checks that hasArchestraTokenPrefix returns true, not which prefix was matched. Minor.

  3. Legacy token cache behavior — In mcp-gateway.utils.ts:1207, resolveArchestraToken caches by rawTokenHash (SHA-256 of full token), so an incoming legacy archestra_... token and a new arch_... token will never collide. Good.

Minor nits

  1. Stale comment in better-auth.ts — The comment around platform/backend/src/auth/better-auth.ts:150 still references archestra_8594... but new keys will now look like arch_0123456789 (14 chars total). Worth refreshing the example.

  2. Duplicate isArchestraPrefixedToken — The function exists in both platform/backend/src/models/team-token.ts:59 (exported and used) and platform/backend/src/types/team-token.ts:87 (dead; no imports anywhere). This predates the PR, but since the PR is already touching both files it'd be a good opportunity to delete the dead one in types/team-token.ts. Fix this →

  3. Naming driftARCHESTRA_TOKEN_PREFIX now holds the value arch_, which is slightly confusing given the PR's stated goal ("should not expose an archestra_ token prefix"). The constant name implies Archestra branding while the value is the neutral prefix. Not worth renaming now given the blast radius, but could be renamed in a follow-up to PLATFORM_TOKEN_PREFIX / CURRENT_PLATFORM_TOKEN_PREFIX. A short comment above the constant explaining the rationale (it's the current value; kept under this name for churn reasons) would help future readers.

  4. Token length change isn't documented — New tokens are now 37 chars (arch_ + 32 hex) vs. 42 chars previously (archestra_ + 32 hex). Nothing I could find validates this in DB/schema, so it shouldn't break storage, but if any downstream consumer hard-coded the 42-char assumption (e.g. display truncation) it'd be worth double-checking. Consider a fast grep for hardcoded 42 or .length === 42 before merging.

Tests

  • platform/shared/consts.test.ts is new and covers current + legacy matching, empty/invalid inputs ✓
  • mcp-gateway.utils.test.ts tests both ARCHESTRA_TOKEN_PREFIX and LEGACY_ARCHESTRA_TOKEN_PREFIXES[0] prefixes for skip-OAuth semantics ✓
  • llm-proxy-auth.test.ts:280-320 explicitly tests that both prefixes are excluded from JWKS auth attempts ✓
  • team-token.test.ts, user-token.test.ts, virtual-api-key.test.ts, api-key.test.ts all cover legacy-prefix rejection paths ✓

One small gap: I didn't find a roundtrip test that creates a token with the legacy archestra_ prefix (via direct DB seed + secret) and then validates it through TeamTokenModel.validateToken / MCPGatewayToken validation. The existing tests validate that invalid legacy tokens return null, but not that a pre-existing valid legacy token still authenticates. Given the whole value prop of the PR is "old tokens keep working", I'd recommend adding at least one test that:

  1. Inserts a row with tokenStart = 'archestra_abcd' and a matching secret
  2. Calls TeamTokenModel.validateToken('archestra_abcd' + rest)
  3. Asserts it resolves

This would be cheap insurance against future refactors accidentally breaking legacy compatibility. Add this test →

Security

No security regressions spotted. Entropy is unchanged (still 16 random bytes / 32 hex chars). The getArchestraTokenPrefix helper uses strict prefix matching and the legacy prefix list is a hardcoded const, not user-controlled.

Docs

Doc updates are accurate. Minor consistency check: docs/pages/platform-quickstart.md replaces archestra_119220a7bfc485d66b678d3e9fb2db36 with arch_119220a7bfc485d66b678d3e9fb2db36 — a realistic new token would be arch_ + 32 hex chars (= 37 chars total, which is what the quickstart example now shows). ✓


Summary

Approve with minor nits. The change is low-risk, the test coverage for the new helpers is good, and the backward compat story is clearly documented. The main things I'd address before merging are:

  1. Refresh the stale archestra_8594... comment in better-auth.ts:150
  2. Add a happy-path legacy-token validation test (as described above)
  3. Delete the dead isArchestraPrefixedToken duplicate in types/team-token.ts (opportunistic cleanup)

None are blockers.
• Branch: codex/token-prefixes

@joeyorlando joeyorlando merged commit db5929c into main Apr 11, 2026
46 checks passed
@joeyorlando joeyorlando deleted the codex/token-prefixes branch April 11, 2026 04:35
joeyorlando added a commit that referenced this pull request Apr 11, 2026
🤖 I have created a release *beep* *boop*
---


##
[1.2.10](platform-v1.2.9...platform-v1.2.10)
(2026-04-11)


### Features

* make posthog analytics configurable
([#3707](#3707))
([7ae9101](7ae9101))


### Bug Fixes

* `/llm/costs` table scrolling
([#3722](#3722))
([6a42ba8](6a42ba8))
* apply MCP OAuth lifetime for gateway slugs
([#3711](#3711))
([362aaec](362aaec))
* Bedrock tool name encoding
([#3706](#3706))
([0e2c2d1](0e2c2d1))
* costs timeframes and surface limit reset settings
([#3709](#3709))
([6e4154b](6e4154b))
* jira oauth discovery overrides
([#3721](#3721))
([2c4cf8f](2c4cf8f))
* OIDC discovery trusted origins for IdP registration
([#3714](#3714))
([adb5f5e](adb5f5e))
* preserve shared chat agents on fork
([#3715](#3715))
([252edfc](252edfc))
* reranker model dropdown labels
([#3704](#3704))
([ebd1c8a](ebd1c8a))
* session logs loading state
([#3712](#3712))
([ffba126](ffba126))


### Miscellaneous Chores

* **ci:** add ID-JAG MCP e2e test
([#3702](#3702))
([1a5078a](1a5078a))
* **deps:** bump next from 16.1.7 to 16.2.3 in /platform/frontend
([#3708](#3708))
([d47967c](d47967c))
* use neutral token prefixes with legacy support
([#3719](#3719))
([db5929c](db5929c))

---
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: archestra-ci[bot] <222894074+archestra-ci[bot]@users.noreply.github.com>
Co-authored-by: Joey Orlando <joey@archestra.ai>
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.

1 participant