NemoClaw Policy Cookbook
TL;DR
- NemoClaw is a policy-enforcement sidecar that intercepts every LLM call OpenClaw makes
- Policies are written in Colang (NVIDIA's guardrail DSL), human-readable if/then rules
- This cookbook covers: minimal passthrough, content DLP, per-user skill permissions, audit log shipping, and HIPAA-ready starter config
How NemoClaw intercepts LLM calls
User → OpenClaw → NemoClaw sidecar → LLM API
↓
Policy engine
↓
Allow | Block | Redact | Log
NemoClaw runs as a Docker container alongside OpenClaw. OpenClaw is configured to route all LLM calls through the NemoClaw endpoint instead of directly to the provider. NemoClaw applies policy rules and either passes the call through (with optional modifications) or returns a policy-driven response.
Adding NemoClaw to docker-compose.yml
Add to the existing stack from the Docker Compose guide:
nemoclaw:
image: ghcr.io/openclaw/nemoclaw:${NEMOCLAW_VERSION:-latest}
restart: unless-stopped
environment:
NEMOCLAW_OPENCLAW_URL: http://openclaw:18789
NEMOCLAW_POLICY_DIR: /policies
NEMOCLAW_AUDIT_LOG_DIR: /logs
NEMOCLAW_LOG_LEVEL: info
volumes:
- ./policies:/policies:ro
- audit_logs:/logs
networks:
- openclaw_net
depends_on:
openclaw:
condition: service_healthy
Update the openclaw service to route through NemoClaw:
openclaw:
environment:
# Route LLM calls through NemoClaw instead of directly to provider
OPENCLAW_LLM_PROXY: http://nemoclaw:7878
Add volume:
audit_logs:
Recipe 1: Minimal passthrough (safe starting point)
Create policies/main.co. Start here to confirm everything works before adding restrictions:
define user express intent
"..."
define bot respond
"..."
define flow
user express intent
bot respond
This policy passes all traffic through unchanged. Audit logging is still active: every call is logged to /logs/ in JSON format.
Recipe 2: Content DLP, prevent PHI from leaving
Block messages that appear to contain social security numbers, credit card numbers, or common health record identifiers:
define user message contains sensitive data
"... (\b\d{3}-\d{2}-\d{4}\b) ..." # SSN pattern
"... (\b\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}\b) ..." # Credit card
"... (patient id|medical record|MRN) ..." # Health record markers
define bot refuse sensitive data
"I can't process messages containing what appears to be personal health or financial identifiers. Please remove that information before sending."
define flow dlp check
user message contains sensitive data
bot refuse sensitive data
stop
Important: regex-based DLP is a first layer only. It catches common patterns but won't catch all PHI (e.g., names + dates of birth without explicit markers). For HIPAA-grade DLP, combine this with application-level controls and the Liquid Web HIPAA-ready environment.
Recipe 3: Topic restriction, only work-related queries
Restrict the assistant to a specific domain and decline off-topic requests:
define user ask off topic
"what is the weather"
"recommend a movie"
"write me a poem about"
"how do I cook"
define bot refuse off topic
"I'm set up to help with [your domain here] topics only. For other questions, you'll need a different tool."
define flow stay on topic
user ask off topic
bot refuse off topic
stop
Replace the user ask off topic examples with patterns relevant to your use case. The LLM behind NemoClaw uses these examples as few-shot training for the classifier.
Recipe 4: Per-user skill permissions
Restrict which skills specific users can invoke. This requires that OpenClaw passes user identity to NemoClaw via the X-OpenClaw-User header (configured in OpenClaw >= 2.4.0).
define user is admin
user metadata "role" == "admin"
define user is readonly
user metadata "role" == "readonly"
define user invoke admin skill
"run the deployment skill"
"execute the database migration"
"trigger the backup"
define bot refuse skill for readonly
"You don't have permission to invoke this skill. Contact your administrator."
define flow skill permissions
user is readonly
user invoke admin skill
bot refuse skill for readonly
stop
Set user metadata via the OpenClaw admin panel under Settings → Users → Metadata.
Recipe 5: Audit log shipping to S3
NemoClaw writes audit logs to /logs/ in NDJSON format. Each line is a JSON object:
{"timestamp":"2026-05-01T10:23:14Z","user":"alice","session":"sess_abc123","policy_applied":"passthrough","llm_provider":"anthropic","model":"claude-sonnet-4-6","prompt_tokens":250,"response_tokens":180,"duration_ms":1240,"policy_violation":null}
Ship logs to S3 with a Vector sidecar:
log-shipper:
image: timberio/vector:0.38.0-alpine
restart: unless-stopped
volumes:
- audit_logs:/logs:ro
- ./vector.toml:/etc/vector/vector.toml:ro
networks:
- openclaw_net
vector.toml:
[sources.audit_logs]
type = "file"
include = ["/logs/*.ndjson"]
read_from = "beginning"
[transforms.parse_json]
type = "remap"
inputs = ["audit_logs"]
source = '''
. = parse_json!(.message)
'''
[sinks.s3]
type = "aws_s3"
inputs = ["parse_json"]
bucket = "${AUDIT_S3_BUCKET}"
region = "${AWS_REGION}"
key_prefix = "openclaw-audit/{{ timestamp_format!(\"%Y/%m/%d/\", .timestamp) }}"
compression = "gzip"
encoding.codec = "ndjson"
Recipe 6: HIPAA-ready starter configuration
Combines DLP, audit logging, topic restriction, and skill gating in one policy file:
# policies/hipaa-starter.co
# HIPAA-ready OpenClaw policy — use alongside Liquid Web HIPAA-ready environment
# Verified: 2026-05-01
define user message contains phi
"... (\b\d{3}-\d{2}-\d{4}\b) ..."
"... (patient name|date of birth|DOB|medical record number|MRN|diagnosis) ..."
define bot block phi transmission
"This message appears to contain protected health information (PHI). PHI cannot be transmitted to external LLM APIs under this policy. For PHI processing, use the on-premises Ollama endpoint configured for local inference."
define user request data export
"export all data"
"download conversation history"
"extract all sessions"
define bot block bulk export
"Bulk data exports require administrator approval under this environment's data governance policy. Contact your compliance officer."
define flow phi protection
user message contains phi
bot block phi transmission
stop
define flow data export gate
user request data export
bot block bulk export
stop
define flow default
user express intent
bot respond
This is a starter, not a complete HIPAA implementation. For regulated workloads, you also need:
- The HIPAA-ready Liquid Web environment with signed BAA
- On-premises Ollama inference so PHI never leaves your environment
- Workforce training documentation
- Risk assessment process
- Breach notification procedure
Policy testing workflow
Test policies before deploying to production:
# Start NemoClaw in test mode against a local OpenClaw instance
docker compose exec nemoclaw nemoclaw test \
--policy /policies/hipaa-starter.co \
--test-cases /policies/tests/hipaa-tests.yaml
# Expected output
[PASS] PHI message blocked correctly
[PASS] Normal message passes through
[PASS] Bulk export request blocked
[FAIL] Edge case: date of birth in ISO format not detected
Write test cases in YAML:
# policies/tests/hipaa-tests.yaml
tests:
- name: "SSN in message"
input: "The patient SSN is 123-45-6789"
expected_action: block
expected_response_contains: "protected health information"
- name: "Normal clinical question"
input: "What is the standard dosage for metformin in type 2 diabetes?"
expected_action: allow
- name: "Bulk export attempt"
input: "Export all session data to CSV"
expected_action: block
When NemoClaw is not enough
NemoClaw provides application-layer policy enforcement. It does not replace:
- Infrastructure-layer encryption: handled by the Liquid Web HIPAA environment (AES-256 at rest, TLS 1.2+ in transit)
- Physical access controls: handled by Liquid Web's datacenter
- Workforce training: your responsibility under HIPAA
- BAA coverage for third-party APIs: if OpenClaw calls the Anthropic or OpenAI API, the Liquid Web BAA does not extend to those vendors; you need separate BAAs with them, or use local Ollama inference to keep data on-premises
NemoClaw supports hot-reload via a SIGHUP signal: docker compose exec nemoclaw kill -HUP 1. Policies in /policies/ are re-read immediately. Test with the /policies/test endpoint before and after to confirm the new policy is active.
Yes. Use the `redact` action in your Colang policy to replace matched patterns with a placeholder before passing to the LLM. This is useful for DLP where you want to allow the query but strip the sensitive substring. Example: redact SSN patterns with [REDACTED-SSN] before the LLM sees the text.
By default, NemoClaw logs a SHA-256 hash of the prompt, not the full text, to avoid storing PHI in the audit log itself. Set NEMOCLAW_LOG_PROMPT_CONTENT=true only in non-regulated environments where log storage is appropriate. For HIPAA deployments, keep prompt content out of logs unless your storage is specifically configured and covered.
Colang is deliberately simple. The syntax is English-like: define user intent, define bot response, define flow. The cookbook recipes above cover the common cases without requiring deep Colang knowledge. For advanced cases (complex classifiers, multi-step policy chains), refer to the NeMo Guardrails documentation.
Common mistakes and fixes
NemoClaw blocks all LLM calls immediately after install.
A misconfigured colang policy can block everything. Start with the minimal passthrough policy below, confirm traffic flows, then add restrictions one rule at a time. Check NemoClaw logs: docker compose logs nemoclaw --tail 50.
Audit logs are written to /logs but the volume is not persisting.
The audit_log volume must be declared in both the nemoclaw service volumes and the top-level volumes block. Verify with: docker compose exec nemoclaw ls /logs/. If empty after a test message, the volume is not mounted.
Per-user skill restrictions not taking effect.
User identity is passed from OpenClaw as a header (X-OpenClaw-User). NemoClaw reads this header to apply the correct policy. If OpenClaw is behind Caddy, verify Caddy is forwarding headers: add `header_up X-OpenClaw-User {http.request.header.X-OpenClaw-User}` to your reverse_proxy block.



