#safety audit question

1 messages · Page 1 of 1 (latest)

burnt carbon
#

gateway.trusted_proxies_missing Reverse proxy headers are not trusted
gateway.bind is loopback and gateway.trustedProxies is empty. If you expose the Control UI through a reverse proxy, configure trusted proxies so local-client checks cannot be spoofed.

Fix: Set gateway.trustedProxies to your proxy IPs or keep the Control UI local-only.
fs.state_dir.symlink State dir is a symlink
~/.openclaw is a symlink; treat this as an extra trust boundary.

What do these mean in plain language, and should I/how should I fix them?

teal novaBOT
#

To help others find answers, you can mark your question as solved via Right click solution message -> Apps -> ✅ Mark Solution

obtuse lionBOT
#

gateway.trusted_proxies_missing — “Reverse proxy headers are not trusted”

Plain language: If you put the Gateway/Control UI behind nginx/Caddy/Traefik/etc, that proxy will send headers like X-Forwarded-For to tell the app “the real client IP is 
”. OpenClaw does not trust those headers unless you explicitly list the proxy’s IP in gateway.trustedProxies.

Why it warns: some security checks treat “local” clients differently. If OpenClaw blindly trusted X-Forwarded-For, anyone could spoof “I’m localhost” and potentially bypass protections via a misconfigured proxy.

Do you need to fix it?

  • No if you only use the Control UI locally (e.g. open http://127.0.0.1:18789) and you’re not reverse-proxying it.
  • Yes if you’re accessing the Control UI through a reverse proxy (including Tailscale Serve / any TLS terminator in front).

How to fix (typical):

  • If the proxy runs on the same machine, set:
    • gateway.trustedProxies: ["127.0.0.1"]
  • If it’s in Docker, it might be something like 172.17.0.1 or the proxy container IP (depends on your network).

Docs: https://docs.openclaw.ai/gateway/security#reverse-proxy-configuration (and https://docs.openclaw.ai/gateway/trusted-proxy-auth)


#

fs.state_dir.symlink — “State dir is a symlink”

Plain language: Your OpenClaw state directory (default ~/.openclaw) is a symlink to somewhere else. That directory contains high-value stuff (config, tokens, credentials, session logs). A symlink adds another “trust boundary”: you now also need to trust the symlink target path and its permissions.

Do you need to fix it?

  • Not strictly — it’s a warn, not “critical”.
  • You should fix it if the symlink points to anything sketchy like a synced folder (Dropbox/iCloud), a shared/multi-user location, or a place where another process/user could modify/replace it.

How to fix (options):

  • Best: make ~/.openclaw a real directory (not a symlink) and keep permissions tight.
  • If you intentionally want it elsewhere, keep the target directory locked down (owned by you, not group/world writable/readable), and be aware that whoever can modify the target effectively can modify OpenClaw’s brain.

If you tell me what you’re using for the reverse proxy (nginx/Caddy/Tailscale/Docker) and what ~/.openclaw points to, I can suggest the exact trustedProxies value / whether the symlink is actually risky in your setup.

burnt carbon
#
  1. I'm using Docker now. But do I need to fix it if I only use Tailscale on my device and then use control UI via Mac screenshare to the OpenClaw host machine, but not Tailscale directly in OpenClaw?

  2. How about those "grey" notes from the openclaw doctor?

│Memory search is enabled but no embedding provider is configured. │
│ Semantic recall will not work without an embedding provider. │
│ Gateway memory probe for default agent is not ready: No API key found for provider │
│ "openai". Auth store: ~/.openclaw/agents/main/agent/auth-profiles.json │
│ (agentDir: /.openclaw/agents/main/agent). Configure auth for this agent │
│ (openclaw agents add <id>) or copy auth-profiles.json from the main agentDir.

obtuse lionBOT
#
  1. Docker + “I only open the Control UI via screenshare on the host” → you can ignore gateway.trusted_proxies_missing.

That warning is only about this situation:

  • You have something in front of the Gateway (nginx/Caddy/Traefik/Tailscale Serve, etc.)
  • That “something” forwards requests to the Gateway and adds headers like X-Forwarded-For
  • OpenClaw needs to know which proxy IPs it’s allowed to trust, otherwise those headers could be spoofed

If you’re literally screen-sharing into the OpenClaw host and opening the UI in a browser on that host (talking to 127.0.0.1), you’re not using a reverse proxy. Nothing to fix.

If later you do put a reverse proxy in front, then you should set gateway.trustedProxies to the proxy’s IP(s) (keep it minimal; don’t trust whole subnets). Docs: https://docs.openclaw.ai/gateway/security#reverse-proxy-configuration


  1. Those grey openclaw doctor notes are not security findings — they’re “feature not configured” hints about semantic memory search.

You’re seeing:

#
  • “Memory search is enabled but no embedding provider is configured”
  • “No API key found for provider ‘openai’ 
 auth-profiles.json 
”

Plain meaning: OpenClaw can do semantic memory recall by generating embeddings over MEMORY.md and memory/*.md. That requires either:

  • a remote embeddings API key (OpenAI/Gemini/Voyage/Mistral), or
  • a local embeddings setup (if you explicitly configure local memory embeddings)

If no embeddings provider key is available, semantic recall stays disabled (the bot still has memory files on disk; it just can’t do vector search over them).

Key nuance (from the FAQ): Codex OAuth does not grant embeddings, so it won’t satisfy memory search even if chat/completions work. Source: https://docs.openclaw.ai/help/faq#does-semantic-memory-search-require-an-openai-api-key

How to “fix” it (pick one):

  • If you want semantic recall: provide an embeddings key (e.g. OPENAI_API_KEY or models.providers.openai.apiKey, or use Gemini/Voyage/Mistral equivalents), and/or explicitly set the memorySearch provider for the agent. Full doc: https://docs.openclaw.ai/concepts/memory
  • If you don’t care about semantic recall: you can safely ignore those grey notes.

If you tell me whether you want semantic memory search (and whether you prefer local vs remote), I can point you to the minimal config shape for your exact preference.

burnt carbon
#

I‘d like to have semantic recall, and I have Qdrant running in Docker. I'd like to make sure the recall can cover multi-language (at least two languages) and two users.

obtuse lionBOT
#

If your goal is OpenClaw’s built-in semantic recall (the memory_search tool over MEMORY.md + memory/**/*.md):

