Skip to main content

Security architecture

How Nora protects credentials, sessions, runtime access, and operator data across the control plane, the provisioning workers, and the agent runtimes it manages.
Nora is built to be trustworthy in real operator environments: you run it on your own infrastructure, supported credentials are encrypted with your key by default, and every layer between a browser and a runtime enforces access control. This page collects the security posture in one place.

Secrets and credentials

  • LLM provider keys, integration credentials, remote-host SSH credentials, and per-agent runtime gateway tokens are encrypted at rest by default with AES-256-GCM before they are stored in PostgreSQL. The encryption key is the operator-owned ENCRYPTION_KEY generated during setup. Gateway tokens (the bearer that authenticates the control plane to each runtime sidecar) are encrypted on write and decrypted only at the point of use; they are never returned in API responses. NORA_ALLOW_PLAINTEXT_SECRETS=true is an explicit insecure recovery/development override and should never be enabled on an internet-facing installation.
  • Provider and integration credential reconciliation is exact and fail closed. Credential create, rotation, and deletion replace Nora-managed runtime environment, OpenClaw file/SQLite auth profiles, custom-provider configuration, MCP configuration, and managed default-model state from the current durable state, including empty state. Mutations are serialized with deployment and lifecycle reconciliation so an older job cannot republish a revoked key. New runtimes launch credential-neutral and receive the current state only inside that fence. A runtime that cannot be reconciled is stopped and quarantined; if Nora cannot confirm that containment, the mutation returns a committed 502 instead of reporting success.
  • Bootstrap artifacts are secret-free. Docker and NemoClaw immutable container metadata and startup scripts do not carry provider, integration, gateway, pairing, or MCP credentials. The one deliberate exception is the per-agent Hermes gateway key (API_SERVER_KEY), a platform-generated runtime identity credential that Docker Hermes sets in the container environment because the Hermes image’s s6-supervised gateway reads only the container environment — this exposes it to no principal that could not already read Nora’s managed runtime state (Docker-socket holders and the agent’s own container user). Kubernetes stores mutable credentials in per-agent Secrets rather than ConfigMaps or pod-spec literals. Runtime startup derives pairing state from the owner-only gateway token and rebuilds mutable auth/config from restricted managed state.
  • Setup generates strong secrets. The installer creates JWT_SECRET and ENCRYPTION_KEY with OpenSSL and preserves existing secrets on re-runs.
  • Production refuses weak secrets by default. The backend will not boot when JWT_SECRET is missing, too short, or a placeholder, or when ENCRYPTION_KEY is missing or malformed. The only encryption-key bypass is the explicit NORA_ALLOW_PLAINTEXT_SECRETS=true override, which logs a security warning and transfers responsibility to the operator.
  • Self-hosted first-run claim flow. A self-hosted instance may leave both bootstrap-admin fields blank; until the first user registers, signup runs in “claim this server” mode and that account becomes platform admin. Hosted PaaS never exposes public admin claim: an empty PaaS database requires explicit secure bootstrap credentials before the API listener starts. GET /api/auth/bootstrap-status exposes the safe claim state plus runtime OAuth visibility, platform mode, and signup-challenge metadata, never provider verification secrets.

