Common NemoClaw Integration Policy Examples

View as Markdown

Use these examples when a sandbox is already installed and an integration needs network access. This page covers only integrations that NemoClaw currently ships as maintained policy preset YAML under nemoclaw-blueprint/policies/presets/. For complete blueprint examples that combine a model, agent harness, OpenShell policy, and integration workflow, refer to Community Solutions. Integration setup usually has two separate parts:

  • Configure the integration itself, such as a bot token, OAuth credential, or agent plugin setting.
  • Allow the sandbox to reach the integration’s network endpoints through NemoClaw and OpenShell policy.

Use NemoClaw commands for policy changes that should be tracked with the sandbox. Use OpenShell directly when you need to inspect blocked requests or approve a one-off request in the TUI.

Before You Start

Replace my-assistant with your sandbox name in the examples.

Check the current policy state first:

$nemohermes my-assistant policy-list

For a live view of blocked requests, open the OpenShell TUI in a separate host terminal:

$openshell term

When the agent reaches an endpoint that is not in the policy, the TUI shows the host, port, requesting binary, method, and path when available. Approve a request only when you understand why the integration needs it. An approval updates the running policy, but it does not create a reviewable NemoClaw preset entry that policy-add can replay.

Supported Integration Presets

NemoClaw ships maintained policy presets for common services in nemoclaw-blueprint/policies/presets/. Messaging channel presets are scoped to the sandbox’s active agent; if an agent does not have a matching channel policy, that channel preset is omitted from policy-list and policy-add <preset> reports it as unknown.

WorkflowPreset
Brave Searchbrave
Homebrew packagesbrew
Discord messagingdiscord
GitHub and GitHub APIgithub
Gmail IMAP and SMTPgmail
Hugging Face Hub and Inference APIhuggingface
Jira and Atlassian Cloudjira
Local Ollama or vLLM through the host gatewaylocal-inference
OpenClaw model-pricing reference fetchopenclaw-pricing
npm and Yarn packagesnpm
Microsoft 365, Outlook, and Graph APIoutlook
Public reference APIspublic-reference
Python Package Indexpypi
Slack messagingslack
Tavily Searchtavily
Telegram Bot APItelegram
Weather and geocoding APIsweather
WeChat (personal) iLink Bot API (experimental)wechat
WhatsApp Web messaging (experimental)whatsapp

Preview the endpoints before applying:

$nemohermes my-assistant policy-add outlook --dry-run

Apply the preset:

$nemohermes my-assistant policy-add outlook --yes

Remove it later if the sandbox no longer needs that access:

$nemohermes my-assistant policy-remove outlook --yes

Email and Calendar With Microsoft 365

Use the outlook preset for Microsoft 365 email and calendar workflows that use Microsoft Graph or Outlook endpoints. The preset allows access to graph.microsoft.com, Microsoft login, and Outlook service endpoints.

$nemohermes my-assistant policy-add outlook --dry-run
$nemohermes my-assistant policy-add outlook --yes

policy-add exits non-zero if the outlook preset is already applied. If you applied it in the lifecycle example above, run nemohermes my-assistant policy-remove outlook --yes first (or skip this apply step).

Then configure the email or calendar tool credentials through the integration you run in the sandbox. Keep OAuth client secrets and refresh tokens out of policy files.

If the tool still fails, run openshell term, trigger the workflow again, and inspect the blocked request. If the blocked endpoint is not covered by the maintained outlook preset, treat it as a separate policy review instead of assuming it is part of the supported preset.

Telegram Bot Messaging

Telegram needs channel configuration and egress policy. If you already enabled Telegram during onboarding but did not include the preset, add it to the running sandbox:

$nemohermes my-assistant policy-add telegram --yes

To add Telegram after onboarding, set the token on the host, add the channel, rebuild so the image picks up the channel config, and apply the policy preset:

$export TELEGRAM_BOT_TOKEN=<your-bot-token>
$NEMOCLAW_NON_INTERACTIVE=1 nemohermes my-assistant channels add telegram
$nemohermes my-assistant rebuild
$nemohermes my-assistant policy-add telegram --yes

If delivery fails, open the TUI and send a test message to the bot:

$openshell term

The matching preset for each supported messaging channel is the channel name (telegram, discord, slack, wechat, or whatsapp).

Slack or Discord Messaging

Slack and Discord also need channel configuration and egress policy. Use the matching policy preset after you configure the channel credentials.

For Slack:

$export SLACK_BOT_TOKEN=<your-slack-bot-token>
$export SLACK_APP_TOKEN=<your-slack-app-token>
$NEMOCLAW_NON_INTERACTIVE=1 nemohermes my-assistant channels add slack
$nemohermes my-assistant rebuild
$nemohermes my-assistant policy-add slack --yes