0) Important: Qdrant isn’t the backend OpenClaw uses for memory search (today)

OpenClaw’s memory search index is per-agent SQLite on disk (default ~/.openclaw/memory/<agentId>.sqlite), optionally accelerated by sqlite-vec. There isn’t a “Qdrant memory backend” config knob right now.

So: you can keep Qdrant running (it’s great for your own projects/skills), but it won’t automatically become OpenClaw’s recall store unless you build a custom tool/skill around it.

Docs:

If you want native Qdrant support, that’s a feature request → https://github.com/openclaw/openclaw/issues


1) Multilingual recall: it’s mostly about the embedding provider/model

Memory search works by embedding chunks of your markdown and doing similarity search. For “two languages”, you need an embedding setup that supports multilingual / cross-lingual similarity.

#

In config terms that means:

  • set agents.defaults.memorySearch.provider to a provider that supports your languages, and
  • provide its API key (or use provider: "local" with a multilingual local embedding model).

OpenClaw supports these providers for memory embeddings: openai | gemini | voyage | mistral | local. (This is specifically for embeddings, separate from your chat model.)

Docs:


2) Two users: you almost certainly want two agents (true isolation)

OpenClaw’s clean “two users” story is multi-agent routing:

  • each user gets their own agentId
  • each agent has its own workspace (separate MEMORY.md, memory/
)
#
  • each agent gets its own memory index sqlite file automatically
  • you route inbound messages to the right agent with bindings

Docs: https://docs.openclaw.ai/concepts/multi-agent

Minimal shape (JSON5)

(Example shown with Telegram DMs — same idea works for WhatsApp/etc; the peer.id value is platform-specific.)

{
  agents: {
    list: [
      { id: "userA", workspace: "/data/ws-userA" },
      { id: "userB", workspace: "/data/ws-userB" },
    ],
    defaults: {
#
      memorySearch: {
        enabled: true,
        provider: "openai", // or gemini/voyage/mistral/local
        // model: optional; pick one that supports your languages (or leave default)
        sync: { watch: true, onSessionStart: true, onSearch: true },
      },
    },
  },

  bindings: [
    { agentId: "userA", match: { channel: "telegram", peer: { kind: "direct", id: "123456" } } },
    { agentId: "userB", match: { channel: "telegram", peer: { kind: "direct", id: "987654" } } },
  ],
}
#

