Security
Control which outbound HTTP connections Inspire is allowed to make. This page lets you toggle specific categories of external access and maintain an allowlist of permitted hosts.
Navigate to Settings > Security.
Requires the Admin role.
Outbound HTTP Policy
Each toggle controls a category of outbound requests. Disabling a category blocks all HTTP calls in that category, regardless of the host allowlist.
| Toggle | What It Controls |
|---|---|
| RSS feed fetches | Outbound requests made by RSS data connectors |
| File connector URL fetches | Outbound requests from file-based connectors (JSON, CSV, XML URLs) |
| Google Fonts access | Access to fonts.googleapis.com and fonts.gstatic.com for font installation |
| Network reachability probes | Health check pings to Renderer and DataPool services |
| AI provider and proxy access | Calls to Anthropic, OpenAI, and the Optymyse AI proxy |
| Audit webhook delivery | Outbound HTTP requests for audit event webhooks |
| License server access | Communication with the Optymyse licensing server for validation |
Disabling License server access prevents online license validation. If your license requires periodic check-in, it may become invalid. Use offline licensing if your security policy forbids outbound connections.
Host Allowlist
The host allowlist restricts which external hosts Inspire can connect to. Enter one host per line.
Supported Formats
| Format | Example | Matches |
|---|---|---|
| Exact host | api.openai.com | Only api.openai.com |
| Wildcard subdomain | *.blob.core.windows.net | Any subdomain of blob.core.windows.net |
Default Allowlist
A fresh installation includes these default allowed hosts:
fonts.googleapis.com
fonts.gstatic.com
api.openai.com
ai.api.optymyse.com
*.blob.core.windows.net
licensing.api.optymyse.comYou can add or remove entries as needed. The allowlist is checked in addition to the category toggles — a request must pass both the toggle check and the host allowlist.
How Validation Works
When Inspire makes an outbound HTTP request:
- The request category is checked against the toggles. If the category is disabled, the request is blocked.
- The target host is checked against the allowlist. If the host is not in the list (and no wildcard matches), the request is blocked.
- If both checks pass, the request proceeds.
This applies to all outbound HTTP made by the Edge server, including AI API calls, connector data fetches, webhook delivery, and license server communication.
Cookie Security
Controls authentication cookie behaviour including HTTPS enforcement, cross-site policy, and session timeouts.
| Setting | Default | Description |
|---|---|---|
| Require HTTPS-only cookies | Off | When enabled, auth cookies are only sent over HTTPS. Enable for cloud deployments behind a TLS-terminating reverse proxy. Leave disabled for on-premise HTTP deployments. |
| SameSite policy | Lax | Controls cross-site cookie behaviour. Strict — cookies never sent cross-site (recommended for HTTPS deployments). Lax — cookies sent on top-level navigation (default, safe for on-prem). None — cookies sent on all cross-site requests (not recommended). |
| Session inactivity timeout | 4 hours | Sessions expire after this period of inactivity. Range: 1–24 hours. |
| Absolute session lifetime | 8 hours | Sessions expire after this period regardless of activity. Range: 1–72 hours. |
Changes to cookie security require a service restart to take effect. A banner will appear after saving to indicate this.
HSTS (HTTP Strict Transport Security)
When enabled, the server sends a Strict-Transport-Security header instructing browsers to only connect via HTTPS for the specified duration.
| Setting | Default | Description |
|---|---|---|
| Enable HSTS | Off | Sends the HSTS header on all responses. Only effective when HTTPS is also configured. |
| Max age (days) | 365 | How long browsers should remember to enforce HTTPS. |
HSTS is difficult to undo — once a browser receives the header, it will refuse HTTP connections for the configured duration. Only enable after confirming HTTPS works correctly.
Allowed Hosts
Restricts which Host header values each service accepts. This prevents host header injection attacks in deployments where services are exposed directly without a reverse proxy.
| Field | Default | Description |
|---|---|---|
| Edge | * | Hostnames the admin UI / API server accepts |
| Renderer | * | Hostnames the display rendering service accepts |
| DataPool | * | Hostnames the data ingestion service accepts |
Separate multiple hosts with semicolons (e.g. inspire.example.com;admin.example.com). Set to * to allow any host — this is safe when running behind a reverse proxy (Traefik, nginx, Kamal) that validates the Host header.
Changes to allowed hosts require a service restart to take effect. Renderer and DataPool read their allowed hosts from Edge’s runtime config API on startup.
Service-to-Service Authentication
Inspire uses an internal API key (InternalApiKey) for authentication between Edge, Renderer, and DataPool services.
Key Rotation
To rotate the internal API key without downtime:
- Set the new key as
INTERNAL_API_KEYand the old key asINTERNAL_API_KEY_PREVIOUSin all service environments. - Restart all services — they will accept both keys during the grace period.
- Once all services are running with the new key, remove
INTERNAL_API_KEY_PREVIOUSand restart.
DataPool Read Endpoints
All DataPool data read endpoints require the internal API key for authentication. Unauthenticated read requests are rejected with 401 Unauthorized. This ensures that only Edge and Renderer (which hold the internal API key) can query DataPool data, preventing direct unauthenticated access even on internal networks.
Endpoint Access Signing
Display and presenter access tokens can optionally use a dedicated signing key (EndpointAccessSigningKey) separate from the service authentication key. This limits blast radius — if the signing key is compromised, it cannot be used for service-to-service API access. Falls back to InternalApiKey when not set.
Device Pairing
Pairing codes are 8 characters from a 32-character unambiguous alphabet (no 0/O, 1/I/L), displayed as ABCD-EFGH. Codes expire after 15 minutes and are single-use. Device tokens are generated using cryptographically secure random bytes.
Proof of Play Integrity
Play event batches sent from Renderer to Edge are signed with HMAC-SHA256 using the internal API key. Edge verifies the signature before storing events, preventing forgery even if an attacker has network access to the internal API. Event timestamps are validated to be within 7 days of the current time (with 5 minutes of future tolerance for clock skew).
Account Security
Lockout thresholds and password complexity rules. Changes take effect immediately — ASP.NET Core Identity re-reads these options on every sign-in attempt and password change, so there is no restart banner.
| Setting | Default | Range | Description |
|---|---|---|---|
| Max failed login attempts | 5 | 1–20 | Account locks after this many consecutive failed sign-ins |
| Lockout duration (minutes) | 15 | 1–1440 | How long the lockout persists before the counter resets |
| Password minimum length | 8 | 1–128 | Shortest allowed password |
| Require a digit | On | — | Password must contain at least one 0-9 |
| Require lowercase | On | — | Password must contain at least one a-z |
| Require uppercase | On | — | Password must contain at least one A-Z |
| Require non-alphanumeric | On | — | Password must contain at least one symbol |
Raising the minimum length does not force existing users to change their password — it only applies to new accounts and password changes from that point on.
Rate Limits
HTTP request rate limits for six distinct policies. Each policy is a sliding window partitioned by IP (for unauthenticated endpoints) or user ID (for authenticated endpoints).
| Policy | Default permit limit | Window | Partition | What it protects |
|---|---|---|---|---|
| Auth | 10 | 5 min | Per IP | /api/Authenticate, license activation, account updates — brute force defence |
| Uploads | 30 | 1 min | Per IP | File, font, and stored file GET — bandwidth defence |
| AI | 20 | 1 min | Per user | AI generation endpoints — cost defence |
| Admin | 5 | 1 min | Per user | License, network, key rotation — tamper resistance |
| Pairing | 5 | 15 min | Per IP | Device pairing — enumeration defence |
| User mgmt | 60 | 1 min | Per user | Tenant user CRUD, resource sharing, API key config — abuse defence |
Changes hot-reload for new partition keys. In-flight sliding windows keep their old limit until they expire (at most the window duration). Rate-limited endpoints return HTTP 429 Too Many Requests with a Retry-After header.
Data Retention
Controls how long proof of play data and audit history are kept before automatic cleanup. Both run weekly via background jobs.
| Setting | Default | Description |
|---|---|---|
| Play event retention (days) | 90 | Auto-delete proof-of-play rows older than this. Set to 0 to disable. |
| Audit log and CRUD history (days) | 365 | Auto-delete AuditLog and ServiceStack CrudEvent rows older than this. Set to 0 to keep audit history indefinitely (compliance use case). |
At enterprise scale with 100+ endpoints rotating scenes every 30 seconds, the play events table grows approximately 288,000 rows per day. The weekly cleanup jobs delete old records in batches to avoid locking the database.
Setting audit retention to 0 is intentional — some compliance regimes require audit logs to be kept forever. Only use this if you have enough storage and an external backup strategy.
Audit Categories
Turn off categories of audit events you don’t want recorded. Disabled categories are not written to the AuditLog table or streamed to the webhook.
| Category | Covers |
|---|---|
| Licensing | License activation, deactivation, validation |
| Emergency | Emergency broadcast activation and cancellation |
| DeviceManagement | Display refresh, screenshot requests |
| Connectors | Connector registration, key rotation |
All categories default to enabled on fresh installs. Use this to quieten high-frequency noise (e.g. disable DeviceManagement if operators refresh displays constantly) while keeping compliance-critical categories like Licensing enabled.
Audit Webhook
Stream audit events to an external SIEM or logging service. The delivery job runs every 10 seconds but only POSTs when enough time has passed since the last successful delivery, so the effective cadence is controlled by the Interval setting below.
| Setting | Default | Range | Description |
|---|---|---|---|
| Webhook URL | — | — | HTTPS endpoint that receives POST with JSON array of audit events. Leave empty to disable. |
| Webhook secret | — | — | Shared secret sent in X-Webhook-Secret header for verification. |
| Batch size | 100 | 10–1000 | Maximum events per webhook delivery |
| Interval (seconds) | 30 | 10–600 | Minimum seconds between deliveries |
Failed deliveries are retried on the next cycle — the cursor only advances on successful delivery, so no events are lost. The webhook receives events without RequestBody to avoid leaking sensitive data.
Sensitive Value Masking
Audit detail strings are passed through a defence-in-depth scrubber before they are written to the database or pushed to the webhook. The scrubber redacts common credential patterns even if they accidentally end up in a free-text detail field:
| Pattern | Example input | Stored as |
|---|---|---|
password=... | password=hunter2 | password=*** |
secret=... | secret=abc123 | secret=*** |
token=... | token=eyJhbGc... | token=*** |
api_key=... / api-key=... / apikey=... | api_key=sk-ant-... | api_key=*** |
client_secret=... | client_secret=foo | client_secret=*** |
bearer=... | bearer=eyJhbGc... | bearer=*** |
License keys are additionally masked to first 8 + last 4 characters (e.g. Key: ABCDEFGH…WXYZ). Audited request DTOs also pick safe properties at the source — only the listed fields ever reach the detail string in the first place.
Audit webhook streaming requires the AUDIT_WEBHOOK entitlement (Professional tier and above).
Upload Limits
Controls how big an upload Inspire will accept. There are two layers: an application-layer limit enforced by FileUploadServices (hot-reload) and a transport-layer limit enforced by Kestrel (restart required — Kestrel captures its limit at startup).
| Setting | Default | Range | Description |
|---|---|---|---|
| Max file upload (MB) | 25 | 1–10240 | Images, video, PDF, SVG. Hot-reload. |
| Max font upload (MB) | 5 | 1–1024 | woff, woff2, ttf, otf. Hot-reload. |
| Kestrel max request body (MB) | 30 | 1–10240 | Transport-level ceiling. Restart required — Kestrel reads this when the HTTP server binds. |
The Kestrel limit must be greater than or equal to the largest application-layer limit, otherwise Kestrel will reject oversized requests before they even reach FileUploadServices. A restart-required banner appears when the saved Kestrel value differs from the active one.
Renderer Health Monitor
How aggressively Edge probes Renderer instances for health. Tighten for low-latency failover; relax for flaky networks where transient failures shouldn’t mark a node unhealthy.
| Setting | Default | Range | Description |
|---|---|---|---|
| Probe interval (seconds) | 15 | 5–600 | How often each Renderer is probed |
| Failure threshold | 2 | 1–20 | Consecutive failed probes before a Renderer is marked unhealthy |
Both hot-reload. Interval changes take effect on the next probe cycle; threshold changes take effect on the next failure.
MFA Enforcement (per tenant)
Configured via Settings > SSO, not this page. Each tenant has two flags that work together:
| Flag | Effect |
|---|---|
| MFA enabled | Shows the TOTP setup flow on the user account page |
| Require MFA for all users | Users who haven’t enrolled are flagged at login with mfaEnrollmentRequired=true in the response meta. The frontend redirects them to /account?mfa=required for enrolment on their next sign-in. |
Enabling “Require MFA” does not lock out existing users — they can still sign in (so the first admin who flips the toggle doesn’t lock themselves out), but they’re forced through enrolment before they can do anything else. Existing sessions are not interrupted.
Saving Changes
A sticky bar appears when you have unsaved changes. Click Save Changes to apply or Discard to revert.