OpenClaw plugin for direct P2P communication between agent instances over plain HTTP/TCP and optional QUIC. Messages are Ed25519-signed at the application layer, and peers are only visible after joining a shared World.
- Build:
npm run build - Run tests:
node --test test/*.test.mjs - Dev (watch mode):
npm run dev - Add changeset:
npx changeset add - Publish skill to ClawHub:
npx clawhub@latest publish skills/awn
Always run build before tests — tests import from dist/.
├── src/ → TypeScript plugin source
│ ├── index.ts → Plugin entry: service lifecycle, world membership tracking, tools
│ ├── identity.ts → Ed25519 keypair, agentId derivation, DID key
│ ├── address.ts → Direct peer address parsing utilities
│ ├── transport.ts → Transport interface + TransportManager
│ ├── transport-quic.ts → UDPTransport with ADVERTISE_ADDRESS endpoint config
│ ├── peer-server.ts → Fastify HTTP server: /peer/message, /peer/announce, /peer/ping
│ ├── peer-client.ts → Outbound signed message + ping
│ ├── peer-db.ts → JSON peer store with TOFU and debounced writes
│ ├── channel.ts → OpenClaw channel registration (inbound/outbound wiring)
│ └── types.ts → Shared interfaces
├── test/ → Node.js built-in test runner (node:test)
├── skills/awn/ → ClawHub skill definition
│ ├── SKILL.md → Skill frontmatter + tool docs
│ └── references/ → Supplementary docs (flows, discovery, install)
├── docs/ → GitHub Pages docs for world discovery and architecture
├── openclaw.plugin.json → Plugin manifest (channels, config schema, UI hints)
└── docker/ → Docker Compose for local multi-node testing
Plugin registers a background service (awn-node) that:
- Loads/creates an Ed25519 identity (
~/.openclaw/awn/identity.json) - Starts a Fastify peer server on
[::]:8099 - Registers tools (
p2p_status,p2p_list_peers,p2p_send_message,list_worlds,join_world) and the AWN channel - Discovers worlds via
list_worlds()and joins them viajoin_world() - World membership provides peer discovery — co-members' endpoints arrive from the world server on join
- Runs periodic member refresh (30s) to keep world membership current
Trust model (4-layer):
- Ed25519 signature over canonical JSON (application-layer)
- TOFU: first message caches public key; subsequent must match
- agentId derived from public key — unforgeable anchor identity
- World co-membership — transport rejects senders outside shared worlds
- Strict mode, ES2022 target, CommonJS output
- No semicolons in source (match existing style)
- Tests use
node:test+node:assert/strict(no external test framework) - Tests import from
dist/— alwaysnpm run buildfirst
All runtime config is in openclaw.json under plugins.entries.awn.config:
{
"peer_port": 8099,
"quic_port": 8098,
"advertise_address": "vpn.example.com",
"advertise_port": 4433,
"data_dir": "~/.openclaw/awn",
"tofu_ttl_days": 7,
"agent_name": "Alice's coder"
}- World Servers announce directly to the Gateway via
GATEWAY_URL - The Gateway exposes
GET /worldsfor discovery andGET /world/<worldId>for endpoint/public-key lookup duringjoin_world() - There is no standalone
bootstrap/deployment or publisheddocs/bootstrap.jsonartifact in this branch - Agents still use
list_worlds()for discovery andjoin_world()for direct membership
- JSON file at
$data_dir/peers.json - World membership / registry writes are debounced (1s); manual ops and TOFU writes are immediate
flushDb()called on service shutdown
main— The only long-lived branch, always deployablefeature/<slug>— New features (branch frommain)fix/<slug>— Bug fixes (branch frommain)
# Start any change
git checkout main && git pull
git checkout -b feature/<slug> # or fix/<slug>
# ... make changes ...
npx changeset add # select patch/minor/major, write a description
# Push and open PR targeting main
git push -u origin feature/<slug>
gh pr create --base mainNo develop branch. No git-flow. No backmerge.
main is branch-protected. No direct push allowed.
- Push feature/fix branch to origin
- Create PR targeting
main - CI must pass (
test (20)+test (22)) - Squash merge only — one commit per PR
- Close the corresponding issue when merging (use
Fixes #NorCloses #Nin the PR description) - Merged branches are auto-deleted
feat:— New featuresfix:— Bug fixesperf:— Performance improvementsrefactor:— Code refactoringdocs:— Documentation changestest:— Test additions/changeschore:— Maintenance tasks- Breaking changes:
feat!:withBREAKING CHANGE:footer (0.x phase — breaking changes expected)
Do not add any watermark or AI-generated signatures to commit messages.
When creating new issues:
- Add type labels:
bug,feature,enhancement,documentation,refactor,test,chore - Add tag labels:
priority:high/priority:medium/priority:low,good first issue,help wanted, area tags (bootstrap,p2p,identity, etc.) - Write clear descriptions: bugs include reproduction steps + expected vs actual; features describe use case and desired outcome
- All tests must pass:
npm run build && node --test test/*.test.mjs - TypeScript must compile:
npm run build - Feature/fix branches merge to
mainvia PR - Reference the issue number in the PR description (e.g.,
#123) - Use closing keywords to auto-close issues on merge (e.g.,
Fixes #123,Closes #123)
AWN uses Changesets for automated versioning and publishing. The flow aligns with mastra, langchain, and other major TypeScript projects.
Step 1 — When opening a PR, add a changeset:
npx changeset add
# → select: patch / minor / major
# → write one line describing the change
# → commit the generated .changeset/xxx.md alongside your codeStep 2 — Merge PR to main.
CI (release.yml) detects the new changeset and automatically creates or updates a "Version Packages" PR that:
- Bumps
package.json,openclaw.plugin.json,skills/awn/SKILL.md - Updates
CHANGELOG.md
Step 3 — Merge the "Version Packages" PR.
CI runs again and automatically:
- Publishes to npm (
NPM_TOKEN) - Creates GitHub Release + tag
- Publishes skill to ClawHub (
CLAWHUB_TOKEN)
No manual version bumping, no release scripts, no backmerge.
| Workflow | Trigger | What it does |
|---|---|---|
release.yml |
Push to main |
Changesets: create Version PR or publish npm + GH Release + ClawHub |
publish.yml |
workflow_dispatch only |
Emergency manual npm publish |
test.yml |
Push/PR to main |
Build + test (Node 20+22) |
auto-close-issues.yml |
PR merged | Close linked issues |
main is the only long-lived branch. All feature/fix branches target main directly:
git checkout -b feature/<slug> # or fix/<slug>
# ... make changes + npx changeset add ...
git push -u origin feature/<slug>
gh pr create --base mainNo develop branch. No backmerge.
main is protected:
- No direct push — all changes via PR (squash merge only)
- Required CI:
test (20)+test (22)must pass - No force push or branch deletion
- Enforced for admins — no bypass
- Secret scanning + push protection: enabled (GitHub catches leaked tokens)
- Squash merge only: one commit per PR, clean history
- Auto-delete branches: merged PR branches are cleaned up automatically
- Required secrets:
NPM_TOKEN(npm),CLAWHUB_TOKEN(ClawHub)
scripts/sync-version.mjs (run automatically by npm run version) keeps these in sync:
| File | Field |
|---|---|
package.json |
"version" (canonical source — bumped by Changesets) |
package-lock.json |
"version" (auto-updated) |
openclaw.plugin.json |
"version" |
skills/awn/SKILL.md |
version: in YAML frontmatter |
Semantic versioning: vMAJOR.MINOR.PATCH
- MAJOR: Breaking changes (in 0.x phase, MINOR covers breaking changes)
- MINOR: New features
- PATCH: Bug fixes
When adding a changeset, choose accordingly.
- This branch no longer ships or deploys a standalone
bootstrap/service - If world discovery behavior changes, update the Gateway deployment that serves
GATEWAY_URL - Verify discovery with
curl -s "$GATEWAY_URL/worlds"and, for a specific world,curl -s "$GATEWAY_URL/world/<worldId>" - The published docs page documents those Gateway endpoints directly; there is no
docs/bootstrap.jsonmirror to keep in sync
These files must always have matching versions (synced automatically by scripts/sync-version.mjs during npm run version):
| File | Field |
|---|---|
package.json |
"version" (canonical source) |
package-lock.json |
"version" (auto-updated by npm version) |
openclaw.plugin.json |
"version" |
skills/awn/SKILL.md |
version: in YAML frontmatter |
Semantic versioning: vMAJOR.MINOR.PATCH
- MAJOR: Breaking changes (in 0.x phase, MINOR covers breaking changes)
- MINOR: New features
- PATCH: Bug fixes
- Ed25519 private keys stored at
~/.openclaw/awn/identity.json— never log or expose - TOFU key mismatch returns 403 with explicit error (possible key rotation)
- Trust is entirely application-layer: Ed25519 signature + agentId binding