# Yonexus — Project Plan ## 1. Goal Yonexus is a cross-instance communication system for OpenClaw, implemented as **two separate plugins**: - `Yonexus.Server` - `Yonexus.Client` Together they provide: - communication between multiple OpenClaw instances - a central WebSocket hub model - client pairing and authentication - heartbeat-based client liveness tracking - rule-based message dispatch - out-of-band pairing notification to a human administrator via Discord DM - TypeScript interfaces for higher-level plugin/runtime integrations This project is no longer a role-switched single plugin. It is now explicitly split into two installable plugins with distinct responsibilities. --- ## 2. Plugin Split ## 2.1 Yonexus.Server `Yonexus.Server` is installed only on the main OpenClaw instance. Responsibilities: - start and maintain the WebSocket server - accept incoming client connections - maintain the client registry - handle pairing flow - verify authentication proofs - track heartbeat and connection state - route or relay messages to connected clients - rewrite inbound client messages before rule dispatch - send Discord DM pairing notifications to the human administrator ## 2.2 Yonexus.Client `Yonexus.Client` is installed on follower OpenClaw instances. Responsibilities: - connect to the configured Yonexus server - generate and persist local keypair on first use - persist local client identity and secret - perform pairing confirmation - perform authenticated reconnect - send periodic heartbeats - expose client-side messaging and rule registration APIs --- ## 3. Deployment Model A Yonexus network contains: - exactly one OpenClaw instance running `Yonexus.Server` - one or more OpenClaw instances running `Yonexus.Client` Topology rules: - `Yonexus.Server` must be reachable via fixed IP/domain or otherwise stable addressable endpoint - `Yonexus.Client` instances do not need stable public IP/domain - all `Yonexus.Client` instances connect outbound to the `Yonexus.Server` WebSocket endpoint - no direct client-to-client communication is required in v1 - inter-client communication, if needed, is relayed by `Yonexus.Server` --- ## 4. Configuration Model ## 4.1 Yonexus.Server Config ```ts followerIdentifiers: string[] notifyBotToken: string adminUserId: string listenHost?: string listenPort: number publicWsUrl?: string ``` Semantics: - `followerIdentifiers`: allowlist of client identifiers permitted to pair/connect - `notifyBotToken`: Discord bot token used to send pairing notifications - `adminUserId`: Discord user id of the human administrator who receives pairing codes by DM - `listenHost`: local bind host for WebSocket server - `listenPort`: local bind port for WebSocket server - `publicWsUrl`: optional canonical external URL advertised/documented for clients ## 4.2 Yonexus.Client Config ```ts mainHost: string identifier: string notifyBotToken: string adminUserId: string ``` Semantics: - `mainHost`: WebSocket endpoint of `Yonexus.Server` - `identifier`: unique identity of this client inside the Yonexus network - `notifyBotToken`: kept aligned with shared config expectations if future client-side notification behaviors are needed - `adminUserId`: human administrator identity reference shared with the Yonexus system ## 4.3 Validation Rules ### Yonexus.Server - must provide `followerIdentifiers` - must provide `notifyBotToken` - must provide `adminUserId` - must provide `listenPort` - must be deployed on a reachable/stable endpoint ### Yonexus.Client - must provide `mainHost` - must provide `identifier` - must provide `notifyBotToken` - must provide `adminUserId` ### Shared - invalid or missing required fields must fail plugin initialization - unknown client identifiers must be rejected by `Yonexus.Server` --- ## 5. Runtime Lifecycle ## 5.1 Yonexus.Server Startup On OpenClaw gateway startup: - initialize persistent client registry - start WebSocket server - register builtin protocol handlers - register application rule registry - start heartbeat/status sweep timer ## 5.2 Yonexus.Client Startup On OpenClaw gateway startup: - load local persisted identity, private key, and secret state - generate keypair if absent - connect to `mainHost` - perform pairing or authentication flow depending on local state - start heartbeat schedule after successful authentication - attempt reconnect when disconnected --- ## 6. Server Registry and Persistence `Yonexus.Server` must maintain a registry keyed by client `identifier`. Each client record contains at minimum: - `identifier` - `publicKey` - `secret` - pairing state - pairing expiration data - pairing notification metadata - connection status - security counters/window data - heartbeat timestamps - last known session metadata The registry must use: - in-memory runtime state for active connections and recent security windows - persistent on-disk storage for durable trust state ### 6.1 Proposed Server Record Shape ```ts interface ClientRecord { identifier: string; publicKey?: string; secret?: string; pairingStatus: "unpaired" | "pending" | "paired" | "revoked"; pairingCode?: string; pairingExpiresAt?: number; pairingNotifiedAt?: number; pairingNotifyStatus?: "pending" | "sent" | "failed"; status: "online" | "offline" | "unstable"; lastHeartbeatAt?: number; lastAuthenticatedAt?: number; recentNonces: Array<{ nonce: string; timestamp: number; }>; recentHandshakeAttempts: number[]; createdAt: number; updatedAt: number; } ``` --- ## 7. Pairing and Authentication ## 7.1 First Connection and Key Generation When a client connects to the server for the first time: - `Yonexus.Client` generates a public/private key pair locally - the private key remains only on the client instance - the public key is sent to `Yonexus.Server` during handshake If the server sees that: - the client identifier is allowed, and - there is no valid `secret` currently associated with that identifier then the server must enter pairing flow. ## 7.2 Pairing Flow ### Step A: Pairing Request Creation `Yonexus.Server` generates: - a random pairing string - an expiration time The pairing string must **not** be sent to the client over WebSocket. Instead, `Yonexus.Server` uses `notifyBotToken` to send a Discord DM to `adminUserId` containing: - the client `identifier` - the generated `pairingCode` - the expiration time ### Step B: Pairing Confirmation The client must provide the pairing code back to the server before expiration. How the client operator obtains the pairing code is intentionally out-of-band from the Yonexus WebSocket channel. The server only trusts that the code came through some human-mediated path. If the client sends the correct pairing code before expiration: - pairing succeeds ### Step C: Secret Issuance After successful pairing: - `Yonexus.Server` generates a random `secret` - `Yonexus.Server` returns that `secret` to the client - `Yonexus.Server` stores client `publicKey` + `secret` - `Yonexus.Client` stores private key + secret locally If Discord DM delivery fails: - pairing must not proceed - server should mark the pairing attempt as failed or pending-error - client must not receive a usable pairing code through the protocol channel If pairing expires before confirmation: - pairing fails - the client must restart the pairing process ## 7.3 Reconnect Authentication Flow After pairing is complete, future client authentication must use: - the stored `secret` - a 24-character random nonce - current UTC Unix timestamp The client builds a proof payload from: - `secret` - `nonce` - `timestamp` Logical concatenation order: ```text secret + nonce + timestamp ``` Implementation recommendation: - use a canonical serialized object and sign its bytes rather than naive string concatenation in code The client signs the proof using its private key and sends it to the server. The server verifies: 1. identifier is known and paired 2. public key matches stored state 3. proof contains the correct `secret` 4. timestamp difference from current time is less than 10 seconds 5. nonce does not collide with the recent nonce window 6. handshake attempts in the last 10 seconds do not exceed 10 If all checks pass: - authentication succeeds - the client is considered authenticated for the session If any check fails: - authentication fails - server may downgrade or revoke trust state ## 7.4 Unsafe Condition Handling The connection is considered unsafe and must return to pairing flow if either is true: - more than 10 handshake attempts occur within 10 seconds - the presented nonce collides with one of the last 10 nonces observed within the recent window When unsafe: - existing trust state must no longer be accepted for authentication - the client must re-pair - server should clear or rotate the stored `secret` - server should reset security windows as part of re-pairing --- ## 8. Heartbeat and Client Status The server must track each client’s liveness state: - `online` - `unstable` - `offline` ## 8.1 Heartbeat Rules Each client must send a heartbeat to the server every 5 minutes. ## 8.2 Status Transitions ### online A client is `online` when: - it has an active authenticated WebSocket connection, and - the server has received a recent heartbeat ### unstable A client becomes `unstable` when: - no heartbeat has been received for 7 minutes ### offline A client becomes `offline` when: - no heartbeat has been received for 11 minutes When a client becomes `offline`: - the server must close/terminate the WebSocket connection for that client ## 8.3 Status Evaluation Strategy The server should run a periodic status sweep timer. Recommended interval: - every 30 to 60 seconds --- ## 9. Messaging Model Yonexus provides rule-based message dispatch over WebSocket. ## 9.1 Base Message Format All application messages must use the format: ```text ${rule_identifier}::${message_content} ``` ## 9.2 Server-Side Rewriting When `Yonexus.Server` receives a message from a client, before rule matching it must rewrite the message into: ```text ${rule_identifier}::${sender_identifier}::${message_content} ``` This ensures server-side processors can identify which client sent the message. ## 9.3 Builtin Rule Namespace The reserved rule identifier is: ```text builtin ``` It is used internally for: - handshake - pairing - heartbeat - protocol/system messages User code must not be allowed to register handlers for `builtin`. --- ## 10. TypeScript API Surface ## 10.1 Yonexus.Client API ```ts sendMessageToServer(message: string): Promise ``` Rules: - sends message to connected `Yonexus.Server` - message must already conform to `${rule_identifier}::${message_content}` ```ts registerRule(rule: string, processor: (message: string) => unknown): void ``` Rules: - rejects `builtin` - rejects duplicate rule registration unless explicit override support is added later ## 10.2 Yonexus.Server API ```ts sendMessageToClient(identifier: string, message: string): Promise ``` Rules: - target client must be known and currently connected/authenticated - message must already conform to `${rule_identifier}::${message_content}` ```ts registerRule(rule: string, processor: (message: string) => unknown): void ``` Rules: - rejects `builtin` - rejects duplicate rule registration unless explicit override support is added later - processors are invoked with the final received string after any server-side rewrite --- ## 11. Hooks and Integration ## 11.1 Yonexus.Server Hooking `Yonexus.Server` must register hooks so that when OpenClaw gateway starts: - the WebSocket server is started - the server registry is initialized - builtin protocol handling is enabled - heartbeat sweep begins ## 11.2 Yonexus.Client Behavior `Yonexus.Client` must: - connect outbound to `mainHost` - manage local trust material - handle pairing/authentication transitions - emit heartbeats after authentication - reconnect after disconnect with retry/backoff behavior --- ## 12. Storage Strategy ## 12.1 Yonexus.Server Storage Server persists at minimum: - identifier - public key - secret - trust state - pairing code + expiry if pairing is pending - pairing notification metadata - last known status - metadata timestamps May persist or reset on restart: - recent nonces - recent handshake attempts Recommended v1: - clear rolling security windows on restart - keep long-lived trust records ## 12.2 Yonexus.Client Storage Client persists at minimum: - identifier - private key - secret - optional last successful pair/auth metadata Security notes: - private key must never be sent to the server - secret must be treated as sensitive material - encryption-at-rest can be a future enhancement, but any plaintext local storage must be documented as a limitation if used initially --- ## 13. Error Handling Structured errors should exist for at least: - invalid configuration - unauthorized identifier - pairing required - pairing expired - pairing notification failure - handshake verification failure - replay/nonce collision detected - unsafe handshake rate detected - target client not connected - duplicate rule registration - reserved rule registration - malformed message --- ## 14. Initial Implementation Phases ## Phase 0 — Protocol and Skeleton - finalize split-plugin configuration schema - define persistent data models - define builtin protocol messages - define startup hooks for both plugins - define rule registry behavior - define Discord DM notification flow ## Phase 1 — Transport MVP - Yonexus.Server WebSocket server startup - Yonexus.Client WebSocket client startup - reconnect logic - builtin protocol channel - persistent registry/state scaffolding ## Phase 2 — Pairing and Authentication - client keypair generation - pairing request creation - Discord DM notification to admin user - pairing confirmation flow - secret issuance and persistence - signed proof verification - nonce/replay protection - unsafe-condition reset to pairing ## Phase 3 — Heartbeat and Status Tracking - client heartbeat sender - server heartbeat receiver - periodic sweep - status transitions: online / unstable / offline - forced disconnect on offline ## Phase 4 — Public APIs and Dispatch - `sendMessageToServer` - `sendMessageToClient` - `registerRule` - first-match dispatch - server-side sender rewrite behavior ## Phase 5 — Hardening and Docs - integration tests - failure-path coverage - restart recovery checks - protocol docs - operator setup docs for server/client deployment --- ## 15. Non-Goals for Initial Version Not required in the first version unless explicitly added later: - direct client-to-client sockets - multi-server clustering - distributed consensus - message ordering guarantees across reconnects - end-to-end payload encryption beyond the pairing/authentication requirements - management UI --- ## 16. Open Questions To Confirm Later 1. Exact signing algorithm: - Ed25519 is a strong default candidate 2. Should `mainHost` accept only full WebSocket URLs or also raw `ip:port` strings? 3. Is human code relay sufficient for v1 pairing, or should admin approve/deny controls be added later? 4. On unsafe condition, should the old public key be retained or should the client generate a new keypair? 5. Should offline clients support queued outbound messages from server, or should sends fail immediately? 6. Are rule identifiers exact strings only, or should regex/prefix matching exist later? --- ## 17. Immediate Next Deliverables After this plan, the next files to create should be: - `FEAT.md` — feature checklist derived from this plan - `README.md` — concise system overview for both plugins - `plugin.server.json` or equivalent server plugin manifest - `plugin.client.json` or equivalent client plugin manifest - implementation task breakdown