Skip to main content

Security Policy

Reporting a vulnerability

Do not open a public GitHub issue for security vulnerabilities. Email: [email protected] Include:
  • A description of the vulnerability
  • Steps to reproduce
  • The version / commit SHA you tested against
  • Your disclosure timeline preference
We’ll acknowledge within 72 hours and keep you updated on our fix. We’ll credit you in the release notes unless you prefer to remain anonymous.

Scope

In scope:
  • The HQ UI (apps/ui/)
  • The gateway image and its daemons (gateway/)
  • The Supabase migration and its triggers/RPCs (db/)
  • The installer (installer/)
  • Published Docker images on GHCR
Out of scope:
  • Supabase itself (report to Supabase)
  • OpenClaw (report to openclaw.ai)
  • Third-party dependencies (report to the respective maintainers; if HQ is affected we’ll track + patch)
  • Social engineering of users or maintainers

Trust model and known risks

HQ is self-hosted admin software with tenant-scoped row-level security. Self-hosted installs use a single default tenant, so all authenticated users share the workspace. If you’re planning to deploy it, these are the trust boundaries you should understand.

Supabase is fully trusted

The gateway uses your Supabase service-role key to write directly to the database. Anyone with that key has full read/write access to every table. Keep it in .env on disk; never commit it; rotate it if leaked.

The Docker socket is mounted into the runner

The runner service mounts /var/run/docker.sock so it can execute docker compose restart gateway, docker compose pull (update), and similar actions triggered from the UI. Any RCE in the runner is equivalent to root on the host. This is by design — the alternative would be shelling out to SSH, which has its own risks. If this is unacceptable for your threat model, run HQ in a dedicated VM or don’t expose the UI to untrusted users. A tighter future alternative is docker-socket-proxy with a narrowed action allowlist. Contributions welcome.

RLS and tenant isolation

Every table uses tenant-scoped RLS policies — authenticated users can only access rows where tenant_id matches the JWT’s app_metadata.tenant_id. Self-hosted installs use a single default tenant, so in practice all authenticated users see all data. Make sure Supabase email signup is disabled or invite-only, otherwise anyone who signs up for your Supabase project gets full HQ access within the default tenant. The multi-workspace registry adds database-level isolation on top; fine-grained per-role permissions are not implemented.

Default port bindings are loopback-only

docker compose up -d without the installer binds all HQ services (UI, noVNC, files-API) to 127.0.0.1. The installer’s “Tailscale” or “Public” mode explicitly flips them to 0.0.0.0. This is deliberate — if you docker compose up blindly on a cloud VM, nothing is publicly reachable. If you override UI_HOST_PORT or FILES_API_HOST_PORT to 0.0.0.0:* manually, you’re exposing those services to every interface on the host. Make sure you have a firewall, reverse proxy, or Tailscale between them and the internet. noVNC is not host-exposed — the UI proxies it through /api/novnc over Docker’s internal network.

The installer runs curl | bash

The one-line installer (curl install.yourhq.ai | bash) pipes untrusted input through shell. This is a standard open-source install pattern, but not risk-free. For production installs:
  1. Download the script first and read it:
    curl -fsSL install.yourhq.ai -o install.sh
    less install.sh
    bash install.sh
    
  2. Pin a specific release rather than main:
    curl -fsSL https://raw.githubusercontent.com/yourhq/yourhq/v0.1.0/installer/install.sh | bash
    
  3. Clone the repo and run ./installer/install.sh directly.
The installer itself invokes curl | sh from get.docker.com and tailscale.com/install.sh — we’re not signing or pinning those scripts. They’re canonical upstream install scripts. If you don’t trust them, install Docker and Tailscale manually and skip the installer.

Secrets are encrypted at rest

All credentials stored in the secrets table (channel tokens, OAuth tokens, user API keys) are encrypted with AES-256-GCM before writing to the database. The plaintext value is never stored in Supabase and never returned in API responses. Key derivation (self-hosted): The encryption key is derived from your SUPABASE_SERVICE_ROLE_KEY via HKDF-SHA256 (salt: yourhq-secrets-v1, info: aes-256-gcm, 32 bytes). Anyone with your service role key can derive the encryption key and decrypt secrets — this is the same trust boundary as the service role key itself. Key derivation (hosted): Uses a dedicated HOSTED_SECRETS_KEY held in the worker environment, separate from the service role key. Gateway-side decryption: The secrets_sync daemon (running in the runner container) fetches encrypted values, decrypts them in memory, and writes plaintext to .env files on the gateway-state volume (chmod 0600). These files are the same trust boundary as the service role key and the Docker socket. Agent tools read secrets from the environment — the LLM never sees the values. What this protects against:
  • Database dumps or backups leaking plaintext credentials.
  • SQL injection or RLS bypass exposing raw values.
  • Supabase dashboard viewers accidentally seeing token values.
What this does NOT protect against:
  • Compromise of the gateway container (secrets are in plaintext .env files on the volume).
  • Compromise of the service role key (derives the encryption key).
  • An agent’s tools intentionally logging or exfiltrating secrets (treat agent tool code as trusted).

Agent capabilities

Each agent runs in the gateway container with a full Chrome browser, a messaging channel (Telegram, Discord, Slack, or none), and the ability to call openclaw plugins. Agents can:
  • Browse any URL
  • Log into websites using saved cookies
  • Read/write to their git branch in the gateway’s volume
  • Read/write to your Supabase via the shared service-role credentials
  • Call openclaw plugins (browser automation, voice, phone control, etc.)
