@uxnan/shared
v0.0.34-alpha.20261015
Published
Shared JSON-RPC and E2EE contracts for the Uxnan ecosystem (bridge, relay, mobile).
Readme
@uxnan/shared
Shared JSON-RPC and E2EE contracts for the Uxnan ecosystem — the
single source of truth every component agrees on. Consumed as a local workspace
dependency by the bridge and the
relay (which bundles only the dependency-free
@uxnan/shared/relay subpath into its Worker); Uxnan Desktop
imports its types straight from src/ (the $shared alias) for the chats it
drives through the bridge; the mobile app keeps manually-synced Dart
equivalents (see
architecture/02b-contracts-and-requirements.md
§1 for the canonical contract list).
Status: implemented and stable — 103 JSON-RPC methods + 25 streaming notifications, kept lock-step at build time with the
METHOD_NAMESarray and theStreamNotificationenum (a compile-time assertion insrc/jsonrpc/method-registry.tsfails the build on any drift). Changes are recorded inCHANGELOG.md.
Why it exists
Four independent codebases — a Node.js bridge, a relay that runs as a Cloudflare Worker, a Tauri + Svelte desktop app and a Flutter app — have to agree on exactly the same messages on the wire. Rather than letting each one drift its own way, every shape lives here once: the request and response envelopes, the streaming notifications, the E2EE handshake, the pairing payload, and the domain and agent models. The bridge and relay import this package directly, Uxnan Desktop imports its types from source, and the mobile app mirrors it in Dart. When a contract changes, it changes here first, and the build refuses to pass if the registry and the spec disagree.
flowchart TB
shared["@uxnan/shared<br/>JSON-RPC + E2EE contracts"]
bridge["uxnan-bridge<br/>(imports directly)"]
relay["uxnan-relay Worker<br/>(bundles ./relay)"]
desktop["uxnandesktop<br/>(imports types via $shared)"]
mobile["uxnanmobile<br/>(hand-synced Dart mirror)"]
shared --> bridge
shared --> relay
shared --> desktop
shared -. mirrored .-> mobileWhat's inside
| Area | Exports |
|---|---|
| JSON-RPC | envelope types + constructors (makeRequest, makeNotification, makeResponse, makeErrorResponse), error codes (JsonRpcErrorCode + Uxnan-specific -32000..-32010), RpcError, typed method registry (JsonRpcMethodRegistry + METHOD_NAMES), isKnownMethod |
| Streaming | StreamNotification enum + param types (TurnStartedParams, MessageDeltaParams, ThinkingDeltaParams, ContentBlockParams, TurnCompletedParams, TurnUsage, TurnErrorParams, TurnAbortedParams, ModelResolvedParams, and the multi-client set: ThreadUpdatedParams, ThreadDeletedParams, TurnCreatedParams, ApprovalResolvedParams, QuestionResolvedParams) |
| Local control | the loopback channel Uxnan Desktop uses on the bridge's machine: LOCAL_CONTROL_FILE / LOCAL_CONTROL_PATH, LocalControlDiscovery, the LocalControlFrame union (hello + message), isValidLocalClientId, localReceiverId (architecture/02a §5.8.15) |
| Replica sync | sync/changes (SyncChanges: revisioned threads, projects, settings, deletions, presence), BridgeSettings { home, name, relay }, ClientPresence, Project registry fields, Turn.seq, Thread.origin (src/models/sync.ts, architecture/02a §5.8.17) |
| Agent locations | agent-locations.json (package root) + locateAgent / agentLocation: where every agent CLI installs — the one table the bridge and Uxnan Desktop resolve from |
| E2EE | handshake messages (clientHello / serverHello / clientAuth / ready), buildHandshakeTranscript, SecureEnvelope, PairingPayload v3 (relay?: PairingRelay { url, routingId, ticket? } + hosts?: string[], at least one) with Base64(utf8(JSON)) QR encoding |
| Relay | @uxnan/shared/relay (subpath export, dependency-free so the Worker can bundle it; also re-exported from the root): the relay control protocol — routes /v1/host · /v1/connect · /v1/channel (relayRoutePath, parseRelayPath), relaySigningMessage, the challenge / auth / ready / allow / ticket / dial frames and their parsers, RELAY_CLOSE (4001–4011), RELAY_PING/RELAY_PONG, limits and timeouts, RELAY_COMPATIBILITY_DATE (architecture/02a §5.10). Models in src/models/relay.ts: RelayEndpoint, RelayStatus (incl. hostKey), RelaySetupParams, RelayUseParams, RelaySetParams, RelayCredentialParams, RelayRemoveParams, RelayUpdatedNotification — for relay/status · setup · use · set · update · rotate · remove and stream/relay/updated |
| Models | thread / turn / message (with MessageContent polymorphic blocks), durable CompactionContentBlock and AssistantResponseBoundaryBlock, git, workspace (incl. browseDirs + exists), project, auth, session/trust (BridgeStatus incl. latestVersion?/updateAvailable?), bridge-owned profile metrics (complete-ledger export/import), approval, question (interactive multiple-choice) |
| Agent sessions | agent/sessions · holds · hold · release · requestHandoff · handoffAnswer (AgentSessionSummary, AgentSessionHold, hand-off outcomes), stream/agent/held / handoffRequested, SessionHeld (-32010), ONE_SHOT_PROMPT_OPENERS / isOneShotPrompt (src/models/agent-session.ts, src/agents/one-shot.ts, architecture/02a §5.8.19) |
| Agents | IAgentAdapter (with respondApproval, listModels, nativeSessionId, adoptNativeSession, listNativeSessions → NativeSessionInfo, SendTurnOptions { threadId, turnId, text, service?, effort?, options?, attachments?, cwd?, accessMode? }), AgentModel (incl. version?, isDefault?, options?, contextWindow?, isLatestAlias?), AgentCapabilities (incl. images, approvals, reportsContextUsage, reportsCompaction), AgentDescriptor.deprecated, AgentConfig (cwd, agentId, model, plus optional binaryPath/extraArgs) |
| Agent views | the page ↔ host protocol (VIEW_METHODS, VIEW_HOST_METHODS, ViewHostContext, VIEW_THEME_VARIABLES and VIEW_PAGE_VARIABLES, VIEW_THEME_EVENT), annotations (ViewAnnotation, formatViewAnnotations), declarative view blocks (ViewBlock, VIEW_BLOCK_TYPES, parseViewBlocks), the view tool names and limits (VIEW_TOOL_NAME, VIEW_MAX_HTML_BYTES, VIEW_MAX_BLOCKS, VIEW_MAX_BLOCKS_BYTES) |
| Version | compareVersions / isNewerVersion — dependency-free SemVer precedence (used by the bridge's npm update check) |
| Validation | Ajv validators for requests, responses, envelopes, pairing payload |
Usage
import {
makeRequest,
isKnownMethod,
validateJsonRpcRequest,
METHOD_NAMES,
type AgentModel,
type PairingPayload,
} from '@uxnan/shared';
// Code that must not pull Ajv or Node built-ins (the relay Worker):
import { parseRelayClientFrame, RELAY_CLOSE } from '@uxnan/shared/relay';Develop
npm run build # tsc → dist/
npm test # tsc + node --test dist/test
npm run typecheck # tsc --noEmitRequires Node ≥18. The package is ESM-only.
Source of truth
The canonical contract list lives in this package — see
src/jsonrpc/method-registry.ts (METHOD_NAMES)
and src/jsonrpc/notifications.ts
(StreamNotification). The spec mirrors it in
architecture/02b-contracts-and-requirements.md
§1.2 / §1.4. Per AGENTS.md → Spec drift control, any change here MUST be
reflected in the spec in the same change set.
Published
@uxnan/shared is published to npm; uxnan-bridge then pins
"@uxnan/shared": "^0.x" — the release workflow replaces the "*" workspace
spec with the exact published version at build time. The relay is not
published: its Worker bundle (shared protocol included) ships inside the
bridge package.
See bridge/FOR-DEV.md → Packaging.