Skip to main content

Administration & Instance Settings

This page is for the people who run a tripl instance: managing members, granting the right level of access, issuing API keys for agents and scripts, and tuning the server-wide settings the app exposes in its UI.

tripl is a single multi-user instance ("workspace") that contains every project. Almost everything an administrator does lives under Settings, which has two contexts:

  • Workspace — Members, Data sources, API keys, your own Profile and Security, and the owner-only Instance sections.
  • Project — per-project configuration (General, Plan rules), covered in the user guide rather than here.
Settings vs. environment variables

The Instance sections in the UI let an owner override a subset of the server's configuration, stored in the instance database. Everything else — database/broker URLs, the encryption key, the application secret — is set only through environment variables. Sections differ in when an override applies — some at use-time, some on the next restart (see When changes take effect). For the full env-var reference (including the variables behind the settings below) see Configuration.

Roles & permissions

tripl has exactly three roles, defined server-side as the UserRole enum (owner, editor, viewer):

RoleCan readCan edit plan contentManage members & rolesInstance settings
ViewerYesNoNoNo
EditorYesYesNoNo
OwnerYesYesYesYes

How roles are assigned:

  • The first user to register becomes owner. This guarantees every instance has at least one operator who can manage roles. Every subsequent registration defaults to editor.
  • A viewer is never created automatically — an owner has to downgrade a member to viewer in Settings → Members.
  • Only an owner can change anyone's role. The API refuses to demote the last remaining owner (400 Cannot demote the last remaining owner), so you cannot lock yourself out of the instance.
Role changes sign the user out

When an owner changes a member's role, every active session for that user is deleted in the same transaction, so the new permissions take effect on their next request. An in-flight request that already passed authentication still finishes with the old role; only the next request is affected.

How permissions are enforced

The backend gates endpoints with role/scope dependencies, not just UI hiding:

  • Editor-or-above is required for any mutation. Viewers receive 403 Editor role required.
  • Owner is required for member role changes and all Instance settings. Non-owners receive 403 Owner role required.
  • Owner-only endpoints additionally require an interactive owner session — an API key can never reach them, even a write-scoped key owned by an owner (403 Owner session required).

Members

Settings → Members lists everyone with access to the workspace. The roster itself is visible to any authenticated user; only owners see the per-row role dropdown and can change roles. Non-owners see read-only role chips.

Each row shows the member's name (or email), email, join date, and role (Owner / Editor / Viewer). Owners cannot change their own role from this screen — use another owner account if you need to step down, and remember the last-owner guard above.

No invitations yet

There is no email-invitation flow in the current release. New members join by registering at the sign-in page; the instance then defaults them to editor, and an owner adjusts their role afterward. Registration is rate-limited (see Security & access).

Profile & account security

These two sections live under Settings → Account and apply only to the signed-in user.

Profile

Settings → Profile shows your details pulled from the authenticated account:

  • Full name, Email, and Role — all read-only here. Role is "Set by a workspace owner."
Profile preferences are not persisted server-side

Avatar upload, the Preferences block (Timezone, Date format, Start of week) and the Notifications toggles (Incident alerts, Review requests, Weekly digest) are presentation-only in this release. They operate on local state and are not backed by an API endpoint, so they do not save across devices or sessions. Treat them as not-yet-wired.

Account security

Settings → Security presents Password change, Two-factor authentication, and Active-session management.

Account-security controls are not functional yet

The password-change form, 2FA toggle, recovery codes, and the "Sign out all" / active-sessions list are presentation-only placeholders — there are no backend endpoints behind them in the current release. The only real authentication flows are register, login, and logout. To invalidate a user's sessions today, change their role (which deletes their sessions) or have them log out. Sessions also expire automatically after the configured TTL (session_ttl_hours, default 168 hours / 7 days).

API keys & governance

API keys are long-lived bearer tokens for non-browser clients — LLM agents and CLI scripts. They are managed per user at Settings → API keys (backed by /api/v1/me/api-keys). A user only ever sees and revokes their own keys; there is no cross-user key administration, even for owners.

Creating a key

The create form (and the POST /api/v1/me/api-keys endpoint) takes:

FieldNotes
NameRequired, 1–100 chars. A human label (e.g. claude-agent).
Scoperead (default) or write. See below.
ProjectOptional. Bind the key to a single project by slug, or leave as All projects for full account reach.
Expires in (days)Optional, 1–3650. Blank means no expiration.

