Security & compliance
Security and keys
Authentication
Runtime API: Bearer token with a project API key (sk_live_… / sk-test-…) or a Keycloak access JWT for a user with organisation membership. Each key has HRN-based scopes covering specific route patterns; the server checks that at least one scope on the key matches the route’s required HRN. Admin API: only a verified Keycloak access JWT in Authorization: Bearer (same contract as OIDC sign-in to the web console). Web console: Keycloak OIDC with JWT tokens. Session management via keycloak-js. Users must be members of the target organisation.
Encryption at rest
All sensitive data — secrets, LLM provider keys, session events, contextVars — is encrypted at rest using AES-256-GCM. PlanVault™ uses Tink-only envelope encryption: each organisation has a DEK wrapped under the customer deployment key hierarchy, such as operator-supplied local Tink KEK material or a customer KMS integration. Plaintext DEK exists in memory only while unwrap succeeds.
DEK rotation can be triggered by an OWNER or ADMIN via the console or Admin API. A background worker re-encrypts all affected rows. During a healthy rotation (status IN_PROGRESS), new encrypted writes use the pending DEK version; if rotation metadata is inconsistent, those writes fail closed with HTTP 409 Conflict. KEK / KMS policy changes are deployment-operator procedures outside the PlanVault™ console.
Warning: DEK rotation
Runtime API keys (project)
Calls to /api/v1/projects/{projectId}/… accept a Bearer token from any valid key for that project (primary or additional) whose scopes cover the route’s required HRN. Plaintext is shown only at issuance or right after rotation. The console stores the primary key in per-user server-side UI preferences; keep integration secrets in your own systems. Do not embed keys in public frontends or source repos.
Warning: API key recovery
Outbound authentication
The credential an imported OpenAPI service authenticates with lives on a connection, not in a secret name: the model never sees or passes it, and the execution boundary applies it itself — after the plan was approved, immediately before the request is built. For oauth2_client_credentials PlanVault acquires the token itself (RFC 6749 §4.4, with client_secret_basic, client_secret_post, or a signed private_key_jwt), keeps it encrypted under the organisation key, refreshes it ahead of expiry with a skew, and serialises acquisition per connection — twenty parallel tool calls produce one request to the authorization server. An upstream 401 buys exactly one re-acquisition and one retry before failing as a typed token_refresh_failed. Neither the token nor the client secret nor the private key reaches an API response, a log line, the audit trail, run diagnostics, or an export.
For APIs that require mutual TLS, the mtls mode stores a client certificate and its key (unencrypted PKCS#8) under the same organisation key. It is the one credential that never reaches the request: the transport presents it during the handshake, before an HTTP message exists, so no header is added. PlanVault checks the pair on save — it signs and verifies a probe — so a key that does not match its certificate is refused with a message that says so, rather than as an opaque handshake failure inside somebody’s run. Replacing the certificate takes effect on the next call. It does not change whom PlanVault trusts: the JDK’s default trust managers stay in place, so a private CA remains a trust-store question for the deployment rather than something a connection can quietly relax. When the service wants a bearer token on top of the certificate, that same certificate authenticates the client at the token endpoint (RFC 8705 tls_client_auth) and both are applied.
The same mechanism covers everything outbound, not only imported OpenAPI services: an MCP server, an outbound webhook and a custom LLM provider each bind a connection too, and no other credential mechanism is left in the runtime. For a webhook the connection supplies both the credential and the HMAC signing secret — the signature itself stays webhook-specific, because it is computed over the request body, but the secret lives where every other one does: encrypted under the organisation key, with the same status, audit trail and rotation. A custom LLM provider's api key stopped being an encrypted column of its own and moved onto a connection as well. The practical consequence is single: "what authenticates this call" has one answer and one place to see it, and deleting a bound connection is refused with a 409 rather than quietly unauthenticating a live integration.
Roles & access control
Four organisation membership roles exist (stored uppercase): **OWNER**, **ADMIN**, **DEVELOPER**, **MEMBER**. **OWNER** and **ADMIN** share the privileged Owner/Admin gate: organisation soft-delete, DEK rotation, invitations and role changes, audit log listing, minting and rotating Runtime API keys (including primary), org/project LLM budget caps, session and audit retention, and outbound HTTP policy overrides where the API requires Owner/Admin. **OWNER-only** API surfaces include toggling **Semantic Routing Cache** (auto-scenario embedding cache) and organisation-wide **debug content access**. **DEVELOPER** uses Admin APIs guarded by “Developer or higher”: tool catalog, OpenAPI/MCP, scenarios, many LLM/integration settings, org/project secrets, **creating projects**; **cannot** manage membership, read audit logs, rotate the org DEK, delete the organisation, mint or rotate Runtime keys, or change budget caps / retention / outbound HTTP overrides. **MEMBER** uses chats and projects; Admin API catalog/scenario management returns **HTTP 403**. For **Runtime JWT** calls, HRN scopes come from `RoleScopeResolver`: Members get a narrow session/chat/history grant set; Developers get broader session/tools coverage but are denied selected writes (project integrations, session secrets, scheduled/recurring schedule writes). **Project API keys** are authorised from the key’s scopes alone, independent of the user’s org role.
Tenant-safe OpenTelemetry
Infrastructure telemetry is designed for customer-operated observability stacks. Deployments can export traces, logs, and bounded metrics through OTLP over mTLS to an operator-controlled collector, then route those signals to Tempo, Loki, Prometheus/Grafana, Datadog, Splunk, or another backend selected by the customer.
Tenant correlation uses W3C trace context plus `trace_id`, `span_id`, and a pseudonymous `organization_hash` derived from a separate observability HMAC key. Raw organisation ids, project ids, session ids, user ids, emails, JWT claims, and payload values should not be emitted as telemetry attributes or metric labels. Audit webhooks include `trace_id` when available so SIEM events can be correlated with distributed traces.
GDPR and data
Where to manage retention and how to download copies for portability and data-subject requests. Legal agreements, ROPA, and deployment providers remain your organisation’s responsibility.
Organisation data and retention
On this page: session retention; JSON export for the whole organisation; external user field with per-user export and erasure of that user’s sessions within the org; DEK rotation. JSON exports include decrypted event history; secret names may appear, secret values are never included. A full organisation archive may include summarised membership information and transparency metadata for routing suggestions (without embedding vectors), when permitted by export policy.
Project data export (GDPR)
Download a JSON archive for a single project (sessions and events in that project; secret values are not exported).
Deletion and auto-pruning
Deleting an organisation in the UI removes its projects, sessions, and related rows in the PlanVault™ database. Copies at external LLM providers, infrastructure logs, or your own backups still need separate handling.
Session retention (days) controls when the background job may prune sessions for the organisation (0 means no automatic pruning by this setting).
Admin API and automation
The same and additional operations (org/project export, per–external-user erasure, DEK rotation, and more) are available via the Admin API with a verified Keycloak access JWT (typically after console sign-in, or from your IdP for approved automation). See API docs and Swagger for endpoint reference and embedded Swagger.