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 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 application
  • app.contentgrid.com/deployment-id: Unique identifier for the deployment
  • app.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:

  1. Authentication: Client authenticates directly with Keycloak and receives a JWT with user attributes
  2. Request with Token: Client makes HTTPS request to Ingress with JWT in Authorization header
  3. JWT Validation: Gateway validates JWT signature using Keycloak’s public keys
  4. Gateway Routing: Gateway maps domain to application ID
  5. Policy Evaluation: Gateway queries centralized OPA with user attributes from JWT
  6. Residual Encoding: OPA returns residual expression that Gateway encodes in a new JWT
  7. Application Processing: Gateway forwards request with JWT containing residual to Application Server
  8. Data Access: Application decodes residual, translates to SQL filter, and queries database
  9. 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:

  1. 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.
  2. Event Routing: RabbitMQ routes the event to the appropriate consumer queue based on the routing key.
  3. Event Consumption: Slingshot consumes the event from its dedicated queue.
  4. Webhook Matching: Slingshot looks up webhook configurations matching the event’s entity type and trigger.
  5. Payload Enrichment: Slingshot merges originator metadata (application ID, deployment ID) into the event payload.
  6. Signature Generation: Slingshot generates an RS256-signed JWT scoped to the target endpoint URL.
  7. Webhook Delivery: Slingshot sends the enriched payload and JWT signature to the external endpoint via HTTPS POST.
  8. 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.