Scopes:

  • read — GET endpoints only. Any write surface returns 403 API key has read-only scope.
  • write — full editor-level access, still subject to the owning user's role. A write key is useless beyond reading if its owner is a viewer. Creating a write-scoped key itself requires the creator to be editor or above (a viewer cannot mint a write key, and the UI hides the option).

Project binding is orthogonal to scope. A project-bound key authenticates only /projects/{slug}/... routes for that one project; any other project (403 API key is not authorized for this project), or any instance-wide route (/me/..., /users, ...), is rejected (403 API key is scoped to a single project). Use it to fence an agent into a single project. Unbound keys keep full cross-project reach.

The token is shown exactly once

On creation, the full token is returned a single time and displayed in a "Copy your API key now" dialog. Copy it immediately — only a non-secret prefix (tk_<scope-letter>_<first chars>, e.g. tk_r_… / tk_w_…) is stored and shown afterward. Server-side, only a SHA-256 hash of the token is persisted, so a database dump cannot replay tokens.

Governing keys

  • Inventory. The Active keys card lists every key with its name, prefix, scope chip (read/write), bound project (or "All projects"), and last-used / status (never used, used <date>, expired, or revoked). last_used_at is updated on use but throttled to at most once per ~60s to avoid write amplification on the auth hot path.
  • Revocation is immediate and soft. Revoking sets revoked_at; the key stops authenticating at once (callers begin receiving 401). Revoke is idempotent. Creating and revoking keys are recorded in the audit log (api_key.create / api_key.revoke).
  • Expiry is enforced server-side: an expired key authenticates as if unknown (401).

For how clients present these tokens (the Authorization: Bearer <token> header) and the endpoints they unlock, see the Agent API guide.

Instance settings (owner only)

Settings → Instance is visible only to owners — non-owner accounts see an "Owner role is required to view or change instance-level settings" message, and the API rejects them (GET/PATCH/PUT /api/v1/settings all require an owner session). It exposes a curated subset of the server configuration as overrides stored in the database.

How overrides work

Each editable field resolves as database override → environment value. The defaults come from the environment / Settings object; saving a value in the UI writes an override row, and a per-section Reset removes the override so the field falls back to its env value. Most fields carry a small source badge showing whether the value is currently coming from env or an override.

Secrets are write-only

Secret fields — the AI API key, the search-embedding API key, and the SMTP password — are encrypted at rest (Fernet) and never returned to the browser. The UI shows only a Configured / Not configured placeholder; leave the field blank to keep the existing value, type a new value to replace it, or use Clear to remove the override. Encryption requires ENCRYPTION_KEY to be set (see Configuration).

When changes take effect

Sections apply at one of two times:

Use-time (no restart). The Runtime (query limits, app base URL), Email, and AI sections are resolved override → env value on each call, so edits apply immediately. The worker falls back to env-only config if it can't read the settings table, so background jobs never fail on a settings read.

Restart-time (next deploy). The Security & access, Storage, and Observability sections are consumed by parts of the server that are wired once at process start — the middleware stack (CORS, security headers, the session cookie), the auth rate limiters, the photo storage backend, logging, and the metrics route. At startup the saved overrides are applied onto the running configuration before any of those are built, so they take effect on the next restart/redeploy — exactly what the UI's "overrides take effect on the next deploy" note means. You no longer need to also set the matching environment variable; the env var is just the default the override replaces.

A running process won't pick these up live

Because these sections are read once at boot, editing them does not change a process that's already running — restart the API (and worker) container for the new values to take effect. A bad value (e.g. a CORS origin that locks you out) is recovered the same way you set it: edit it back, or clear the override, and restart. The matching environment variable still works as the baseline default — see Configuration.

The sections, with the fields each exposes:

Runtime

Core server configuration.

  • App base URL — used in emails, webhooks and the ingest endpoint (app_base_url).
  • Scan row limit default — default warehouse row cap for scans (scan_row_limit_default, default 50,000).
  • Metrics row limit default — default row cap for metric queries (metrics_row_limit_default, default 100,000).

Email