For Discord:

$export DISCORD_BOT_TOKEN=<your-discord-bot-token>
$export DISCORD_SERVER_ID=<your-discord-server-id>
$NEMOCLAW_NON_INTERACTIVE=1 nemohermes my-assistant channels add discord
$nemohermes my-assistant rebuild
$nemohermes my-assistant policy-add discord --yes

If you enabled Slack or Discord during onboarding, apply only the matching preset:

$nemohermes my-assistant policy-add slack --yes
$nemohermes my-assistant policy-add discord --yes

WeChat or WhatsApp Messaging (Experimental)

WeChat and WhatsApp are experimental. Both rely on QR-based pairing flows that are more fragile than token-based bots. The upstream client libraries can change behavior without notice.

WeChat uses Tencent’s iLink Bot API for personal accounts. A host-side QR scan captures the bot token during onboarding rather than requiring you to paste it from a developer portal. Add the channel interactively and apply the preset:

$nemohermes my-assistant channels add wechat
$nemohermes my-assistant rebuild
$nemohermes my-assistant policy-add wechat --yes

WhatsApp Web pairs entirely inside the sandbox through QR scan, so channels add does not collect a host-side token. Apply the preset and complete the in-sandbox pairing after the rebuild:

$NEMOCLAW_NON_INTERACTIVE=1 nemohermes my-assistant channels add whatsapp
$nemohermes my-assistant rebuild
$nemohermes my-assistant policy-add whatsapp --yes

If you enabled WeChat or WhatsApp during onboarding, apply only the matching preset:

$nemohermes my-assistant policy-add wechat --yes
$nemohermes my-assistant policy-add whatsapp --yes

GitHub and Jira

Use github when the agent needs GitHub API access or Git access. Use jira when the agent needs Atlassian Jira access.

Preview first:

$nemohermes my-assistant policy-add github --dry-run
$nemohermes my-assistant policy-add jira --dry-run

Apply the preset that matches the workflow:

$nemohermes my-assistant policy-add github --yes
$nemohermes my-assistant policy-add jira --yes

The jira preset intentionally allows Node.js access to Atlassian Cloud and does not allow curl. When validating it manually, avoid plain curl -s against auth.atlassian.com. Atlassian can return an empty redirect body even when the request succeeds. An empty curl -s output from that endpoint is inconclusive before or after approval. Do not use it as a pass/fail signal. Use a body-visible API probe instead:

$node -e "require('https').get('https://api.atlassian.com', r => console.log(r.statusCode))"
$curl -sS --max-time 10 -w '\n%{http_code}\n' https://api.atlassian.com/oauth/token/accessible-resources

Before approval, the curl probe should report 000 or a local policy denial. After explicitly approving curl for api.atlassian.com in OpenShell, it should return Atlassian’s unauthenticated 401 JSON response. The 401 response is the expected success signal for this manual probe. This manual probe proves curl reached Atlassian, but no Jira credentials were supplied.

Remove access when the task is done:

$nemohermes my-assistant policy-remove github --yes
$nemohermes my-assistant policy-remove jira --yes

Web search requires both the selected provider’s credential and its matching network policy preset. Onboarding suggests brave or tavily only when you selected that provider, including under the Restricted tier. If you unselected or removed the matching preset, preview and add it before using web search.

Hermes supports Tavily Search through NemoClaw onboarding and does not support Brave Search. Apply the tavily preset if it is missing.

$nemohermes my-assistant policy-add tavily --dry-run
$nemohermes my-assistant policy-add tavily --yes

Rerun onboarding when you enable or disable Tavily because the Hermes backend and OpenShell credential attachment are part of the sandbox image. Configure TAVILY_API_KEY during that onboarding run.

The tavily preset permits only POST /search and POST /extract to api.tavily.com. It enables request-body credential rewriting because Hermes sends its resolver placeholder in the JSON api_key field. OpenShell replaces that placeholder at egress, so the raw key is not written into the sandbox configuration.

Weather and Public Reference Lookups

Use the weather preset when the agent needs read-only weather or geocoding lookups. The Open tier includes it by default. Balanced and Restricted sandboxes require the explicit policy-add weather step below. The preset covers read-only public weather and geocoding lookups, including current conditions, forecasts, geocoding, and weather alerts, without enabling messaging or productivity APIs.

$nemohermes my-assistant policy-add weather --dry-run
$nemohermes my-assistant policy-add weather --yes

