Tlon/Urbit channel plugin for OpenClaw. Enables your AI agent to communicate via Tlon DMs and group channels.
- DMs: Receive and respond to direct messages
- Group Channels: Participate in group chats (mention-triggered or open mode)
- Thread Replies: Support for threaded conversations
- Rich Content: Images, links, and formatted text
- Ship Authorization: Allowlist ships for DM access
- Channel Authorization: Per-channel ship allowlists with open/restricted modes
- Approval System: Approve/deny new DMs, channel mentions, and group invites via DM
- Settings Store: Hot-reload config via Urbit settings-store (no restart needed)
- Auto-Discovery: Automatically monitors all channels in joined groups
- Cite Resolution: Parse and fetch quoted message content
- Optional Telemetry: Explicit PostHog opt-in for hosted analytics
This plugin is included with OpenClaw. Enable it in your config:
channels:
tlon:
enabled: true
ship: "~your-ship"
url: "https://your-ship.tlon.network"
code: "your-access-code"channels:
tlon:
enabled: true
ship: "~your-ship"
url: "https://your-ship.tlon.network"
code: "your-access-code"
# Owner receives approval requests and can manage the bot
ownerShip: "~your-main-ship"
# Ships allowed to DM the bot directly
dmAllowlist:
- "~trusted-friend"
- "~another-ship"
# Auto-accept settings
autoAcceptDmInvites: true # Accept DMs from ships in dmAllowlist
autoAcceptGroupInvites: false # Require approval for group invites
# Channel discovery
autoDiscoverChannels: true # Monitor all channels in joined groups
groupChannels: # Additional channels to monitor explicitly
- "chat/~host-ship/channel-name"
# Per-channel authorization
authorization:
channelRules:
"chat/~host/public-channel":
mode: "open" # Anyone can interact
"chat/~host/private-channel":
mode: "restricted"
allowedShips:
- "~specific-ship"
# Ships authorized by default for restricted channels
defaultAuthorizedShips:
- "~always-allowed"
# Show model info in responses
showModelSignature: false
# Optional PostHog telemetry. Disabled unless explicitly enabled.
telemetry:
enabled: true
apiKey: "phc_your_project_api_key"
host: "https://us.i.posthog.com"Telemetry is disabled by default. The plugin only sends tlemetry events when channels.tlon.telemetry.enabled
is set to true and an API key is configured.
When enabled, the plugin captures a single TlonBot Reply Handled event each time it enters the OpenClaw reply
flow. The event summarizes OpenClaw usage (tools used, character count, etc.), but does not log message content.
The plugin does not enable telemetry automatically just because an API key is present. enabled: true is
required so open-source installs do not phone home by default.
The approval system lets you control who can interact with your bot. When ownerShip is configured, you'll receive DM notifications for:
- DM requests from ships not on your
dmAllowlist - Channel mentions from ships not authorized for that channel
- Group invites (if
autoAcceptGroupInvitesis false)
When someone not on the allowlist tries to interact, you'll receive a DM like:
New DM request from ~sampel-palnet:
"Hello, I'd like to chat with your bot..."
Reply "approve", "deny", or "block" (ID: dm-1234567890-abc)
- approve: Allow the interaction and add to allowlist. Original message is processed.
- deny: Reject silently. Ship can try again later.
- block: Permanently block using Tlon's native blocking.
The owner can send these commands via DM:
| Command | Description |
|---|---|
blocked |
List all blocked ships |
pending |
List pending approval requests |
unblock ~ship |
Unblock a ship |
This plugin bundles @tloncorp/tlon-skill which provides CLI commands for:
- Contacts and profile management
- Channel listing and history
- Group administration
- Message posting and reactions
- Settings management
The skill is automatically available to your agent. For standalone usage, see the tlon-skill repo.
Full documentation: https://docs.openclaw.ai/channels/tlon
- Docker
- GitHub CLI (
gh) authenticated with tloncorp access - A local Urbit ship (e.g., on
http://localhost:8080) - Anthropic API key (or OpenRouter for alternative models)
# 1. Clone this repo
gh repo clone tloncorp/openclaw-tlon
cd openclaw-tlon
# 2. Run setup (clones tlonbot + tlon-apps, creates .env)
./dev/setup.sh
# 3. Edit .env with your credentials
# - ANTHROPIC_API_KEY or OPENROUTER_API_KEY
# - TLON_URL: http://host.docker.internal:<port> (not localhost!)
# - TLON_SHIP, TLON_CODE
# - TLON_DM_ALLOWLIST, TLON_OWNER_SHIP
# 4. Start dev environment (loads OPENCLAW_GATEWAY_PORT from root .env)
docker compose --env-file .env -f dev/docker-compose.yml up --build
# 5. Access OpenClaw at http://localhost:${OPENCLAW_GATEWAY_PORT:-18789}parent/
├── tlonbot/ # Bot prompts + image-search extension (optional)
├── tlon-apps/ # Source repo for dev-only local @tloncorp/api overrides
└── openclaw-tlon/ # This repo
@tloncorp/api and @tloncorp/tlon-skill are installed via npm for normal installs. During Docker dev, the entrypoint will also link a local @tloncorp/api override from ../tlon-apps/packages/api when that sibling repo has been built.
The dev override uses the real tlon-apps/packages/api package, similar to the old api-beta workflow. You still rebuild tlon-apps separately so its dist/ stays current, and the container startup will link that local package instead of using the published npm copy.
If you want to modify @tloncorp/api locally while working in this repo, first link the sibling package into openclaw-tlon so your editor and local TypeScript resolve against ../tlon-apps/packages/api:
pnpm dev:api:linkThat makes openclaw-tlon resolve @tloncorp/api to ../tlon-apps/packages/api on your machine. After changing the API surface, rebuild it in tlon-apps:
pnpm --dir ../tlon-apps --filter @tloncorp/api buildTo switch back to the published npm package on your host:
pnpm dev:api:unlink- Edit code in this repo or
../tlon-apps/packages/api - If you changed
tlon-apps, rebuild the API package there first:pnpm --dir ../tlon-apps --filter @tloncorp/api build - Restart container:
docker compose --env-file .env -f dev/docker-compose.yml up --build - For faster iteration, run OpenClaw directly on host with npm link
pnpm test # Run unit tests
pnpm test:watch # Watch mode
pnpm test:security # Security tests onlyIntegration tests spin up ephemeral fakezod ships (~zod, ~ten, ~mug) in Docker, boot an OpenClaw gateway with the plugin, and run end-to-end scenarios.
Runs everything in Docker — ships + gateway + tests. Only needs an LLM API key.
1. Create .env with at minimum:
# Required: at least one LLM provider key
OPENROUTER_API_KEY=sk-or-...
# Optional: override the default model (default: openrouter/minimax/minimax-m2.5)
# MODEL=anthropic/claude-sonnet-4-5That's it. The test harness handles ship credentials, config, and cleanup automatically.
2. Run:
pnpm test:integrationThis will:
- Start 3 fakezod ships (~zod as bot, ~ten as test user, ~mug as third party)
- Build and start an OpenClaw container with the plugin
- Wait for ships, gateway, and SSE subscriptions
- Run all test cases in
test/cases/ - Tear everything down on exit
Run a specific test:
pnpm test:integration -- test/cases/dm.test.tsFor iterative development — you manage the ships and gateway yourself, tests run directly against them.
1. Run setup (clones sibling repos, creates .env from template):
./dev/setup.shThis clones tlonbot and tlon-apps as sibling directories and creates .env from .env.example if it doesn't exist. @tloncorp/api still installs from npm by default, but the dev container will link a local override from tlon-apps/packages/api so you can test API changes without publishing first.
2. Edit .env:
# Required: LLM provider key
OPENROUTER_API_KEY=sk-or-...
# Bot ship (the ship running the plugin)
TLON_URL=http://host.docker.internal:8080
TLON_SHIP=~zod
TLON_CODE=lidlut-tabwed-pillex-ridrup
# Test user ship (sends DMs to the bot)
TEST_USER_URL=http://host.docker.internal:8081
TEST_USER_SHIP=~ten
TEST_USER_CODE=lapseg-nolmel-riswen-hopryc
# DM allowlist and owner
TLON_DM_ALLOWLIST=~ten
TLON_OWNER_SHIP=~ten
# Gateway port (match your running gateway)
OPENCLAW_GATEWAY_PORT=18789
# Optional: Brave Search API key (enables web_search + image_search tools)
# BRAVE_API_KEY=BSA...
# Optional: telemetry (explicit opt-in only)
# TLON_TELEMETRY_ENABLED=true
# TLON_TELEMETRY_API_KEY=phc_...
# TLON_TELEMETRY_HOST=https://us.i.posthog.com
# Optional: tlonbot GitHub token (if not using local clone)
# TLONBOT_TOKEN=ghp_...3. Start your dev environment:
pnpm dev # or docker compose --env-file .env -f dev/docker-compose.yml up --build4. Run tests against it:
pnpm test:integration:dev # All tests
pnpm test:integration:dev test/cases/dm.test.ts # Specific test
pnpm test:integration:dev --watch # Watch modeStarts the full Docker environment (ships + gateway) without running vitest, so you can interact with the bot manually — send DMs from ~ten's Landscape, poke around, etc.
pnpm test:manualThis will:
- Start 3 fakezod ships and the OpenClaw gateway in Docker
- Wait for everything to be ready
- Print ship URLs, access codes, and gateway address
- Tail gateway logs (Ctrl+C to stop and tear down)
Ships are accessible via browser:
- ~zod (bot): http://localhost:8080 — code:
lidlut-tabwed-pillex-ridrup - ~ten (user): http://localhost:8081 — code:
lapseg-nolmel-riswen-hopryc - ~mug (3rd party): http://localhost:8082 — code:
ravsut-bolryd-hapsum-pastul
To tear down without attaching to logs:
pnpm test:manual -- --stopVerbose mode: To see full debug output from the OpenClaw entrypoint (config dumps, prompt files, env vars):
VERBOSE=1 pnpm test:manualTo test the image_search tool and Brave-powered web_search:
- Get a Brave Search API key
- Add to
.env:BRAVE_API_KEY=BSA... - Clone tlonbot as a sibling directory:
cd .. && gh repo clone tloncorp/tlonbot
When ../tlonbot exists, docker-compose.local.yml mounts it automatically, making the image-search plugin available. The entrypoint patches the config to load it if present.
Without these, web_search falls back to whatever provider is available, and image_search returns a "no API key" error — tests that don't depend on image search still pass.
MIT