Claudium
Operations

Privacy posture

Exactly what leaves your machine, exactly what doesn't, and the secret-redaction filter that drops anything risky before it touches a viewer.

The summary: only metadata travels. Tool names, region labels, token counts, file paths. No file contents, no prompts, no model outputs, no clipboard.

If you need that to be more than a sentence, this page is the full version.

What the watcher reads

The watcher tails .jsonl files under ~/.claude/projects/. Each line is a Claude Code session log entry. The watcher parses the tool_use blocks out of each assistant message and ignores everything else — system prompts, user messages, model outputs, MCP request bodies. The full message stays in the log on your disk; only the tool-use metadata is forwarded.

For each tool call the watcher extracts:

  • The tool name (Read, Edit, Bash, …).
  • A region label, looked up via TOOL_REGION in lib/regions.js.
  • The output token count (from the usage block).
  • The first 200 characters of the tool input that look like a target (file path, command, query string). Bounded so a runaway tool call can't blow up the row.

Everything else stays on your machine.

What the secret filter drops

Before broadcasting any event the hub runs the target string through lib/redact.js, which scans for:

  • API-key shapes: sk-, ghp_, xoxb-, AKIA… AWS keys, anything matching [A-Za-z0-9_-]{32,} at high entropy.
  • Email + URL credentials: https://user:pass@….
  • JWT triplets: eyJ.eyJ.….
  • Inline password=…, token=…, secret=… patterns.

A hit drops the event — the broadcast never happens, the persist never happens, the contributor sees nothing on the viewer. The hub logs the redaction with the tool name + sender so we can investigate, never the matched substring itself.

The filter errs on the side of dropping; false positives are preferable to leaks. If a legitimate event is being dropped, the operator can lower the entropy threshold via env var (REDACT_ENTROPY_MIN, default 4.2 bits/char).

What the hub stores

When the hub runs in DB-backed mode (USE_DB_TOKENS=1 + DATABASE_URL set):

TableWhat's in it
eventsTool name, region, tokens, target, session id, contributor id, org id, ts.
turnsGrouped events + a compact summary string ("Read auth.ts; Edit auth.ts; ran tests"). No file contents.
turn_embeddings1536-dim vector of the summary. Reversible to "approximately what kind of work happened" — not to the original text.
sendersSender display name + SHA-256 of the contributor token. Never the plaintext token.
sessionsEmail + session id + ip + user-agent.
audit_eventsLogin + admin actions. Append-only.

What's not stored: file contents, prompts, model outputs, clipboard, full URLs (paths and query strings are stored up to 200 chars after redaction).

Network posture

The watcher connects outbound to the hub over wss:// (WebSocket Secure). No incoming connections to your machine; no inbound port to expose.

The hub speaks to:

  • The contributors' watchers (inbound WebSocket).
  • The viewers' browsers (inbound HTTPS + WebSocket).
  • The Postgres database (outbound).
  • OpenAI (outbound, embeddings only — never the live model API).
  • Stripe (outbound, billing only).
  • Resend (outbound, OTP emails only).

Each is configurable via env var. Self-hosters who don't want any of the optional dependencies (no OpenAI, no Stripe, no Resend) skip the env vars and the hub gracefully runs without that feature.

Right to delete

If a contributor leaves your team or your org cancels Claudium:

  • DELETE FROM senders WHERE token_hash = '…' revokes a specific token. The DB cascade does not cross to events — historical data is preserved (the spec is "events outlive senders").
  • DELETE FROM orgs WHERE id = … cascades to everything else: senders, memberships, events, turns, turn_embeddings, subscriptions. One DELETE = full org off-boarding.

/api/org/delete is queued as a one-click admin endpoint but isn't yet shipped. For now this is a psql operation.

What we're explicitly NOT promising

We don't promise:

  • Encryption at rest. Postgres handles that via your provider's defaults. We don't add an application-layer encryption envelope on top.
  • Field-level access controls beyond org boundaries. Everyone with a session in your org can see everyone else in your org. Per-user privacy within an org is a future feature, not a current one.
  • Air-gapped operation. Some optional features (OpenAI embeddings, Stripe billing, Resend OTP) require outbound network. The minimum viable Claudium (broadcast-only, no persistence) works offline, but it's not what most teams want.

Honesty about what we don't do > marketing about what we sort-of do.

On this page