Use the public-reference preset when the agent needs read-only public reference APIs, such as Wikipedia, Wikidata, Wikimedia Commons, Nominatim, or country metadata. The Open tier includes this preset by default.

$nemohermes my-assistant policy-add public-reference --dry-run
$nemohermes my-assistant policy-add public-reference --yes

Package and Model Tooling

Use these presets when an agent workflow needs to install packages or download model assets:

WorkflowPreset
npm or Yarn packagesnpm
Python packages from PyPI with pip, Python, or uvpypi
Homebrew packagesbrew
Hugging Face model or dataset accesshuggingface

Add only the preset required for the task:

$nemohermes my-assistant policy-add npm --yes
$nemohermes my-assistant policy-add pypi --yes
$nemohermes my-assistant policy-add brew --yes
$nemohermes my-assistant policy-add huggingface --yes

Remove package access after a one-time setup task if the sandbox no longer needs it:

$nemohermes my-assistant policy-remove npm --yes
$nemohermes my-assistant policy-remove pypi --yes
$nemohermes my-assistant policy-remove brew --yes
$nemohermes my-assistant policy-remove huggingface --yes

The pypi preset allows Python, pip, virtual-environment Python and pip, and /usr/local/bin/uv to reach PyPI endpoints. If uv is installed somewhere else in the sandbox, add a custom preset for that binary path instead of broadening the maintained preset locally.

Homebrew Specifics

The sandbox base image includes Homebrew (Linuxbrew), so apply the brew preset before installing a formula. A /usr/local/bin/brew wrapper puts the entry point on the sandbox PATH while delegating to the Linuxbrew prefix. Installed formula commands are available from the Linuxbrew bin directory in sandbox shell sessions:

$nemohermes my-assistant policy-add brew --yes
$nemohermes my-assistant exec -- brew install <formula>
$nemohermes my-assistant exec -- bash -lc '<formula-command>'

You do not need to bootstrap Homebrew, install build dependencies, or source brew shellenv inside the sandbox.

Model Pricing

Hermes does not use OpenClaw’s model-pricing reference fetch.

Local Inference

Use local-inference when the sandbox needs access to host-side local inference services such as Ollama or vLLM through the OpenShell host gateway. Onboarding auto-suggests this preset when you choose a local provider. If you need to add it after onboarding:

$nemohermes my-assistant policy-add local-inference --dry-run
$nemohermes my-assistant policy-add local-inference --yes

Then verify the sandbox status:

$nemohermes my-assistant status

Gmail With an App Password

Use the gmail preset when a Python script needs to receive mail through IMAP or send mail through SMTP with a Gmail App Password. The preset allows only /usr/bin/python3 to open raw TLS connections to imap.gmail.com:993 and smtp.gmail.com:465. OpenShell enforces the exact hosts, ports, and binary, but it cannot inspect individual IMAP or SMTP commands inside the encrypted connections. This preset does not grant Gmail REST API, Google OAuth, or service-account endpoints.

Google recommends App Passwords only for clients that cannot use Sign in with Google. This workflow stores the App Password inside the sandbox, where the agent can read it while the file exists. Create an App Password only for this workflow, delete the uploaded file, and revoke the App Password after the task.

Prerequisites

Prepare the Google Account before applying the preset:

  • Turn on 2-Step Verification for the Google Account.
  • Create an App Password for the sandbox workflow.
  • Confirm that the account or Google Workspace administrator permits IMAP and App Passwords.

Google documents the TLS endpoints and ports in IMAP, POP, and SMTP.

Apply the Preset

Preview and apply the preset from the host:

$nemohermes my-assistant policy-add gmail --dry-run
$nemohermes my-assistant policy-add gmail --yes

Upload the App Password

Create gmail_config.json on the host outside any source checkout:

1{
2 "email": "your-address@gmail.com",
3 "app_password": "<16-character-app-password>"
4}

Restrict the host file, prepare a sandbox directory, upload the file, and restrict the uploaded copy:

$chmod 600 /path/to/gmail_config.json
$nemohermes my-assistant exec -- mkdir -p /sandbox/hand
$nemohermes my-assistant upload /path/to/gmail_config.json /sandbox/hand/gmail_config.json
$nemohermes my-assistant exec -- chmod 600 /sandbox/hand/gmail_config.json

Do not commit gmail_config.json or paste its contents into chat, logs, issues, or pull requests.

Download Recent Attachments

Save this standard-library example as download_attachments.py on the host. It reads the 10 most recent messages without marking them as read and writes attachments under /sandbox/hand/gmail_attachments:

