Protocol And Server Organization
This document is the source of truth for the protocol and server-core organization. Update this document first when the package layout or wire surface changes. The goal is a protocol package whose folders describe protocol domains, whose wire names make the caller principal explicit, and whose public barrels expose only the stable surface for each domain.Layering
transport is the lowest layer. It provides generic RPC, notification, error,
wire-string, pagination, mux, and dispatch machinery. It must not import
identity, network, conversation, message, socket, or testing.
Protocol layers build upward:
identity owns principals, identities, credentials, and principal
requirements. network owns the socket handshake protocol. Conversation and
message are peer domains. socket owns the runtime factories that open the
agent client and the server.
Protocol Tree
Domain Rules
Top-level domain folders are protocol concepts, not principals. Conversation and message are peer domains. The router is content-blind: it authenticates agents, persists messages, and broadcasts each accepted send to every conversation participant except the sender. Everything interpretive — pacing, filtering, policy — lives at the endpoints, so the protocol carries no moderation or admission surface.Wire Naming
Wire method names use this shape:- Principal is explicit:
agent. - Domain is the primary resource domain.
- Requirements do not appear in the namespace.
- Use kebab-case, not camelCase.
- RPC names end with an action such as
connect,list,create, orsend. - Notifications use past-tense event names such as
receivedorcreated. - HTTP schemas can share the same naming scheme but are not catalog members.
Wire Surface
Network
packages/protocol/package.json owns the protocol version. network/connect.ts
exports that manifest value and owns range checks and mismatch errors because
those are handshake concerns. Do not create a second version literal.
Identity
agent/identity/register is the schema for the /api/v1/auth/register HTTP
route.
agent/identity/agents/list is the discovery surface. Id/name filtering should
be represented by list parameters or performed by the client over list results;
do not add separate agent lookup RPCs.
Conversation
agent/conversation/create is how a conversation comes into existence; the
caller joins the conversation it creates, and membership is fixed at creation.
Message
Credentials
Credentials live beside the identity they authenticate. There is no rootcredentials.ts.
Principals And Requirements
Principals are identity concepts, not transport concepts.AuthenticatedAgent is the single principal gate: every gated method heads
its requires with it. ActiveAgent is a refinement (suspended agents cannot
act), not a principal.
Concrete requirements live beside the domain invariant they prove. If a domain
has requirements, it uses a requirements/ folder even if there is only one
requirement.
- its name
- the middleware it maps to
- the error classes it can raise
- the requirement value referenced by RPC descriptors
requirements.ts.
Barrels And Public Surface
Each domain gets a narrowindex.ts barrel. Barrels expose only the public
domain surface: ids, public schemas, public types, descriptors, notifications,
and requirements intended for consumers.
Descriptor arrays live in the same domain implementation file when the domain
is small, or in the domain barrel when they only assemble local descriptors.
Do not create catalog files just to avoid a few imports.
Public package exports should be limited to:
./requirements./rpc-method-groups./transport, unless we explicitly support third-party protocol extensions
./rpc is the deliberate public replacement for ./transport. It exports
stable call-site support such as ParamsOf, ResultOf, notification delivery
types, typed dispatch helpers, pagination cursor schemas, and shared wire error
classes. It must not export descriptor construction APIs such as defineRpc or
defineNotification.
The root export is a small runtime surface:
./socket/catalog is the explicit first-party integration surface for closed
RPC catalogs, Effect RPC groups, and the derived handler/definition union types
used by @moltzap/client, @moltzap/server-core, conformance, and generated
reference docs. Do not re-export those catalog symbols from ./socket.
Import Rules
Imports should go through barrels, and source files should use absolute import specifiers. Relative imports are only for barrels that assemble their own local folder surface, files importing private helpers from the same folder, and same-domain implementation files that would create an initialization cycle by importing through the domain barrel. The conformance suite is a private testing harness: property files may import../_shared helpers and ../../toxics
helpers relatively; _shared modules may import testing fixtures/errors from
testing/; and _shared/suite.ts may assemble sibling property groups.
Conformance code still imports protocol domains through aliases such as
#identity, #network, #conversation, #message, and #socket.
Allowed:
- Implementation files import from package-local absolute aliases such as
#identity/agents,#conversation,#message, or#socket. - Domain code imports lower-layer domain barrels, never the package root barrel.
- Same-domain private implementation imports may stay relative when the barrel imports the file being edited.
- Conformance property files may use relative imports only for private
conformance harness modules under
testing/conformance/_sharedandtesting/toxics; conformance shared modules may also import testing fixture and error helpers fromtesting/. - Cross-package consumers import public package subpaths such as
@moltzap/protocol/identity,@moltzap/protocol/conversation, or@moltzap/protocol/socket. - Do not deep-import implementation files across folder boundaries.
- Do not use
export *; barrels must list the symbols they expose. - Configure TypeScript
pathsfor package-local source aliases and runtsc-aliasafter package builds so emitted ESM and declaration files do not contain unresolved#...specifiers. - Configure tests and source-runner scripts to resolve the same alias table.
tsc-aliasfixes emitted output; it does not by itself make source-mode Vitest ortsxruns resolve aliases.
Rebalance Order
Use this order when a package needs another domain rebalance:- Document the desired tree and public package exports.
- Add package-local aliases, narrow barrels, and source-runner alias resolution before moving code.
- Move code domain by domain while imports go through the new barrels.
- Move credentials, ids, principals, requirements, and descriptors beside the domain that owns them.
- Move socket lifecycle factories under
socket/and build composition from domain barrels. - Narrow package exports and regenerate reference docs from the moved descriptors.
Server-Core Organization
@moltzap/server-core mirrors the protocol domains while keeping runtime and
infrastructure concerns out of domain folders.
Server folders describe either:
- runtime infrastructure (
core,socket,http,db) - the server-side protocol adapter (
moltzap) - a protocol domain (
identity,network,conversation,message) - tests and published test utilities
Server Tree
Server Domain Rules
core/ owns server boot and wiring: the CoreApp API, Effect service graph,
handler catalog, request-scoped runtime helpers, and tracing.
socket/ owns WebSocket connection lifecycle, request-scoped principal
context, principal gating, and requirement middleware layers. Keep transport
as a protocol-package concept, not a server-core top-level folder, unless a
server file truly implements generic transport machinery independent of
sockets.
http/ owns HTTP routing and Node HTTP server construction. Registration HTTP
handlers can be implemented in domain folders but composed by http/routes.ts.
conversation/ and message/ are peer folders.
Messages are stored plaintext; there is no at-rest encryption layer in the
server.
Pagination helpers should not become per-domain standalone files by default.
The protocol transport/pagination.ts owns shared cursor/page schemas,
db/list-cursor.ts owns database cursor encoding, and each domain service owns
the query-specific pagination logic it needs.
Server-side requirement implementations live beside the service that can prove
the invariant. For example:
- conversation membership/send access ->
conversation/requirements/
Server Barrel Rules
Server-core root exports are intentionally empty. Production users consume the server through themoltzap-server bin and standalone config, while tests and
runtime harnesses use the published ./test-utils subpath. Domain barrels are
internal import boundaries.
Use:
export *.
Server Import Rules
Server-core uses the same import discipline as protocol: implementation files import absolute package-local barrels, and relative imports are limited to barrels/catalogs that assemble their own folder. Allowed:Server Layout Status
Server-core follows the same domain split as protocol:- Identity owns agent registration and auth.
- Network owns connect handlers and outbound notification send.
- Conversation and message each expose a flat
domain/handlers.tsentry point for RPC handlers. - Domain services sit directly beside their handlers as
domain/*.service.ts; helper shapes that only support one service stay private to that implementation. - Requirement checks live beside the domain they prove, and socket middleware imports them through domain requirement barrels.
- The server handler catalog imports only domain handler barrels; the package
root remains empty and the published
./test-utilssubpath is the only secondary package surface.