Then verify routing with:

openclaw agents list --bindings

3) Docker gotcha: make sure state + workspaces are on persistent volumes

For semantic recall to “stick”, you need persistent storage for:

  • state dir (~/.openclaw → contains the memory sqlite index)
  • each agent workspace (contains MEMORY.md + memory/
)

If those live on ephemeral container FS, you’ll “lose recall” every restart.


Quick questions so I can point you at the exact binding you need

#
  1. What channel are the two users talking through (Telegram? WhatsApp? Discord? only the Control UI)?
  2. Which two languages?
  3. Do you want strict separation (userA can never recall userB), or a shared family brain with two people contributing?
burnt carbon
#
  1. Telegram. I now have two users use one TG bot but using bining to route it to different agents.
    2)English and Chinese, would prefer a local embedding model that can run on a Mac mini.
  2. I want both. I wanted to have a shared brain and each has their own separate memory link to per user.
obtuse lionBOT
#

Got it — Telegram, English+Chinese, local embeddings on a Mac mini, and you want shared + private recall.

A) Two users on one Telegram bot (via bindings) — good approach

Routing each DM to a different agent is exactly how you get hard isolation (separate workspace, separate auth store, separate session store, separate memory index).

If you ever need to confirm the right peer.id to put into bindings, the easiest way is:

  1. have each user DM the bot once
  2. run openclaw sessions --json and look for keys like agent:<id>:telegram:dm:<NUMBER>
    Docs: https://docs.openclaw.ai/cli/sessions

Multi-agent routing doc: https://docs.openclaw.ai/concepts/multi-agent


B) “Shared brain + per-user private memory” (recommended layout)

Memory search indexes:

  • the agent’s own MEMORY.md + memory/**/*.md (private per agent/workspace)
#
  • plus any configured agents.defaults.memorySearch.extraPaths (this is how you add shared memory)

So the clean pattern is:

1) Give each user their own agent + workspace

  • Agent A workspace → private memory for user A
  • Agent B workspace → private memory for user B

2) Create one shared folder that is mounted into both workspaces (Docker)

Example idea (paths are illustrative): mount the same host folder into both workspaces at the same relative path, e.g. shared-memory/, and keep shared notes as markdown files there.

Then configure:

{
  agents: {
#
    defaults: {
      memorySearch: {
        enabled: true,
        extraPaths: ["shared-memory"], // relative to each agent's workspace
      },
    },
    list: [
      { id: "userA", workspace: "/data/ws-userA" },
      { id: "userB", workspace: "/data/ws-userB" },
    ],
  },
}

Now:

  • userA agent indexes /data/ws-userA/MEMORY.md + /data/ws-userA/memory/** (private)
#
  • userB agent indexes /data/ws-userB/... (private)
  • both also index their shared-memory/** (shared)

This is supported end-to-end (including memory_get) because extra paths are explicitly allowed by the memory manager.


C) Local embeddings (English + Chinese)

Set the embedding provider to local:

{
  agents: {
    defaults: {
      memorySearch: {
        enabled: true,
#
        provider: "local",
        local: {
          modelPath: "hf:
 or /path/to/model.gguf"
        },
        sync: { watch: true, onSessionStart: true, onSearch: true },
      },
    },
  },
}

Docs: https://docs.openclaw.ai/concepts/memory

For English+Chinese, the key is picking a multilingual embedding model (whatever you choose, make sure it’s described as multilingual / supports CJK). I’m not going to name a specific model as “the one true answer” because this changes fast, but the config knobs above are the ones that matter.

After configuring, validate with:

#
  • openclaw memory status --deep
  • openclaw memory index --agent userA --verbose
  • openclaw memory index --agent userB --verbose

CLI docs: https://docs.openclaw.ai/cli/memory


One practical UX tip (so the bot actually writes to shared memory)

By default, the “store durable memories” behavior tends to write into the agent’s own memory/YYYY-MM-DD.md.

If you want “shared facts” to land in the shared folder too, add a line to both agents’ AGENTS.md / SOUL.md like:

  • “If a memory is useful for both users, also write it to shared-memory/shared.md.”

(Or keep separate shared files like shared-memory/people.md, shared-memory/house.md, etc.)