Agents cannot:
  • Escape the gateway container (unless they compromise the Docker socket via an RCE in the runner)
  • Read arbitrary files on the host
  • Install new plugins (openclaw manages the plugin list)
Treat agent outputs like untrusted input. If you have an agent that writes files, commit messages, or database rows, sanitize before acting on them.

Dependencies and supply chain

HQ pins specific versions of:
  • Next.js (UI)
  • OpenClaw (agent runtime)
  • openclaw plugins (installed from the openclaw marketplace)
  • Base Docker images (ubuntu:24.04, node:24-slim)
We run npm audit in CI and track advisories. CVEs in the dependency tree are tracked as GitHub issues labeled security. We bump patches in main and expect users to pull updated images. For production deployments, pin image tags:
# docker-compose.yml
services:
  ui:
    image: ghcr.io/yourhq/yourhq-ui:v0.1.0  # not :latest

Hardening recommendations

In rough order of importance:
  1. Enable MFA on your Supabase account. Dashboard → Project Settings → Authentication → enable MFA for your admin user.
  2. Disable Supabase email signup. Authentication → Providers → Email → disable “Enable Email Signups” if you don’t need users to register.
  3. Use Tailscale instead of public HTTPS. The installer’s “Tailscale” mode keeps HQ unreachable from the public internet. Only use “Public HTTPS” when you actually need shared access.
  4. Pin image versions instead of :latest.
  5. Put HQ behind an auth gate. Cloudflare Access, Tailscale, or a reverse proxy with basic-auth — anything that requires authentication before a request hits HQ.
  6. Review the installer before running. curl -o install.sh ...; less install.sh; bash install.sh.
  7. Generate strong secrets. openssl rand -hex 32 for GATEWAY_AUTH_TOKEN. Use a password manager for your Supabase credentials.
  8. Update regularly. Until gateway update actions are available in the UI, run docker compose pull && docker compose up -d manually.

Hosted trust model

If you use the hosted offering at app.yourhq.ai, the trust boundaries are different from self-hosted. Here’s what you should know.

Your data lives in a dedicated database

Each hosted workspace gets its own Supabase project. Your data is not shared with other tenants — there is no multi-tenant database. Row-level security policies enforce tenant_id isolation within each project, but in practice each project has exactly one tenant.

Credentials are encrypted at rest

Your Supabase service-role key, database password, and VNC password are encrypted with ChaCha20-Poly1305 before being stored in the control-plane database. The encryption key (HOSTED_SECRETS_KEY) is held in the worker’s environment, not in the database. User-managed secrets (API keys, channel tokens, OAuth tokens) in your workspace’s secrets table are separately encrypted with AES-256-GCM using the same HOSTED_SECRETS_KEY. This is a different encryption scheme from the control-plane credentials above, but uses the same key material.

What the control plane stores

The master database (hosted_workspaces) stores:
  • Your email address and display name
  • Stripe customer and subscription IDs
  • Your workspace’s Supabase project reference, URL, and anon key (not secret — same data the browser uses)
  • Encrypted service-role key and database password
  • E2B sandbox ID, status, and access URLs
  • Provisioning progress and setup metadata (workspace name, chosen template)

What we can access

  • Supabase Management API: we use this to create, configure, and delete your Supabase project. We can read project status and API keys. We cannot read your data through the management API.
  • Service-role key: decrypted at provisioning time and passed to your gateway sandbox. After provisioning, the worker only decrypts it when the UI requests your workspace config (to connect your browser to your Supabase). We do not read your Supabase data.
  • E2B sandbox: we can pause, resume, and destroy your sandbox. We do not connect to it, run commands in it, or read files from it during normal operation.
  • Worker internal token: authenticates requests between the UI and the worker service. It is not a user credential.

What we cannot access

  • Your Supabase data: we don’t query your contacts, tasks, knowledge items, or agent data. The UI connects directly from your browser to your Supabase project.
  • Your agent sessions: agents run inside the E2B sandbox. We don’t intercept, log, or monitor agent activity.
  • Your VNC sessions: noVNC traffic flows directly between your browser and the sandbox. We don’t proxy or record it.

Sandbox isolation

Each workspace runs in a dedicated E2B sandbox — an isolated Linux VM with its own kernel, filesystem, and network namespace. Sandboxes cannot communicate with each other. The gateway, daemons, and agents all run inside this single sandbox.

Logging policy

The worker logs provisioning stages, sandbox lifecycle events (created, paused, resumed, destroyed), Stripe webhook processing, and errors. Logs include workspace IDs and event types. They do not include your Supabase data, agent outputs, message content, or VNC session data.

Cancellation and data deletion

When you cancel a workspace, the sandbox is paused immediately (no compute charges during the grace period). After the 30-day grace period:
  1. The E2B sandbox is permanently destroyed
  2. The Supabase project is permanently deleted via the management API
  3. All encrypted credentials are cleared from the control-plane database
Both deletions must succeed before the workspace is marked as fully canceled. If either fails, the cleanup loop retries every 6 hours until confirmed.

Secrets and data we collect

HQ collects no telemetry. The self-hosted stack never phones home. The only network calls HQ makes are:
  • To your Supabase project (required)
  • To Tailscale’s control plane (if you enable Tailscale, managed by Tailscale not us)
  • To openclaw for plugin updates and agent-model auth (when you use Codex OAuth, etc.)
  • Installer: get.docker.com, tailscale.com/install.sh, GHCR (for image pulls)
  • Dependency updates: npm, PyPI, apt
No user data, telemetry, or usage metrics flow to a YourHQ server. There isn’t one.