Runtime Platform
The Runtime Platform provides the infrastructure and services required to run, secure, and manage ContentGrid applications. It integrates several components to handle authentication, authorization, policy enforcement, routing, and frontend delivery.
Platform Overview
The Runtime Platform is designed as a Kubernetes-native system. Components dynamically discover resources like ConfigMaps, Secrets, and Services to route requests, enforce policies, and configure applications. This dynamic discovery enables zero-configuration deployment of new applications—the platform automatically detects and integrates them.
graph TB
subgraph "Runtime Platform"
Gateway[Gateway<br/>Entry Point & Routing]
Keycloak[Keycloak<br/>Authentication]
OPA[OPA<br/>Policy Evaluation]
Solon[Solon<br/>Policy Collection]
Navigator[Navigator<br/>Shared Frontend]
Liaison[Liaison<br/>Config Service]
Pathfinder[Pathfinder<br/>Ingress Management]
Ingress[Kubernetes Ingress]
RabbitMQ[(RabbitMQ<br/>Message Broker)]
Slingshot[Slingshot<br/>Webhook Delivery]
TokenMonger[TokenMonger<br/>Automation Auth]
subgraph "Application Instance"
AppServer[Application Server]
end
end
Client --> Gateway
Client --> Keycloak
Gateway --> OPA
Gateway --> AppServer
Navigator -->|token exchange| TokenMonger
Client --> Navigator
Navigator --> |webbrowser request| Liaison
AppServer --> Solon
Solon --> OPA
AppServer -->|publishes events| RabbitMQ
RabbitMQ -->|delivers events| Slingshot
Slingshot -->|HTTPS POST| External[External Systems]
Pathfinder -.->|Creates| Ingress
Ingress -.->|Routes to| Gateway
External -->|token exchange| Tokenmonger
Core Components
Gateway
The Gateway serves as the entry point for all ContentGrid applications. It handles routing and coordinates with authentication and authorization services.
Primary Responsibilities:
- Route requests to the appropriate application based on the request’s domain
- Enforce CORS policies configured per application
- Coordinate user authentication with Keycloak
- Communicate with Open Policy Agent (OPA) for policy evaluation
Dynamic Routing: The Gateway maintains a mapping from domains to application IDs by reading Kubernetes ConfigMaps. When a request arrives for a specific domain name, the Gateway uses this mapping to determine the corresponding application and routes to the appropriate Service for that application.
CORS Configuration: Each application’s CORS origins are configured in a ConfigMap. The Gateway reads these configurations and merges CORS settings for both the API backend and the Navigator frontend, ensuring cross-origin requests are properly handled.
Application Server
The Application Server serves dynamic REST APIs generated from application models. Each ContentGrid application runs as an instance of the same Application Server container, with behavior determined by the application artifact loaded at startup.
The Application Server follows a configuration-driven approach where a single container image serves all applications, enabling consistent operations and rapid iteration without code generation.
For complete details on the Application Server architecture and components, see Application Server.
Keycloak
Keycloak provides authentication and identity management for the platform. Each application has a corresponding realm in Keycloak, though applications can share a realm (typically one realm per organization).
Key Functions:
- Authenticate users via OpenID Connect (OIDC)
- Store user attributes used in authorization policies
- Issue JWT tokens containing user identity and attributes
- Manage OAuth clients for both API access and frontend applications
User attributes stored in Keycloak (such as department, role, or clearance level) are included in JWT tokens and used by applications when evaluating attribute-based access control policies.
Keycloak is an open source project. More information about Keycloak can be found on keycloak.org.
Open Policy Agent
OPA is a centralized policy engine that evaluates attribute-based access control (ABAC) policies for all applications in the platform.
Key Functions:
- Evaluates Rego policies to determine authorization decisions
- Performs partial evaluation to return residual expressions when complete evaluation isn’t possible
- Receives policy bundles from Solon containing all application policies
- Queried by the Gateway before requests reach applications
When the Gateway receives a request, it queries OPA with user attributes and request context. OPA evaluates the relevant policy and returns either a decision (allow/deny) or a residual expression that the Gateway encodes in a JWT for the application to apply at the database level.
Open Policy Agent is an open source project. More information about Open Policy Agent can be found on openpolicyagent.org.
Solon
Solon collects Rego policy files from all applications and makes them available to OPA for policy evaluation.
Policy Collection:
- Discovers applications by querying Kubernetes Services with policy annotations
- Fetches policy files from application management endpoints via HTTP
- Bundles all policies together for OPA consumption
- Keeps OPA’s policy bundle up to date as applications are deployed or updated
Solon acts as the bridge between individual applications (which serve their own policy files) and the centralized OPA instance (which needs all policies to evaluate authorization requests).
Navigator
Navigator is a shared React frontend application used by all ContentGrid applications. Rather than deploying separate frontends per application, a single Navigator instance dynamically adapts to each application’s data model.
Adaptive Behavior:
- Discovers entities and available operations through HAL links
- Renders forms dynamically using HAL-FORMS templates
- Adapts to user permissions automatically (forms only show permitted actions)
- No application-specific code required—purely hypermedia-driven
Deployment Model: Pathfinder creates a separate Ingress resource for Navigator for each application, routing based
on domain. The Navigator instance then loads application-specific configuration from Liaison based on the request’s
Host header.
Liaison
Liaison serves configuration for Navigator on a per-application basis. It acts as a configuration service that provides the necessary settings for Navigator to connect to the correct application and authentication realm.
Configuration Delivery:
- Serves Navigator configuration based on the domainname of the request
- Provides OIDC client ID and issuer URL for authentication
- Enables a single Navigator instance to serve multiple applications
Pathfinder
Pathfinder automatically creates and manages Kubernetes Ingress resources for applications. It watches ConfigMaps and translates them into Ingress configurations, enabling external access to application services.
Two Deployment Variants:
- Pathfinder: Creates Ingress resources for application API backends
- Pathfinder for webapp: Creates Ingress resources for Navigator frontend
Resource Management:
- Reads ConfigMaps with domain routing configuration
- Creates Ingress resources with appropriate routing rules
- Coordinates with cert-manager for TLS certificate provisioning
Certificate Management: When Pathfinder creates an Ingress, cert-manager automatically provisions TLS certificates. Pathfinder adds annotations to ConfigMaps indicating which cluster issuer to use, and cert-manager handles the certificate lifecycle.
RabbitMQ
RabbitMQ serves as the message broker for the Runtime Platform, enabling an event-driven architecture where Application Servers publish entity lifecycle events asynchronously. This decouples the core API request handling from downstream consumers like webhook delivery.
Key Functions:
- Receives entity lifecycle events (create, update, delete) published by Application Servers
- Routes events to dedicated queues using a direct exchange with topic-based routing keys
- Provides durable quorum queues that replicate messages across nodes for data safety
- Handles failed deliveries with a dead-letter exchange for error handling and retry policies
Event Routing: Application Servers publish events to a direct exchange (contentgrid.apps) with routing keys that
separate event types (e.g., contentgrid.events for entity lifecycle events). Each consumer has its own quorum queue,
ensuring that slow or failing consumers do not affect others. Failed deliveries are retried a limited number of times
before being routed to a dead-letter exchange for inspection.
Optional Component: RabbitMQ is an optional component of the Runtime Platform. When RabbitMQ is not deployed, event-driven features such as webhook delivery are unavailable, but the core API functionality remains fully operational.
RabbitMQ is an open source project. More information about RabbitMQ can be found on rabbitmq.com.
Slingshot
Slingshot is the outbound webhook delivery service. It consumes entity lifecycle events from RabbitMQ and delivers them as signed HTTP POST requests to external webhook endpoints configured by application administrators.
Key Functions:
- Consumes events from RabbitMQ containing entity change notifications
- Matches events to webhook configurations based on entity type and trigger type
- Enriches event payloads with originator metadata (application ID and deployment ID)
- Signs every webhook delivery with an RS256 JWT for verifiable authenticity
- Delivers webhooks to external endpoints over HTTPS with a configurable timeout
Dynamic Configuration: Slingshot fetches webhook configurations from Application Server management endpoints at runtime. When an event arrives, Slingshot looks up which webhook endpoints are registered for the matching entity and trigger type, then delivers the event to each matching endpoint.
Signature Verification: Every webhook delivery includes a ContentGrid-Signature header containing an RS256-signed
JWT scoped to the endpoint URL. Receivers can verify the JWT signature by fetching Slingshot’s public signing keys from
the /.well-known/jwks.json endpoint (routed through the Gateway). Signing keys are rotated regularly, and previously
used keys remain available for verification during the transition period.
For more information on configuring webhooks, see the Webhooks guide and Webhooks concepts.
TokenMonger
TokenMonger is the Authorization Service that manages secure token exchange between users, automations, and ContentGrid applications. It enables the “small core, extensible ecosystem” architecture by allowing external automations to authenticate users and access application data without receiving the user’s primary credentials.
Key Functions:
- Issues scoped tokens for automations to authenticate users and access application APIs
- Allows automations to access ContentGrid applications on behalf of a user, with at most the user’s privileges (delegated access)
Token Types: TokenMonger issues three types of tokens, each serving a specific authentication flow:
- User Extension Access Token: Allows a user to authenticate with an automation. Derived from the user’s application access token, this token is scoped to a specific automation and does not grant access to ContentGrid applications.
- Extension Application Access Token: Allows an automation to access a ContentGrid application under its own identity, without user context. Useful for background processing or autonomous operations.
- Extension Delegated Application Access Token: Allows an automation to access a ContentGrid application on behalf of a user. The automation receives at most the user’s privileges. The token carries information about both the user and the automation for audit purposes.
Gateway Integration: The user-facing token exchange endpoint is placed behind the Gateway, which resolves application configuration and creates a gateway-issued JWT that TokenMonger consumes. Automation-facing endpoints are available directly on the platform domain. The Gateway validates tokens issued to automations (EAAT and EDAAT) by verifying their signature against TokenMonger’s JWKS endpoints, and applies the same OPA policy evaluation as for regular user requests.
For more information on the security model, see Architecture Principles and Access Control.
Application Deployment
When an application is deployed to the Runtime Platform, several Kubernetes resources are created to integrate it with the platform services.
Application Service
A Kubernetes Service makes the application accessible to the Gateway and other platform components. The Service is labeled with the application ID and service type, enabling dynamic discovery.
Key Labels:
app.contentgrid.com/application-id: Unique identifier for the applicationapp.contentgrid.com/deployment-id: Unique identifier for the deploymentapp.contentgrid.com/service-type: Type of service (e.g.,api,webapp)
Service Discovery: The Gateway uses these labels to discover Services. When routing a request, the Gateway queries for Services matching the application ID determined from the domain mapping.
OPA Integration: The Service also includes an annotation (authz.contentgrid.com/policy-package) indicating the OPA
policy package location. This enables the platform to collect policies from applications.
Request Flow
Understanding how a request flows through the platform illustrates how these components work together.
sequenceDiagram
autonumber
participant Client
participant Keycloak
participant Gateway
participant OPA as Centralized OPA
participant App as Application Server
participant DB as PostgreSQL
Note over Client, Keycloak: Authentication Flow
Client ->> Keycloak: Login (if no valid token)
Keycloak -->> Client: JWT with user attributes
Note over Client, DB: API Request Flow
Client ->> Gateway: HTTPS Request + JWT
Gateway ->> Gateway: Validate JWT signature
Gateway ->> Gateway: Look up application by domain
Gateway ->> OPA: Authorization query (JWT claims)
OPA -->> Gateway: Allow/Deny + Residual expression
Gateway ->> Gateway: Encode residual in new JWT
Gateway ->> App: Forward request + JWT with residual
App ->> App: Decode residual from JWT
App ->> DB: Query with authorization filter
DB -->> App: Filtered results
App -->> Client: HAL JSON response
Step-by-Step:
- Authentication: Client authenticates directly with Keycloak and receives a JWT with user attributes
- Request with Token: Client makes HTTPS request to Ingress with JWT in Authorization header
- JWT Validation: Gateway validates JWT signature using Keycloak’s public keys
- Gateway Routing: Gateway maps domain to application ID
- Policy Evaluation: Gateway queries centralized OPA with user attributes from JWT
- Residual Encoding: OPA returns residual expression that Gateway encodes in a new JWT
- Application Processing: Gateway forwards request with JWT containing residual to Application Server
- Data Access: Application decodes residual, translates to SQL filter, and queries database
- Response: Application formats response as HAL JSON and returns through Gateway
Event-Driven Architecture and Webhook Delivery
Beyond synchronous API request processing, the Runtime Platform supports an event-driven architecture through RabbitMQ and Slingshot. When entity lifecycle events occur (create, update, delete), Application Servers publish these events asynchronously to RabbitMQ. This decouples the core API from downstream consumers, ensuring that webhook delivery and other event-driven features do not impact API response times.
The following diagram illustrates how entity change events flow through the platform to external webhook endpoints:
sequenceDiagram
autonumber
participant App as Application Server
participant RMQ as RabbitMQ
participant Sling as Slingshot
participant Ext as External System
Note over App, RMQ: Event Publishing
App ->> RMQ: Publish entity event<br/>(application_id, deployment_id, trigger, entity)
Note over RMQ, Sling: Event Consumption
RMQ ->> Sling: Deliver event from queue
Note over Sling, Ext: Webhook Delivery
Sling ->> Sling: Match event to webhook configs
Sling ->> Sling: Enrich payload with originator metadata
Sling ->> Sling: Sign payload with RS256 JWT
Sling ->> Ext: HTTPS POST with signed payload<br/>(ContentGrid-Signature header)
Ext ->> Sling: HTTP Response
alt Failed delivery (after retries)
Sling ->> RMQ: Nack message
RMQ ->> RMQ: Route to dead-letter exchange
end
Step-by-Step:
- Event Publishing: When a mutating operation occurs (e.g., an entity is created or updated), the Application Server publishes an event to RabbitMQ. The event includes headers identifying the application, deployment, trigger type, and entity.
- Event Routing: RabbitMQ routes the event to the appropriate consumer queue based on the routing key.
- Event Consumption: Slingshot consumes the event from its dedicated queue.
- Webhook Matching: Slingshot looks up webhook configurations matching the event’s entity type and trigger.
- Payload Enrichment: Slingshot merges originator metadata (application ID, deployment ID) into the event payload.
- Signature Generation: Slingshot generates an RS256-signed JWT scoped to the target endpoint URL.
- Webhook Delivery: Slingshot sends the enriched payload and JWT signature to the external endpoint via HTTPS POST.
- Error Handling: If delivery fails after the configured number of retries, the event is routed to a dead-letter exchange for inspection and manual intervention.
Scaling and High Availability
The Runtime Platform is designed for horizontal scaling and high availability:
Application Servers: Scale horizontally by increasing replica count. Each replica is stateless (except for database connections) and can handle requests independently. Kubernetes Services load balance across replicas.
Gateway: Runs as a highly available Deployment with multiple replicas. All replicas share the same configuration ( from ConfigMaps), and the Ingress load balances across them.
Keycloak: Can be deployed in clustered mode for high availability. Database-backed session storage enables failover between instances.
Navigator and Liaison: Stateless services that scale horizontally. Liaison reads configuration from Kubernetes API on each request (with caching), so all replicas have consistent configuration.
RabbitMQ: Can be deployed as a standalone instance for development or as a clustered deployment for high availability. Quorum queues replicate messages across cluster nodes, providing data safety even if individual nodes fail.
Slingshot: Stateless service that scales horizontally. Each replica independently consumes from the RabbitMQ queue. The only stateful dependency is the RabbitMQ connection; signing keys are loaded at startup and support rotation.
TokenMonger: Stateless service that scales horizontally behind the Gateway. Signing keys are loaded from the filesystem (backed by Kubernetes Secrets) at startup, with support for runtime key rotation through retired key loading.
Database and Storage: PostgreSQL and S3 are external to the platform and have their own high-availability mechanisms (e.g., PostgreSQL replication, S3 redundancy).
Operational Characteristics
Zero-Configuration Deployment: Adding a new application requires only creating the standard Kubernetes resources ( Deployment, Service, ConfigMaps, Secrets). The platform automatically discovers and integrates the application.
Independent Scaling: Each application scales independently. Heavy workloads on one application don’t affect others.
Resource Isolation: Each application has its own database, S3 bucket, and Keycloak realm (or shared by organization). Resource limits prevent one application from affecting others.
Observability: Standard Kubernetes observability tools work out of the box. Platform components expose Prometheus metrics, health check endpoints, and structured logs.
Summary
The ContentGrid Runtime Platform provides a Kubernetes-native infrastructure that:
- Automatically discovers and integrates applications through labels and dynamic service discovery
- Scales applications independently with horizontal scaling and load balancing
- Manages authentication and authorization through Keycloak and OPA
- Provides a shared Navigator frontend that adapts to any data model
- Handles TLS, routing, and CORS through Gateway and Ingress management
- Delivers entity lifecycle events to external systems via an event-driven architecture with RabbitMQ and Slingshot
- Enables secure extension integration through TokenMonger’s token exchange service
The platform’s design enables operational simplicity—deploying applications requires no platform configuration changes, and standard Kubernetes operations handle scaling, updates, and failover.