SMTP transport for alert delivery (and, in the UI's framing, invitations and digests). Leaving SMTP host blank disables email destinations.

  • SMTP host (smtp_host)
  • Port (smtp_port, default 587)
  • SMTP username (smtp_username)
  • SMTP password (smtp_password, secret — write-only)
  • Use TLS (smtp_use_tls, default on)
  • Default From address (smtp_from_address) — used when a destination doesn't override it.

AI

Powers anomaly explanations, schema/description suggestions, and the assistant. Disabled by default because plan content is sent to the configured provider when enabled.

  • Provider: AI enabled (ai_enabled), Base URL (ai_base_url, default https://api.openai.com/v1; http and localhost endpoints such as a local LLM are allowed), Model (ai_model, default gpt-4o-mini), AI API key (ai_api_key, secret), and a Test AI button that runs a live connection check against the provider.
  • Generation: Timeout seconds (ai_timeout_seconds, default 30), Max output tokens (ai_max_output_tokens, default 700), and three editable system prompts — Describe prompt, Ask prompt, Alert explanation prompt — which fall back to built-in defaults.
  • Search embeddings: toggle (search_embeddings_enabled), read-only Embedding dimensions (search_embedding_dimensions, default 1536, env only), provider (search_embedding_provider, default openai), model (search_embedding_model, default text-embedding-3-small), and Embedding API key (search_embedding_api_key, secret).

If no key is set on either AI secret field, the server falls back to the OPENAI_API_KEY environment value when resolving the effective key.

Security & access

Authentication and network policy for everyone on the instance.

  • Sessions: Session cookie name (session_cookie_name, default tripl_session), Session TTL hours (session_ttl_hours, default 168), Secure cookie (session_cookie_secure).
  • Network & headers: CORS allow origins (cors_allow_origins, comma- separated), Security headers (security_headers_enabled), HSTS (hsts_enabled) and HSTS max age (hsts_max_age_seconds), Content Security Policy (content_security_policy).
  • Rate limiting: master toggle (rate_limit_enabled), Login limit (rate_limit_login_per_minute, default 5/min), Register limit (rate_limit_register_per_hour, default 3/hour), and Trust X-Forwarded-For (rate_limit_trust_forwarded_for).
Trust X-Forwarded-For only behind a trusted proxy

rate_limit_trust_forwarded_for defaults to false. Enable it only when a trusted proxy/load balancer sits in front and overwrites X-Real-IP on every request. On a directly-exposed API, a raw X-Forwarded-For is attacker-controlled — trusting it lets an unauthenticated caller rotate the header per request and bypass the rate limit entirely. Because this section is read from the environment, set it via the RATE_LIMIT_TRUST_FORWARDED_FOR env var, not the UI override.

Storage

Where event photos are persisted (photo_storage_backend: Local filesystem or Google Cloud Storage).

  • Backend: Photo storage backend, Photo max size (photo_max_size_mb, default 10), Allowed MIME types (photo_allowed_mime).
  • Local filesystem: Local photo directory (photo_local_dir, default ./var/photos).
  • Google Cloud Storage: GCS bucket (gcs_photo_bucket), GCS public URLs (gcs_photo_public), GCS credentials path (gcs_photo_credentials_path, blank falls back to Application Default Credentials), Signed URL TTL (gcs_photo_signed_url_ttl_seconds, default 3600).

Observability

How tripl reports its own health.

  • Logging: Log level (log_level: DEBUG/INFO/WARNING/ERROR/CRITICAL, default INFO), JSON logs (log_json), Request ID header (request_id_header, default X-Request-ID).
  • Metrics & tracing: Prometheus metrics (prometheus_metrics_enabled — exposes /metrics), OTLP endpoint (otel_exporter_otlp_endpoint — setting a non-empty value opts the API and worker into OpenTelemetry auto- instrumentation), OTEL service name (otel_service_name, default tripl).

System (read-only)

A health/build panel. Each tile shows Configured or Unset for: Debug mode, Database URL, Sync database URL, RabbitMQ URL, Redis URL, Encryption key, and the OpenAI fallback key. Nothing here is editable — it reflects the process's environment so you can confirm the instance is wired correctly.

  • Configuration — the full environment-variable reference behind these settings.
  • Security — hardening the instance (secrets, HTTPS, CORS).
  • Deployment — running the instance (compose / GHCR image) and what a restart picks up.
  • Agent API guide — using API keys from agents and scripts.
  • Troubleshooting — common operational issues.