1import imaplib
2import json
3from email import policy
4from email.parser import BytesParser
5from pathlib import Path
6
7ROOT = Path("/sandbox/hand")
8CONFIG = json.loads((ROOT / "gmail_config.json").read_text(encoding="utf-8"))
9ATTACHMENTS = ROOT / "gmail_attachments"
10ATTACHMENTS.mkdir(mode=0o700, parents=True, exist_ok=True)
11ATTACHMENTS.chmod(0o700)
12
13with imaplib.IMAP4_SSL("imap.gmail.com", 993) as mailbox:
14 mailbox.login(CONFIG["email"], CONFIG["app_password"])
15 status, _ = mailbox.select("INBOX", readonly=True)
16 if status != "OK":
17 raise RuntimeError("Could not select the Gmail inbox")
18
19 status, search_data = mailbox.search(None, "ALL")
20 if status != "OK":
21 raise RuntimeError("Could not search the Gmail inbox")
22
23 for message_id in search_data[0].split()[-10:]:
24 status, message_data = mailbox.fetch(message_id, "(BODY.PEEK[])")
25 if status != "OK":
26 continue
27 raw_message = next(
28 (entry[1] for entry in message_data if isinstance(entry, tuple)),
29 None,
30 )
31 if raw_message is None:
32 continue
33
34 message = BytesParser(policy=policy.default).parsebytes(raw_message)
35 for part in message.walk():
36 filename = part.get_filename()
37 payload = part.get_payload(decode=True)
38 if part.get_content_disposition() != "attachment" or not filename or payload is None:
39 continue
40 safe_name = Path(filename.replace("\\", "/")).name
41 destination = ATTACHMENTS / f"{message_id.decode()}-{safe_name}"
42 destination.write_bytes(payload)
43 destination.chmod(0o600)
44 print(destination)

Upload and run the script, then copy the attachment directory back to the host:

$nemohermes my-assistant upload ./download_attachments.py /sandbox/hand/download_attachments.py
$nemohermes my-assistant exec -- python3 /sandbox/hand/download_attachments.py
$nemohermes my-assistant download /sandbox/hand/gmail_attachments/ ./gmail_attachments/

Treat every downloaded attachment as untrusted content. Do not execute an attachment in the sandbox or on the host without reviewing it.

Send a Test Message

Save this standard-library example as send_email.py on the host. The example sends a test message back to the configured account:

1import json
2import smtplib
3from email.message import EmailMessage
4from pathlib import Path
5
6config = json.loads(
7 Path("/sandbox/hand/gmail_config.json").read_text(encoding="utf-8")
8)
9message = EmailMessage()
10message["From"] = config["email"]
11message["To"] = config["email"]
12message["Subject"] = "NemoClaw Gmail policy test"
13message.set_content("Sent through the NemoClaw Gmail policy preset.")
14
15with smtplib.SMTP_SSL("smtp.gmail.com", 465, timeout=30) as smtp:
16 smtp.login(config["email"], config["app_password"])
17 smtp.send_message(message)

Upload and run the script:

$nemohermes my-assistant upload ./send_email.py /sandbox/hand/send_email.py
$nemohermes my-assistant exec -- python3 /sandbox/hand/send_email.py

Remove Credentials and Access

Delete the host and uploaded credential files, along with the temporary sandbox files, after downloading any attachments you need:

$rm -f /path/to/gmail_config.json
$nemohermes my-assistant exec -- rm -f /sandbox/hand/gmail_config.json
$nemohermes my-assistant exec -- rm -f /sandbox/hand/download_attachments.py /sandbox/hand/send_email.py
$nemohermes my-assistant exec -- rm -rf /sandbox/hand/gmail_attachments
$nemohermes my-assistant policy-remove gmail --yes

Removing the preset does not delete uploaded files or revoke the App Password. Revoke the dedicated App Password in your Google Account when the sandbox no longer needs it.

Inspect or Replace the Live Policy

Use policy-list for normal preset state:

$nemohermes my-assistant policy-list

Use the NemoClaw policy export when you need an editable copy of the round-trippable base policy. Requires OpenShell 0.0.72+ for the round-trippable policy get --base and policy set --wait syntax.

$nemohermes my-assistant policy-get > current-policy.yaml

The export strips OpenShell metadata and exits non-zero if the base policy cannot be retrieved or validated. Do not add --raw when you plan to edit and reapply the file.

If you must replace the live policy, edit the policy file and apply it back to the sandbox:

$openshell policy set --policy current-policy.yaml --wait my-assistant

openshell policy set replaces the live policy with the file you provide. It does not accept a preset file that starts with a preset: block, and it does not merge a single endpoint into the existing policy. Use nemohermes my-assistant policy-add for maintained NemoClaw presets.

Next Steps