Security

This page sets the operational security baseline for a Curiosity Workspace deployment. Treat it as the minimum to satisfy; align with your organization's specific policies on top.

Security domains

Domain What it covers Where to configure
Identity & auth How users sign in, session lifetime, MFA SSO providers
Authorization What users can see and do once signed in Permissions, Access Control Model
Data protection Encryption in transit and at rest, secrets handling This page + Configuration reference, Workspace secrets
Operational security Patching, image hygiene, change control Deployment, Upgrades
AI data handling What leaves the workspace and reaches a model provider This page + LLM Configuration
Audit & monitoring Who did what, when Monitoring

Baseline practices

  • TLS everywhere. Terminate TLS at a proxy or in-container; enable HSTS (MSK_USE_HSTS=true) and HTTP→HTTPS redirect (MSK_REDIRECT_TO_HTTPS=true).
  • No default credentials. Always set MSK_ADMIN_PASSWORD on first boot. After SSO is wired up, disable the local admin account.
  • Stable JWT key. Set MSK_JWT_KEY explicitly so tokens survive container restarts; rotate it deliberately, not accidentally.
  • At-rest encryption. Set MSK_GRAPH_MASTER_KEY and back it up. Without it, encrypted properties cannot be decrypted after a restore.
  • Secret manager for every MSK_* secret and every model-provider key (AWS Secrets Manager, Azure Key Vault, GCP Secret Manager, Vault).
  • Workspace secrets for anything your own code calls out with. Third-party API keys, service passwords and signing tokens used by endpoints, AI tools and scheduled code belong under Manage → Configure → Secrets, not in the code and not in an environment variable. See Workspace secrets.
  • Least privilege on tokens. Use scoped API tokens for connectors and endpoint tokens for external systems. Never share admin tokens.
  • Separate environments. Dev / staging / prod with distinct secrets and distinct JWT keys.
  • Stronger auth for admins. Require SSO with MFA for any account that has admin permissions.

Network posture

  • The workspace listens on a single HTTP port (MSK_PORT, default 8080). Terminate TLS in front of it.
  • Bind to 127.0.0.1 in local development and to the proxy network in production. Never bind directly to a public interface without an authenticating proxy.
  • Set MSK_PUBLIC_ADDRESS to the user-facing URL so generated links (SSO callbacks, email links) are correct.
  • Egress reaches your LLM/embedding provider. If your network policy requires it, route through MSK_HTTP_PROXY and document the allowlist.

Data protection

  • In transit: TLS to the workspace and to model providers (the workspace uses HTTPS when calling external APIs).
  • At rest: graph storage is encrypted with MSK_GRAPH_MASTER_KEY. Disk-level encryption (LUKS, EBS encryption, Azure Disk encryption, GCP CMEK) is recommended on top.
  • Backups: encrypt at the storage layer; replicate to a separate failure domain. See Backup and restore.
  • Sensitive properties: mark properties that must be encrypted on disk with the SDK's encryption attribute (where supported by your schema).
  • Credentials used by workspace code: store them as workspace secrets under Manage → Configure → Secrets. A stored value is write-only — it is never returned to the interface, never appears in a node preview, a search result, a query result or a workspace definitions export, and cannot be read through the generic field APIs. Code reads it with await GetSecretAsync(Secrets.TheName), and every read is written to the audit log with the user, the secret and the calling endpoint or tool. A definitions export carries only the declaration (the value is written as a NOT EXPORTED placeholder), and an import never overwrites a secret that already exists in the target workspace.

Schema and configuration visibility

Data-level permissions are only half of the exposure surface: names of node types, field names, tool source and agent prompts are configuration, and they are filtered too.

  • The start-up schema payload is filtered per user. GET api/schema/all — loaded by the front-end on start-up — drops node types the user is blocked from by node-type access control, and emits only the fields they may read; the key field is always kept so the schema stays renderable. The node-style list and the renderer list fetched in the same round-trip are filtered by node type the same way, so a restricted type cannot be inferred from its icon or its renderer. System admins get the unfiltered payload, because the admin surfaces must be able to manage a type restricted to a group the admin isn't a member of.
  • Full AI tool and agent listings are admin-only. tools/list and agents/list carry a tool's source and model-facing description, and an agent's system prompt and provider task. tools/list requires a system admin; agents/list is limited to system admins and the desktop app. The chat composer reads tools/user-list / agents/user-list instead, which return only what a person picking a tool needs: the identifier, the display name, the human-readable description and the icon.

