Context
Surfaced in CodeRabbit review of PR #354 (epic/ai-engine → main).
The DJ AI settings page (dashboard/app/(dj)/settings/ai/page.tsx) decides which connector types to offer by fetching the connector policy from /api/admin/llm/policy — an admin-only endpoint. For a normal (non-admin) DJ that call always returns 403, the page treats the resulting null as "all connector types allowed", and shows every provider.
The server still enforces policy at create time (_check_connector_type_allowed returns 403 for disallowed types), so this is not a security hole — but it's a UX problem: a DJ can pick a provider the admin disabled and only discover it's blocked when the create call fails.
Failing closed on the client today is impossible without breaking every DJ (no DJ-readable policy source exists), so PR #354 left the behavior as-is and filed this follow-up.
Proposed fix
Add a DJ-readable policy endpoint, e.g. GET /api/llm/policy, returning the non-sensitive policy surface a DJ needs:
llm_apikey_connectors_enabled
llm_compatible_connector_enabled
- (the set of allowed connector types)
Then have the settings page consume that (not the admin endpoint) and fail closed when it's unavailable.
Acceptance criteria
- New DJ-scoped (
get_current_active_user) policy endpoint + schema + frontend client.
/settings/ai reads the DJ endpoint; on fetch failure, hides api-key connector types (fail closed) rather than showing all.
- Tests: endpoint authz (DJ ok, pending blocked), and the page hides disallowed types.
Context
Surfaced in CodeRabbit review of PR #354 (epic/ai-engine → main).
The DJ AI settings page (
dashboard/app/(dj)/settings/ai/page.tsx) decides which connector types to offer by fetching the connector policy from/api/admin/llm/policy— an admin-only endpoint. For a normal (non-admin) DJ that call always returns 403, the page treats the resultingnullas "all connector types allowed", and shows every provider.The server still enforces policy at create time (
_check_connector_type_allowedreturns 403 for disallowed types), so this is not a security hole — but it's a UX problem: a DJ can pick a provider the admin disabled and only discover it's blocked when the create call fails.Failing closed on the client today is impossible without breaking every DJ (no DJ-readable policy source exists), so PR #354 left the behavior as-is and filed this follow-up.
Proposed fix
Add a DJ-readable policy endpoint, e.g.
GET /api/llm/policy, returning the non-sensitive policy surface a DJ needs:llm_apikey_connectors_enabledllm_compatible_connector_enabledThen have the settings page consume that (not the admin endpoint) and fail closed when it's unavailable.
Acceptance criteria
get_current_active_user) policy endpoint + schema + frontend client./settings/aireads the DJ endpoint; on fetch failure, hides api-key connector types (fail closed) rather than showing all.