Context
chrono-sandbox is a master-credential catalog service (is_public_internal_master_credential_service): auto-provision creates per-user rows with no credential and no UserApiKey, and the proxy resolves the catalog's encrypted master credential at request time. Route identity configuration on those rows is platform-managed by design — user_service_service::update_user_service rejects auto-provisioned rows (ensure_user_managed_service), PUT /api/v1/keys/{id} rejects auto-connected keys, and catalog identity edits propagate to rows via catalog_identity_service.
The remaining gap is caller-side: when one caller needs route identity settings beyond the platform default (aevatarAI/aevatar#3530 needs forward_access_token=true, inject_delegation_token=true, delegation_token_scope ⊇ "proxy:* sandbox:execute" for its code-execution admission), the only per-caller shape would be an alias UserService of the same catalog service — and that is unreachable today:
unified_key_service::create_key requires a credential for node-less direct routing with no master-credential exemption (backend/src/services/unified_key_service.rs:923-935), so an empty-credential alias create fails 400 Credential is required for direct routing (or select a node), even though auto-provision creates exactly this row shape for the same catalog service.
- Proxy master-credential resolution is gated on
source == auto_provision && api_key_id == None (backend/src/services/proxy_service.rs:2463-2467), so such an alias row would not be served even if created.
A per-caller alias is the least-privilege shape here: it grants token forwarding and delegation scopes only to the caller that requested them, instead of widening the catalog default for every user of the service. Delegation-token minting already supports catalog-backed alias slugs (proxy_delegation_token_uses_catalog_slug_for_disambiguated_alias pins actor = catalog slug).
Goal
Allow a caller to create a forward-auth alias UserService of a master-credential catalog service without supplying a credential or node, and have the proxy serve it from the catalog master credential under the existing authorization checks.
Acceptance criteria
create_key accepts empty-credential, node-less creation when the target catalog service passes is_public_internal_master_credential_service: no UserApiKey is created, the auth snapshot comes from the catalog (auto_provision_auth_snapshot), an explicitly requested slug is preserved exactly (SlugCollisionStrategy::PreserveExact), and the request's forward_access_token / inject_delegation_token / delegation_token_scope are persisted with the existing SERVICE_DELEGATION_SCOPES validation.
- Proxy resolution serves credential-less rows of master-credential catalog services regardless of
source, keeping authorize_master_credential, the platform per-user rate limit, and the catalog predicate re-verification exactly as in the current auto-provision branch.
- A duplicate slug for the same user still returns 409; creation for non-master-credential services keeps the existing 400.
- Delegation tokens minted through the alias carry actor = catalog service slug (existing behavior, pinned for the new path).
- Tests cover: empty-credential node-less create for a master-credential service (row has no
api_key_id, requested identity fields persisted); a proxied request through the alias resolving the catalog master credential; unchanged 400 for an ordinary catalog service; unchanged 409 on duplicate slug.
Related: aevatarAI/aevatar#3530. Supersedes #1499 (same finding bundled with operational guidance; this issue carries only the implementation request).
Context
chrono-sandboxis a master-credential catalog service (is_public_internal_master_credential_service): auto-provision creates per-user rows with no credential and noUserApiKey, and the proxy resolves the catalog's encrypted master credential at request time. Route identity configuration on those rows is platform-managed by design —user_service_service::update_user_servicerejects auto-provisioned rows (ensure_user_managed_service),PUT /api/v1/keys/{id}rejects auto-connected keys, and catalog identity edits propagate to rows viacatalog_identity_service.The remaining gap is caller-side: when one caller needs route identity settings beyond the platform default (aevatarAI/aevatar#3530 needs
forward_access_token=true,inject_delegation_token=true,delegation_token_scope ⊇ "proxy:* sandbox:execute"for its code-execution admission), the only per-caller shape would be an aliasUserServiceof the same catalog service — and that is unreachable today:unified_key_service::create_keyrequires a credential for node-less direct routing with no master-credential exemption (backend/src/services/unified_key_service.rs:923-935), so an empty-credential alias create fails400 Credential is required for direct routing (or select a node), even though auto-provision creates exactly this row shape for the same catalog service.source == auto_provision && api_key_id == None(backend/src/services/proxy_service.rs:2463-2467), so such an alias row would not be served even if created.A per-caller alias is the least-privilege shape here: it grants token forwarding and delegation scopes only to the caller that requested them, instead of widening the catalog default for every user of the service. Delegation-token minting already supports catalog-backed alias slugs (
proxy_delegation_token_uses_catalog_slug_for_disambiguated_aliaspins actor = catalog slug).Goal
Allow a caller to create a forward-auth alias
UserServiceof a master-credential catalog service without supplying a credential or node, and have the proxy serve it from the catalog master credential under the existing authorization checks.Acceptance criteria
create_keyaccepts empty-credential, node-less creation when the target catalog service passesis_public_internal_master_credential_service: noUserApiKeyis created, the auth snapshot comes from the catalog (auto_provision_auth_snapshot), an explicitly requestedslugis preserved exactly (SlugCollisionStrategy::PreserveExact), and the request'sforward_access_token/inject_delegation_token/delegation_token_scopeare persisted with the existingSERVICE_DELEGATION_SCOPESvalidation.source, keepingauthorize_master_credential, the platform per-user rate limit, and the catalog predicate re-verification exactly as in the current auto-provision branch.api_key_id, requested identity fields persisted); a proxied request through the alias resolving the catalog master credential; unchanged 400 for an ordinary catalog service; unchanged 409 on duplicate slug.Related: aevatarAI/aevatar#3530. Supersedes #1499 (same finding bundled with operational guidance; this issue carries only the implementation request).