clients/lib is a dependency-free ECMAScript module for modern browsers and
Node 22+. It speaks both the server's standard IRC-over-WebSocket transport and
the bounded M11 native gateway. It is the common protocol/state layer for the
reference clients. The M12A reference MCP adapter is likewise dependency-free.
There is no bundler or generated copy.
- strict IRC message parsing, IRCv3 tag unescaping, and RFC1459 casefolding;
- safe command and client-tag formatting with separate byte and line-injection checks;
text.ircv3.netandbinary.ircv3.netframing;- CAP 302 negotiation, IRCv3 identity/presence negotiation, and SASL PLAIN chunking;
- channel/direct-message state, type-preserving
PRIVMSG/NOTICE, stable message-ID deduplication, history batches, extended joins, userhost-bearing NAMES normalization, membership, live topics, KICK reconciliation, and unread counts; - bounded helpers for channel role modes, account-ban queries/changes, member limits, invitations, and kicks;
- bounded authenticated helpers and complete-state events for recipient-owned direct-message policy and allow/block rules;
- labeled-command helpers for correlating acknowledgments, failures, echoes, and multi-line batches;
- negotiated
draft/multilinelimits, all-or-nothing outbound batch construction, and exact live/history batch reassembly into one model event; - separate account read markers and per-installation delivery markers;
- retained account identity tags for current-avatar lookup without persisting avatar bytes in the shared message model;
- compact revision-tagged IRC automation policy snapshots emitted as
automationpolicystate events rather than transcript messages; - bounded history helpers for LATEST, BEFORE, AFTER, and installation RESUME; and
- content-epoch discovery plus a durable-sync coordinator that prepares the exact server/account storage scope before loading, commits complete pages before acknowledging their final message, and requests the next page; and
- native one-use ticket exchange, optional operation/channel attenuation, UUIDv7 request correlation, structured events, independent pageable automation history, grant-scoped current-roster pages, idempotent publish and visible bounded-invocation operations, validated automation guardrail snapshots, and a native durable coordinator with the same commit-before-ack rule; and
- bounded provider-hosted catalogue search, validated package descriptors, requester-private one-use capabilities, and provider package upsert/removal helpers, plus live provider-side DCC offer, resume, and completion control. Package byte sizes and resume offsets remain exact decimal strings on the wire, and the library never fetches capability URLs.
It intentionally does not store passwords, reconnect automatically, or claim a message was durably received. Those policies belong to an application with an appropriate credential source and durable storage implementation.
import { IrcSession } from "./lib/index.js";
const session = new IrcSession({
url: "wss://irc.example.net:8097/irc",
nickname: "alice-web",
authenticationId: "alice",
password: passwordFromAUserPrompt,
enableDeviceResume: true,
installationToken: tokenLoadedFromPrivateStorage ?? undefined,
});
session.addEventListener("ready", () => {
session.join("#lounge");
const { label } = session.sendLabeledCommand("WHOIS", ["bob"]);
rememberPendingQuery(label);
});
session.model.addEventListener("change", ({ detail }) => {
if (detail.kind === "message") {
render(detail.conversation, detail.message);
}
});
session.addEventListener("automationpolicy", ({ detail }) => {
replaceChannelAutomationState(detail.target, detail);
});
session.connect();The URL must be ws:// or wss://, must use /irc, and cannot contain URL
credentials, a query, or a fragment. Credentials are consumed after the SASL
response is sent and never included in observable outbound events. Create a new
session with freshly obtained credentials when reconnecting.
Read and delivery state have different APIs because they prove different facts:
// Call only after this server-timestamped message is actually visible to the
// human. This advances the account-wide read marker.
session.markVisible("#lounge", message);
// Enable installation resume only when the application has durable local storage.
// Call only after the transaction containing this msgid has committed.
session.acknowledgeStored("#lounge", message);enableDeviceResume defaults to false. Turning it on negotiates
draft/device-resume-0.2. After account-password authentication, the library
binds a supplied dr1... installation token or emits an installation event
with a newly issued token for the application to persist. It never acknowledges
on socket receipt or on insertion into its in-memory model. An application pages with
session.resume(target) and advances the delivery checkpoint after its own
durable commit. This preserves the rule that a crash between network receipt
and local persistence may cause a duplicate, but never a silent gap.
DurableMessageSync implements that ordering for stores exposing
prepareContentEpoch(epoch), loadMessages(conversation), and
putMessage(conversation, message). It never advances a delivery checkpoint
for an arbitrary LATEST/BEFORE/AFTER page, and a storage failure stops resume
for that conversation without acknowledging it.
The server advertises its persisted epoch as CONTENTEPOCH in 005. Before
using durable state, the coordinator asks the store to reconcile that value.
The reference stores atomically clear only the matching server/account scope if
the epoch changed and emit epochprepared with discarded: true when cached
messages were removed. This is cooperative local cleanup, not remote erasure.
The model tracks MARKREAD and MARKDELIVERED independently. Direct-message
history is keyed by the stable @account target carried by its history batch,
not by the replayed nickname prefix.
NativeSession connects to an HTTP(S) control origin, exchanges a human account
password or automated-account app key for a one-use ticket, and opens /v1/sync with
irc.native.v1. Callers may request a ticket restricted to selected v1
operations and canonical channels. NativeDurableSync restores the shared
model, pages from the installation checkpoint, commits events through the existing
store interface, and acknowledges only after that commit succeeds. It also
validates the target-only, zero-to-ten-event invocation context envelope.
Every native channel snapshot also carries the moderator-pause state, fixed window message/byte budgets, repetition and causal-loop thresholds, and active account/channel cooldowns. Policy replacements may keep the same revision when only short-lived cooldown state changes; they may never move the durable policy revision backwards.
The library consumes the secret after the ticket request and redacts the ticket from observable outbound events. Human clients automatically bind a distinct installation token for cursor state. Use HTTPS outside loopback, and remember that a ticket scope attenuates only that ticket: software holding the account password or app key can mint a different one. See the practical guide, the wire contract, and the native terminal.
cd clients
npm test
npm run checkThe tests use only Node's built-in runner. They cover parsing and injection
bounds, SASL chunking/redaction, capability negotiation, stable direct-history
and live account routing, atomic multiline construction and reassembly,
current nickname/account observation, message
deduplication, cursor separation, crash-safe resume ordering,
failed-storage and epoch-change behavior, attenuated native sessions, UUIDv7
generation, requester-private result correlation, MCP lifecycle/framing,
durable invocation context, causal reply proof, private adapter state, and
publisher idempotency, roster-aware causal replies, durable asynchronous job
recovery, Chatbot scheduling/recovery, and ArtifactProvider HTTP/DCC transfer
behavior, plus onboarding/account-control input bounds and authenticated
response validation, bounded avatar caching, and the configured-operator
account/capability/avatar-clear contract.
npm run test:browser
also drives two live invitations through the server-hosted page and exercises
roster-to-account direct-message navigation when the debug daemon and Chrome
are available.
The consumers are documented separately:
- invitation onboarding page
- account controls
- operator controls
- reference terminal client
- reference web client
- native gateway reference client
- reference MCP adapter
- minimal durable CommandBot
- discoverable leading-command CatalogBot
- one-shot ScheduledPublisher
- roster-aware RosterBot
- asynchronous DurableWorker
- configurable ambient Chatbot
- HTTPS/DCC ArtifactProvider