Both are enforced server-side. Configure the underlying rules under Permissions and the Access Control Model.

Token discipline

The three token types and their use cases are documented in Token scopes. Two rules that fail-closed:

  • Connectors get ingestion tokens. Never grant admin to a connector token.
  • External systems get endpoint tokens. Path-scoped tokens are much smaller blast radius than API tokens.

Rotate tokens on a schedule. Rotating MSK_JWT_KEY invalidates every outstanding token at once — use this only when responding to a key-material compromise.

AI / model-provider data handling

Every call to an embedding or chat provider sends a payload to that provider. The choices that matter:

  • Which fields are embedded. Configured under Settings → AI Settings → Embeddings. Don't embed fields that shouldn't leave the workspace.
  • Which provider. Hosted (OpenAI, Anthropic), regional (Azure OpenAI), or self-hosted (a local OpenAI-compatible server). Self-hosted gives you the strongest data-residency guarantees.
  • Provider data policies. Confirm in writing whether your provider trains on the data you send. Most enterprise tiers commit to no training.

The LLM Configuration page documents per-provider setup. For deeper context on what gets sent, see Multimodal Search (for OCR/STT payloads).

Endpoint security

Custom endpoints accept user input and run inside the workspace process. Defensive checklist:

  • Validate every input at the top of the endpoint (Body.FromJson<T>() only gets you typing; you still need to enforce limits, allowlists, and required fields).
  • Use CreateSearchAsUserAsync(..., CurrentUser, ...) instead of the system-scoped variant unless you're knowingly exposing data the caller shouldn't normally see.
  • Avoid SSRF: if an endpoint makes outbound HTTP calls, allowlist destinations.
  • Set request and response size limits. Don't return whole documents when a summary will do.
  • Return safe errors. Surface a traceId and a generic message to the caller; log the full exception on the server side.
  • Never embed a credential. Read it with await GetSecretAsync(Secrets.TheName) at the point of use, and keep it out of logs and out of the response.

See Custom Endpoints.

AI tool security

Tools run within the user's security context and can call out to the graph and search engines. Two specific rules:

  • Scope the tool to a single, clear intent. Vague tools are picked at the wrong times and end up exposing more than intended.
  • Use scope.CurrentUser for retrieval. Permission-aware retrieval is the only thing standing between a tool call and a data leak.
  • Read credentials with await scope.GetSecretAsync(Secrets.TheName), and never let the value reach the tool's result — that goes straight back to the model.

See AI Tools and LLM Agents.

Audit and incident response

  • Audit log forwarded to your SIEM (see Monitoring).
  • Enable the administrative audit types. Two of them matter for an admin trail and are part of the default set the audit page's Configure Auditing action turns on; check both under Manage → Access → Audit log:
    • Admin Action — a system admin performing a sensitive administrative operation. Each entry names the action and its inputs, and is written before the action runs. Covered today: resetting, uploading and patching the front-end website; the Docker self-update; stopping-and-storing the graph and restoring it from a backup; deleting a node schema or an edge schema; impersonating a user; issuing a Library, front-end-upload or Zapier token; and creating, updating, re-valuing or deleting a workspace secret.
    • Secret Accessed — endpoint or AI tool code reading a stored workspace secret, recorded with the user, the secret and the calling endpoint or tool. The value itself never appears in the entry.
  • Documented response plan for: token compromise, key compromise (MSK_JWT_KEY, MSK_GRAPH_MASTER_KEY), unauthorized admin access, model-provider data leak.
  • Quarterly restore drill (see Backup and restore) — a recovery you've never tested is not a recovery.

Next steps

© 2026 Curiosity. All rights reserved.
Powered by Neko