Packages
Every @chatpack/* package, what it exports, and when you need it.
All packages are MIT-licensed and published to npm. Everything is 0.x until
the public API stabilizes - expect minor breaking changes before 1.0.
The current package set covers direct and group conversations, invite links and
join requests, public channels with a browsable directory, text messages,
permissions, read state and unread counts, SSE with gap-fill, memory and Drizzle
storage, Redis event fan-out, typing/presence/receipts, reactions, quote-replies,
validated mentions, message forwarding, participant-scoped search, the
opt-in message thread API,
post-persistence mutation hook, the browser client with React hooks and polling
fallback, Filepack-backed file attachments, and project setup and complete
starter generation with @chatpack/cli init, plus reusable React UI blocks from
@chatpack/ui.
The Chatpack app includes a thread inbox and optional email and browser push
alerts. Those alerts are application code, not core package APIs. Multi-node
presence is available with redisPresenceStore() and the Redis transport.
@chatpack/core
The engine: domain logic, permissions, HTTP handler, SSE, plugins, telemetry. The only package every integration needs.
Main exports:
| Export | What it is |
|---|---|
chatpack(options) | The single factory - returns { api, handler, transport, telemetry, options } |
ChatpackError | Domain error with a stable code |
pairKeyFor(a, b) | Deterministic DM pair key (internal detail, exported for adapters/tests) |
inProcessTransport() | The default single-node transport |
isEphemeralEvent(event) | Type guard for TransportEvent |
isMessageEvent(event) | Type guard for the message half of TransportEvent - TransportEvent has four members (ChatEvent, ReactionEvent, ConversationEvent, EphemeralEvent), so "not ephemeral" no longer means "a message" |
isReactionEvent(event) | Type guard for reaction.added / reaction.removed |
isConversationEvent(e) | Type guard for the membership events participant.added / participant.removed / conversation.updated |
| Types | ChatpackOptions, ChatpackUser, AuthHook, PermissionHooks, StorageAdapter, InviteStorage, ChannelStorage, Transport, ChatpackPlugin, Conversation, ConversationType, Participant, ParticipantRole, Message, MessageWithDetails, Reaction, ReactionSummary, MessageRole, ConversationInvite, InvitePreview, JoinRequest, JoinRequestStatus, ChannelVisibility, ChannelJoinPolicy, ChannelPreview, TelemetryPayload, ... |
Subpath @chatpack/core/plugins:
| Export | What it is |
|---|---|
typing() | Typing indicators - POST /conversations/:id/typing → typing.started / typing.stopped |
presence(options?) | Online/offline - SSE connection is the heartbeat; offlineDelayMs default 5000 |
receipts() | Delivered/read ticks - hooks into send + mark-read |
@chatpack/client
The framework-agnostic browser client for the public REST and SSE contract. Authentication remains owned by the application.
import { createChatClient } from "@chatpack/client";
import { typingClient, presenceClient, receiptsClient } from "@chatpack/client/plugins";
export const chatClient = createChatClient({
credentials: "include",
plugins: [typingClient(), presenceClient(), receiptsClient()],
});
const result = await chatClient.conversations.list();@chatpack/client/react adds useConversations, useConversation,
useMessages, useMessageSearch, useRealtimeStatus, and first-party plugin hooks. It uses
useSyncExternalStore and has no state-library dependency. Invite, join-request,
and channel actions are imperative client methods; they do not add new React
cache queries.
| Export | What it is |
|---|---|
createChatClient(options?) | One per-application client instance |
conversations / messages | Typed REST namespaces with { data, error } results |
conversations.createGroup etc. | The five group mutations (create, add/remove participant, set role, rename) - membership events update the cache |
invites | Create, list, revoke, preview, and accept invite links |
joinRequests | Create, list, approve, and deny join requests |
channels | List public-channel previews and join channels |
messages.react / unreact | Idempotent reaction writes; return the message's complete reaction set |
messages.forward | Copy a message into another conversation (toConversationId); resolves with the copy |
messages.search | Participant-scoped, whole-token, relevance-ranked message search |
realtime | One lazy EventSource with durable deduplication |
realtime config | mode: "auto" | "sse" | "poll" - polling fallback where SSE can't work |
isReactionChatEvent(event) | Narrowing helper for reaction.added / reaction.removed |
isConversationChatEvent(event) | Narrowing helper for participant.added / participant.removed / conversation.updated |
@chatpack/client/plugins | Typing, presence, and receipts client adapters |
@chatpack/adapter-memory
In-memory storage - zero-setup demos and fast, deterministic tests. The
reference StorageAdapter implementation - all twenty-one required methods,
including the two that mentions added (setMessageMentions,
listMentionsByMessageIds) - plus all four optional capabilities (search, the
invites namespace behind invite links and join requests, the channels
namespace behind the public directory, and the moderation namespace).
| Export | What it is |
|---|---|
memoryAdapter() | Create an in-memory storage adapter |
@chatpack/adapter-drizzle
Drizzle ORM (Postgres) storage - real persistence for production. Peer
dependency on drizzle-orm.
| Export | What it is |
|---|---|
drizzleAdapter(db) | Create the adapter from any Drizzle Postgres instance |
chatpackSchema | Drizzle table objects (for your drizzle-kit flow) |
conversations, conversationParticipants, messages, messageReactions, messageMentions, messageSearchTokens, conversationInvites, joinRequests, userBlocks, conversationMutes, moderationReports, userBans | The individual tables |
migrationSql | Idempotent DDL as one string (no Drizzle needed to run it) |
migrationStatements | The same DDL split for one-statement-per-call drivers |
backfillMessageSearchTokens(db) | Index existing message bodies for search after an upgrade |
@chatpack/adapter-prisma
Prisma ORM 7 PostgreSQL storage. Server-only. Pass a consumer-generated Prisma client; the package does not bundle one.
| Export | What it is |
|---|---|
prismaAdapter(client) | Create the adapter from the caller-owned Prisma client |
backfillMessageSearchTokens(client) | Rebuild search tokens after import |
prisma/schema.prisma | Prisma models to copy into the application schema |
prisma/migrations/0001_chatpack/migration.sql | PostgreSQL migration asset |
Verified: Prisma 7.10.0, @prisma/adapter-pg 7.10.0, PostgreSQL 16. Prisma 8
and other providers/drivers are not claimed compatible.
@chatpack/adapter-sqlite
Drizzle ORM SQLite storage for durable local and single-node deployments.
Requires drizzle-orm, better-sqlite3, and Node.js 22 or newer.
| Export | What it is |
|---|---|
sqliteAdapter(db) | Create the adapter from a Drizzle better-sqlite3 database |
chatpackSchema | Drizzle table objects |
migrationSql / migrationStatements | Idempotent SQLite DDL |
backfillMessageSearchTokens(db) | Rebuild search tokens after importing existing messages |
→ SQLite
@chatpack/adapter-mysql
Server-side MySQL 8 storage through Drizzle's mysql2 driver. Requires a
transaction-capable mysql2 connection or pool.
| Export | What it is |
|---|---|
mysqlAdapter(db) | Create the adapter from a Drizzle MySQL instance |
chatpackSchema and table exports | Drizzle schema objects |
migrationSql / migrationStatements | MySQL 8 DDL |
backfillMessageSearchTokens(db) | Rebuild search tokens after importing messages |
→ MySQL
@chatpack/adapter-turso
Drizzle ORM storage backed by Turso/libSQL. It provides the full persistent adapter contract, optional channels, invite links, join requests, and moderation storage.
| Export | What it is |
|---|---|
tursoAdapter(db) | Create the adapter from a Drizzle libSQL instance |
chatpackSchema and table exports | Drizzle schema objects |
migrationSql | Idempotent DDL as one string |
migrationStatements | The same DDL split for one-statement-per-call drivers |
backfillMessageSearchTokens(db) | Index existing message bodies for search after an upgrade |
@chatpack/adapter-supabase
Server-side Supabase/Postgres storage. Pass an already-created privileged Supabase client; apply its RLS-safe migration before use.
| Export | What it is |
|---|---|
supabaseAdapter(client, options?) | Create the adapter from a server-side Supabase client |
SupabaseAdapterOptions | Optional generated-id prefix configuration |
→ Custom adapter and Supabase guidance
@chatpack/next
Next.js App Router integration - sugar for mounting the handler.
| Export | What it is |
|---|---|
toNextRouteHandlers(chat, options?) | Returns { GET, POST, PATCH, DELETE, PUT } for a catch-all route file |
@chatpack/cli
The setup CLI for detecting a project and generating a safe Chatpack integration.
npx @chatpack/cli initThe command supports Next.js App Router, Hono, and Express. In an empty repository it creates a complete Next.js chat application with Better Auth, Auth.js, or Auth0, or a fail-closed Hono/Express backend. In an existing project it generates focused integration wiring. It never provisions accounts, writes secrets, runs migrations, or deploys.
The Next.js UI is reviewed application-owned source. It is not a reusable
@chatpack/ui package, so Chatpack remains headless at the package boundary.
@chatpack/transport-redis
Redis pub/sub Transport for multi-node SSE fan-out. Needed once you run more
than one app server: the default transport fans out inside a single process, so
a message sent on node B never reaches a stream held by node A.
import { redisTransport } from "@chatpack/transport-redis";
import { Redis } from "ioredis";
const transport = redisTransport({
publisher: new Redis(process.env.REDIS_URL!),
subscriber: new Redis(process.env.REDIS_URL!),
});| Export | What it is |
|---|---|
redisTransport(options) | A Transport plus nodeId and close() |
DEFAULT_CHANNEL | "chatpack:events" |
| Types | RedisTransportOptions, RedisPublisher, RedisSubscriber, ... |
Bring your own client (ioredis or node-redis v4+) - Chatpack has no Redis
dependency. Two separate connections are required, since a client in subscriber
mode cannot PUBLISH.
@chatpack/file
Filepack-backed message attachments. Mounts Filepack's upload/download routes
below the Chatpack handler (default /api/chat/files) and validates every
attachment reference against the conversation before a message persists.
Chatpack stores only stable references
({ filepack: { version: 1, attachments: [{ id, name, contentType, size }] } })
in message metadata - never bytes, signed URLs, or object keys.
| Export | What it is |
|---|---|
createFileAttachmentPlugin(opts) | The plugin: nested Filepack routes + blocking message hook |
createFileAttachmentMessageHook | Just the beforeMessageSend validation hook |
createFileAttachmentMetadata | Build the metadata namespace from ready Filepack files |
parseFileAttachmentMetadata | Validate the namespace in arbitrary message metadata |
@chatpack/file/client | Browser wrapper over @filepack/client (uploads, resume, media) |
Requires a Filepack instance
(@filepack/core + a storage provider) and an authorizeUpload host policy.
Remember to also export PUT from the catch-all route.
Two things worth knowing before you build an attachment UI:
- A message still needs a
body. Attachments live in metadata and never substitute for the text, so an image-only send is400 INVALID_INPUT- synthesize a body (the file name works) rather than sending"". - Pass
controlFetchto the browser client while@filepack/clientis at or below 0.1.1: it holdsglobalThis.fetchunbound and calls it as a method, which Chrome rejects. The failure looks like a network error but no request is ever sent.controlFetch: (input, init) => fetch(input, init)fixes it.
import { createChatpackFileClient } from "@chatpack/file/client";
const files = createChatpackFileClient({
basePath: "/api/chat/files",
controlFetch: (input, init) => fetch(input, init),
});llms.txt in every tarball
Every published package ships the repo's
llms.txt - the
single-fetch integration guide for AI builders and coding agents - at its
root: node_modules/@chatpack/core/llms.txt.
Versioning
Releases are managed with Changesets;
every package keeps a CHANGELOG.md. Follow releases on
GitHub or via
npm view @chatpack/core dist-tags.