b696c6a0afadd5670049025b278774a7194c5876
Lifecycle:
- Move runtime.start() and shutdown handlers out of register() into
api.on("gateway_start", ...) and api.on("gateway_stop", ...). register()
runs in every CLI subprocess that loads plugins (e.g. `openclaw completion`,
`openclaw doctor`); without this gate the runtime would open a network
connection / bind a listener every time those one-shot commands ran.
- Drop process.once("SIGTERM"/"SIGINT") in favour of the gateway_stop hook,
which is the documented way for plugins to react to shutdown.
- Stop relying on the non-standard `api.rootDir` field (not present on the
current OpenClawPluginApi); compute the per-plugin data directory as
~/.openclaw/yonexus-server and ensure it exists before use.
Plugin SDK convention update:
- Wrap default export with definePluginEntry({ id, name, description, register })
per the current openclaw plugin authoring contract.
- Re-type the register function to accept OpenClawPluginApi instead of the
hand-crafted { rootDir, pluginConfig, ... } shape.
- Use focused subpath imports openclaw/plugin-sdk/plugin-entry and
openclaw/plugin-sdk/core.
- Add openclaw as a devDependency (file:/usr/lib/node_modules/openclaw) so
tsc resolves the SDK type subpaths at build time.
- Modernize openclaw.plugin.json: drop version/entry/permissions, add
activation.onStartup so gateway_start fires for this plugin at boot.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Yonexus.Server
Yonexus.Server is the central hub plugin for a Yonexus network.
It runs on the main OpenClaw instance and is responsible for:
- accepting WebSocket connections from follower instances
- enforcing the
followerIdentifiersallowlist - driving pairing and authenticated reconnects
- tracking heartbeat/liveness state
- rewriting inbound client rule messages before dispatch
- sending pairing notifications to the human admin via Discord DM
Status
Current state: core runtime MVP with Discord DM transport wired via REST API
Implemented in this repository today:
- config validation
- runtime lifecycle wiring
- JSON persistence for durable trust records
- WebSocket transport and single-active-connection promotion model
- pairing session creation
- auth proof validation flow
- heartbeat receive + liveness sweep
- rule registry + send-to-client APIs
- Discord DM pairing notifications via Discord REST API (
notifyBotToken+adminUserId)
Still pending before production use:
- broader lifecycle integration with real OpenClaw plugin hooks
- more operator-facing hardening / troubleshooting polish
- expanded edge-case and live-environment validation beyond the current automated suite
Install Layout
This repo expects the shared protocol repo to be available at:
protocol/
In the umbrella repo this is managed as a submodule.
Configuration
Required config shape:
{
"followerIdentifiers": ["client-a", "client-b"],
"notifyBotToken": "<discord-bot-token>",
"adminUserId": "123456789012345678",
"listenHost": "0.0.0.0",
"listenPort": 8787,
"publicWsUrl": "wss://example.com/yonexus"
}
Field notes
followerIdentifiers: allowlisted client identifiersnotifyBotToken: bot token used for pairing notificationsadminUserId: Discord user that receives pairing DMslistenHost: optional bind host, defaults to local runtime handlinglistenPort: required WebSocket listen portpublicWsUrl: optional public endpoint to document/share with clients
Runtime Overview
Startup flow:
- validate config
- load persisted trust records
- ensure allowlisted identifiers have base records
- start WebSocket transport
- start liveness sweep timer
Connection flow:
- unauthenticated socket connects
- client sends
hello - server decides
pair_required,waiting_pair_confirm, orauth_required - if needed, server creates a pending pairing request and notifies admin out-of-band
- client confirms pairing or authenticates with signed proof
- authenticated connection is promoted to the active session for that identifier
Public API Surface
Exported runtime helpers currently include:
sendMessageToClient(identifier: string, message: string): Promise<boolean>
sendRuleMessageToClient(identifier: string, ruleIdentifier: string, content: string): Promise<boolean>
registerRule(rule: string, processor: (message: string) => unknown): void
Rules:
builtinis reserved and cannot be registered- server-side dispatch expects rewritten client-originated messages in the form:
${rule_identifier}::${sender_identifier}::${message_content}
Persistence
Durable state is stored as JSON and includes at least:
- identifier
- pairing status
- public key
- secret
- pairing metadata
- heartbeat / auth timestamps
- last known liveness status
Rolling nonce and handshake windows are intentionally rebuilt on restart in v1.
Development
Install dependencies and run type checks:
npm install
npm run check
Limitations
Current known limitations:
- DM delivery depends on Discord bot permissions and the target user's DM settings
- no offline message queueing
- no multi-server topology
- no management UI
- transport is covered mainly by automated tests rather than live Discord end-to-end validation
Related Repos
- Umbrella:
../ - Shared protocol:
../Yonexus.Protocol - Client plugin:
../Yonexus.Client
Description
Languages
TypeScript
98.2%
JavaScript
1.8%