Authentication and authorization

  • JWT-based sessions authenticate dashboard and API requests; OAuth sign-in flows hand off to backend-issued HttpOnly session cookies.
  • Role-based access control separates operators from platform admins — admin routes are guarded by explicit role checks, and the admin dashboard is a separate application surface.
  • Workspace API keys are tenant-bound capabilities. Agent scopes are checked before gateway, file, NemoClaw, Hermes, lifecycle, integration, monitoring, and ClawHub agent handlers, and the requested agent must be assigned to the key’s exact workspace. Agent list responses are filtered to that workspace, while deploy, adopt, and duplicate atomically create the assignment before any queue or runtime side effect. Demo activation, migration-draft deployment, and all Remote Docker agent operations remain session-only.
  • Migration inspection is a privileged boundary. Migration drafts and uploads require a browser session. Reading a live Docker container is available only to a platform admin on a self-hosted control plane because it crosses the backend Docker-socket boundary; hosted users and workspace API keys cannot invoke it. Live SSH pull is disabled until independently verified host-key pinning is available, so cross-host migration uses an exported bundle instead.
  • Scoped Agent Hub API keys can be issued, rotated, and revoked per tenant, with dedicated middleware guarding the public /api/agent-hub/* surface.
  • Signup abuse controls: signup-specific burst and daily rate limits, duplicate-email short-circuiting, and Cloudflare Turnstile or Google reCAPTCHA with server-side challenge verification. A self-hosted operator may opt out; hosted PaaS signup fails closed until a provider is configured.

API and edge hardening

  • helmet security headers and CORS restricted to the configured CORS_ORIGINS allowlist.
  • Layered rate limiting: a global limiter plus a stricter limiter on mutating requests in the API, and nginx-level rate-limit zones for auth and API paths at the public edge.
  • Centralized unexpected-error handling: uncaught failures route through a sanitizing error handler with correlation IDs. Some runtime and upstream-proxy routes intentionally return bounded operational failure messages, so operators should still keep detailed service logs private.
  • TLS setup is scripted (infra/setup-tls.sh) for public deployments, and public nginx trusts CF-Connecting-IP only from Cloudflare’s published networks so proxied per-IP controls remain accurate without accepting direct-header spoofing.

Runtime access control

  • The authenticated gateway proxy is the normal browser and control-plane path to a runtime’s API, and it restricts upstream addresses to an allowlist of ranges. Remote Docker also publishes runtime ports on the registered host so Nora’s workers can reach them; those ports must stay behind the documented firewall and private encrypted network.
  • Live exec, log, and metrics WebSocket streams enforce access control — runtime terminal and log streaming are authenticated, not open sockets.
  • Proxied agent assets are token-validated before they are served.
  • BYOC remote hosts pin their SSH host key. The first successful connection test records the remote host’s SSH public key (trust-on-first-use); every later connection — connection tests and deploys alike — rejects a host presenting a different key, so a changed/spoofed host (man-in-the-middle) fails closed rather than silently connecting.

Runtime isolation

Isolation strength is a property of the deploy target + sandbox profile, not a single global setting. Pick the tier that matches how much you trust the agent’s code.

Standard profile — Docker/Kubernetes (GA), Remote Docker (experimental)

  • Each agent runs in its own container with its own namespaces (PID, mount, network, IPC, UTS). Docker and Remote Docker enforce operator-set CPU and RAM limits (NanoCpus/Memory); their disk_gb value is metadata, not a storage quota. Kubernetes applies workload resource limits plus provisioned storage requests. The host kernel’s default container protections apply (Docker’s default seccomp profile, dropped ambient capabilities).
  • This is shared-kernel isolation: appropriate for first-party or trusted agent code, the same trust boundary you’d give any container you deploy. It is not intended as a hard boundary for arbitrary untrusted code — for that, use the NemoClaw profile or an independently configured VM/microVM runtime.

NemoClaw profile — hardened sandbox (experimental, OpenClaw only)

The nemoclaw profile is the hardened tier for running less-trusted agent code. On top of the standard limits it adds, verifiably in the provisioner:
  • Non-root execution as a dedicated unprivileged user (UID 998) under Landlock filesystem restrictions and a seccomp syscall filter (baked into the runtime image).
  • All Linux capabilities dropped (CapDrop: ["ALL"]), adding back only NET_BIND_SERVICE, with no-new-privileges set so a child process can never regain privilege.
  • noexec,nosuid tmpfs for the sandbox’s writable directories, so dropped payloads can’t be executed from scratch space.
  • Default-deny egress: the sandbox runs with no general bridge network; outbound access is mediated by the OpenShell network policy (deny by default), not open to the internet.

Proxmox LXC target (experimental)

Proxmox places standard OpenClaw or a prepared Hermes image in an unprivileged LXC managed through verified API TLS and pinned SSH. LXC remains a shared-kernel container boundary; Nora does not claim VM-grade or separate-kernel isolation for this adapter. NemoClaw on Proxmox remains blocked because writing policy files inside an LXC would not enforce the OpenShell sandbox contract.

Roadmap

  • gVisor / Kata via Kubernetes RuntimeClass — syscall-interception and microVM isolation for the standard Kubernetes target, selectable per agent. Not wired today; on the roadmap.
  • Default-deny egress NetworkPolicy templates for the standard Kubernetes target.

Control-plane / worker separation

The control plane and workers are separate services, but deployment access is not a worker-only security boundary. Provisioning runs in a dedicated worker over Redis/BullMQ; the backend API also mounts the Docker socket and shared backend-adapter code for runtime health checks and upgrade coordination. Treat both services as host-privileged components wherever those mounts are enabled.

Standards and protocols

  • MCP is first-class and shipped — Nora publishes a control-plane MCP server (@noraai/mcp-server, in the official MCP Registry) and supports per-agent MCP server management. See Operating Nora over MCP.
  • OpenTelemetry GenAI (gen_ai.* / OTLP + Prometheus export) — available: export runtime telemetry under the OTel GenAI semantic conventions so per-exchange chat spans and per-agent token/cost/resource metrics flow into your existing observability backend. Disabled by default and fail-open. See the OpenTelemetry export guide. (Per-tool-call sub-spans depend on runtime event streams and remain on the roadmap.)
  • A2A (Agent-to-Agent) — on the roadmap: serve Agent Cards / A2A discovery for managed OpenClaw and Hermes agents.

Backups and audit

  • Scheduled backups are encrypted with AES-256-GCM in a versioned archive format; restores verify the format header before decrypting.
  • Audit logging records operator actions in PostgreSQL for review.

Your data stays yours

Nora is self-hosted: the database, secrets, runtime workloads, and logs all live on infrastructure you control. There is no required external control plane and no phone-home dependency in the product. The full source is open under Apache 2.0 — every claim on this page can be verified in the repository. Found a vulnerability? Please report it responsibly — see the security policy and contact page.