Engine Application

Security Implementation

Authentication establishes who the caller is; authorization is decided in two places — method security on the controllers, and the consumer rules in AgentService for project, agent and conversation access.

The externally relevant behaviour is documented in Authentication and Consumer Security; the shared vocabulary — roles, API keys and access control records — in Security Model.

Filter Chain

engine/security/SecurityConfiguration.kt builds the SecurityWebFilterChain:

  • CSRF disabled (the API is token-based), HSTS enabled.

  • anyExchange().permitAll() — the chain deliberately authorizes nothing at the exchange level. Endpoint authorization is expressed with @PreAuthorize on controller methods, which @EnableReactiveMethodSecurity activates. An endpoint without @PreAuthorize is therefore open.

  • ApiKeyFilter and BasicAuthWebFilter are added after the authentication order.

  • The OAuth2 resource server validates JWTs against the configured issuer (spring.security.oauth2.resourceserver.jwt.issuer-uri) with a converter that maps authorities through AuthorityMapper.

Authentication Mechanisms

Mechanism Implementation

JWT

Spring Security’s resource server support. AuthorityMapper reads realm_access.roles from the token and grants one authority per role. The idpRef and extRoles claims are consumed later, by the consumer authorization rules.

API key

ApiKeyFilter reads the X-Api-Key header or the apiKey query parameter and matches it against the enabled keys in promethist.security.api-keys.*, granting that key’s roles. An unmatched key responds 401 immediately; a request with no key at all continues unauthenticated.

HTTP Basic

BasicAuthWebFilter decodes the header and looks up the matching BasicIdentity document. Unknown credentials are rejected with 401; otherwise the request carries a BasicAuthenticationToken (username and secret) whose authorities are the identity’s roles, which @PreAuthorize checks. The identity’s idpRef and extRoles are used by consumer authorization. The distinct token type matters — a plain UsernamePasswordAuthenticationToken is also used by the API key filter and by the Twilio and Slack channels, which carry a principal without credentials.

None

Requests continue anonymously. What an anonymous caller may do is decided per endpoint and per project.

engine/security/Functions.kt exposes currentAuthentication and jwtAuthentication as reactive accessors, which is how controllers and the pipeline read the caller identity.

Within a conversation the identity can be replaced mid-stream with the #authorization:<jwt> control token — see Session Windowing.

Roles

The role vocabulary is defined in common/security/Constants.kt: admin, owner, viewer, manager, designer, consumer. Controllers reference them through the ROLE_* constants in @PreAuthorize expressions, for example:

@PreAuthorize("hasAuthority('$ROLE_MANAGER')")
@GetMapping("/api/session/{sessionId}")

Consumer Authorization

Which projects and agents a caller may list and run is decided by AgentService, not by the filter chain. The rules — identity requirement modes, identity provider claim matching, optional external role authorization, agent state visibility and direct reference resolution — are specified in Consumer Security.

Access control records themselves are AccessControlList documents scoped to a tenant or a project, read through AclService in common, which is enabled by promethist.acl-service.enabled. A record grants a role either to an internal user ID or to an external identity (identity provider ref plus role), which is what makes enterprise role mapping possible without provisioning users up front.

Secrets

Provider credentials that must be scoped per tenant or project are stored as Secret documents and read through SecretService. Deployment-wide credentials come from the mounted properties file and the environment instead — see Core Deployment.

Debugger Authorization

The debugger endpoints require an authenticated user, and additionally check through LLMDebuggerAuthorizationService that the user holds an ACL record on the project or tenant of the session being inspected. Session content is not visible to any authenticated user.

Data Protection in Logs

SensitiveInformationMaskerService masks personal and sensitive data in tokens and output elements before they reach the logs or the LLM invocation records, so diagnostics do not become a second copy of the conversation content.