identity-resolver
**Canonical user identity resolution across messaging channels**
by bowen31337 · published 2026-03-22
$ claw add gh:bowen31337/bowen31337-identity-resolver# identity-resolver
**Canonical user identity resolution across messaging channels**
Description
Resolves multi-channel user identities (Telegram, WhatsApp, Discord, web, etc.) to canonical user IDs, preventing state fragmentation when users interact via multiple channels.
**Problem it solves:** Without identity resolution, a user messaging via Telegram and WhatsApp appears as two different users, causing fragmented memory, access control, and per-user state across skills.
**Solution:** Maps all channel identities to one canonical user ID automatically.
Installation
**Prerequisites:** Install `uv` if not already installed:
curl -LsSf https://astral.sh/uv/install.sh | sh**Install the skill:**
cd /path/to/openclaw/workspace
# Via ClawHub (recommended)
clawhub install identity-resolver
# Or via Git
git clone https://github.com/clawinfra/identity-resolver skills/identity-resolverQuick Start
For End Users
# Initialize identity map (auto-detects owner from USER.md)
cd /path/to/workspace
uv run python skills/identity-resolver/scripts/identity_cli.py init
# Verify your identity
uv run python skills/identity-resolver/scripts/identity_cli.py resolve \
--channel telegram --user-id YOUR_TELEGRAM_ID
# Output: your-canonical-id
# List all registered identities
uv run python skills/identity-resolver/scripts/identity_cli.py listFor Skill Developers
Add to your skill's Python code:
import sys
from pathlib import Path
# Import identity resolver
sys.path.insert(0, str(Path.cwd() / "skills" / "identity-resolver" / "scripts"))
from identity import resolve_canonical_id
# Get canonical user ID from session context
import os
channel = os.getenv("OPENCLAW_CHANNEL") # e.g., "telegram"
user_id = os.getenv("OPENCLAW_USER_ID") # e.g., "123456789"
canonical_id = resolve_canonical_id(channel, user_id)
# Use canonical_id for all user-specific operations
# Example: User-specific memory file
memory_file = f"data/users/{canonical_id}/memory.json"Features
✅ **Auto-registers owner** from workspace USER.md
✅ **Thread-safe** identity map storage with fcntl locking
✅ **CLI + Python API** for both users and developers
✅ **Path traversal protection** — sanitizes all canonical IDs
✅ **Zero dependencies** — pure Python stdlib
✅ **Multi-channel support** — Telegram, WhatsApp, Discord, web, and future channels
Use Cases
Multi-User Memory Systems
# tiered-memory skill integration
canonical_id = resolve_canonical_id(channel, user_id)
memory_tree = f"memory/users/{canonical_id}/tree.json"Access Control
# agent-access-control skill integration
canonical_id = resolve_canonical_id(channel, user_id)
if is_owner(canonical_id):
# Full access
else:
# Limited accessCross-Platform User Tracking
# Same user across Discord + Telegram
discord_id = resolve_canonical_id("discord", "user#1234")
telegram_id = resolve_canonical_id("telegram", "987654321")
# Both resolve to same canonical ID if registeredAPI Reference
Core Functions
**`resolve_canonical_id(channel, provider_user_id, workspace=None, owner_numbers=None) -> str`**
Resolve channel identity to canonical user ID.
**`add_channel(canonical_id, channel, provider_user_id, workspace=None, display_name=None)`**
Add channel mapping to a canonical user (creates user if doesn't exist).
**`remove_channel(canonical_id, channel, provider_user_id, workspace=None)`**
Remove channel mapping from canonical user.
**`list_identities(workspace=None) -> dict`**
Return all identity mappings.
**`get_channels(canonical_id, workspace=None) -> list`**
Get all channels for a canonical user.
**`is_owner(canonical_id, workspace=None) -> bool`**
Check if canonical ID is the owner.
CLI Commands
# Initialize
identity init [--force]
# Resolve (auto-detect from env or explicit params)
identity resolve [--channel CH] [--user-id ID]
# Add mapping
identity add --canonical ID --channel CH --user-id ID [--display-name NAME]
# Remove mapping
identity remove --canonical ID --channel CH --user-id ID
# List all
identity list [--json]
# Get channels
identity channels --canonical ID [--json]
# Check owner
identity is-owner --canonical ID [--json]Identity Map Format
Location: `data/identity-map.json` or `memory/identity-map.json`
{
"version": "1.0",
"identities": {
{
"version": "1.0",
"identities": {
"alice": {
"canonical_id": "alice",
"is_owner": true,
"display_name": "Alice Johnson",
"channels": [
"telegram:123456789",
"whatsapp:+1234567890",
"whatsapp:+9876543210",
"whatsapp:+5555555555"
],
"created_at": "2026-01-15T10:00:00Z",
"updated_at": "2026-01-15T10:05:00Z"
},
"bob": {
"canonical_id": "bob",
"is_owner": false,
"display_name": "Bob Smith",
"channels": [
"discord:bob#1234",
"telegram:987654321"
],
"created_at": "2026-01-15T10:10:00Z",
"updated_at": "2026-01-15T10:10:00Z"
}
}
}
}
}
}Security
Integration Examples
See `docs/TIERED_MEMORY_INTEGRATION_EXAMPLE.md` for complete working example.
License
MIT - See LICENSE file
Author
OpenClaw Agent <agent@openclaw.local>
Links
More tools from the same signal band
Order food/drinks (点餐) on an Android device paired as an OpenClaw node. Uses in-app menu and cart; add goods, view cart, submit order (demo, no real payment).
Sign plugins, rotate agent credentials without losing identity, and publicly attest to plugin behavior with verifiable claims and authenticated transfers.
The philosophical layer for AI agents. Maps behavior to Spinoza's 48 affects, calculates persistence scores, and generates geometric self-reports. Give your...