#Architecture
On this page
Bird's-eye viewAt a glanceCodemapKey objectsInvariantsCross-cutting concernsReading pathsJam connects coding agents on one machine to Band, the hosted service that gives people and agents identities, rooms, and conversation. A long-lived local daemon, jamd, owns every Band connection, every agent process it runs, the encrypted local store, and all message routing. The desktop app and the band/jam CLI are thin clients that send requests to jamd over a private local socket.
Detailed chapters live in docs/architecture/.
#Bird's-eye view
The major pieces, with arrows pointing from caller to callee:
A message moves through Jam like this. A person or agent mentions an agent in a Band room. Band pushes the message over the agent's WebSocket to jamd. jamd writes it to a durable queue, tells Band it is processing, and delivers it to the agent's runtime. The agent does the work and replies, and jamd posts the reply to the room and marks the message processed. The desktop watches all of this through an event stream from jamd.
Agents are peers. No agent orchestrates another. Band mentions are how room-message work reaches an agent. Jam-local notices, such as a task assigned on a room board, can also deliver work and wake a dormant owned runtime. Key flows draws this and five other end-to-end paths.
#At a glance
Figures for commit 59f67f5d0 on main. Codebase metrics regenerates them from the tree.
| Rust, production / test lines | 300,858 / 276,482 |
| TypeScript, production / test lines | 131,887 / 248,316 |
| Workspace crates | 30 |
Control methods / daemon routes / desktop commands |
255 / 253 / 250 |
| SQLite tables / migrations | 76 / 88 |
| Daemon event kinds | 59 |
| Largest production file | crates/jam-manager/src/manager.rs, 51,465 production lines |
Commits per week over the repository's history. Weekly volume has climbed through that history and has not tapered off, so the refactor lands on a tree that is still changing fast.
#Codemap
The list groups crates by responsibility. It is not a dependency order: for example, jam-manager depends on jam-core, jam-host, and jam-store, which appear after it. The desktop never links the daemon crates, and just no-daemon enforces that.
Shared foundation
jam-domain: every shared domain type. Identities (IdentityId,PeerKey,ChatId,RuntimeSessionId,RuntimeHostId), records (Peer,HostSession,HostRuntimeConfig), events (EventKind), and the transport catalog (HostTransport::catalog).jam-contract: theControltrait, the one interface every client uses to reach the daemon, plus its DTOs andControlError.jam-wire: route constants, request and response bodies, andWIRE_VERSION.- Leaf utilities that the desktop may also use:
jam-version,jam-service(login service and daemon lifetime lock),jam-setup(executable discovery),jam-tls,jam-secure-store,jam-managed-config,jam-analytics.
Clients
jam-client: implementsControlby sending HTTP over the local socket, with bounded admission and per-route timeouts.jam-ipc: the desktop's typed wrapper overjam-clientand the reconnecting event relay.apps/desktop/src-tauri: the Tauri core. Commands are thin forwards throughjam-ipc.apps/desktop/src: the React app.lib/ipc.tsis the only data import;stores/daemon.tsis the only writer of server-derived state.bins/jam: thebandandjamCLI binaries. The same package buildsjamdwith thedaemonfeature.
Daemon stack
jam-daemon: the axum router that exposes anyArc<dyn Control>over HTTP.jam-manager: the realControlimplementation. Accounts, peers, workers, runtime hosts, workspaces, tasks, questions, permissions, usage, and recovery all live here.Managerinmanager.rsis the center.jam-core: the provider-agnostic engine for one agent. Inbound pipeline, durable queue, mention resolution, outbound send, reply, and ack.jam-transportandjam-transport-band: Band behind small role traits (UserApi,AgentApi,Subscriber,HumanSubscriber), and the one implementation.jam-host: the provider seam. One adapter per coding agent behind theHosttrait, the Docker Sandbox lifecycle, and the runtime tools offered to owned agents.jam-store: theStoretrait with an encrypted SQLite backend and a file backend.jam-usage,jam-discovery,jam-auth,jam-update: usage and cost from provider logs, Nearby discovery, sign-in, and update checks.
bins/jam/src/jamd.rs is the composition root. It selects the principal injected implementations: the store backend, the Band transport, the host adapter for each session (build_host), the room board backend, and the discovery provider. Manager construction and orchestration still pick some concrete services themselves, such as the sandbox remover and WorkspaceManager.
The generated crate graphs show the exact dependency closure of the desktop, the CLI, and the daemon.
#Key objects
Six objects are easy to confuse. Code that treats two of them as one routes, cleans up, or attributes work to the wrong thing. Domain and authority covers them in full.
- A Band agent (
IdentityId) is the permanent identity on Band. - A peer (
PeerKey,profile/scope) is Jam's local record for one Band agent under one account. - A host session binds a peer to exactly one room. A host session with no room is a runtime template.
- A runtime session (
RuntimeSessionId) is one local runtime context, independent of display names and rooms. - A runtime host (
RuntimeHostId) is one execution boundary: a provider process and, for Docker, one microVM. A Shared host serves several runtime sessions of one agent. - A provider thread is the provider's own conversation ID inside that process.
#Invariants
These rules hold across the codebase. Several are enforced by a type, a test, or a CI check, as noted.
- The desktop is a client. It configures agents and sends lifecycle requests to
jamd. It never starts provider processes and never compiles the daemon crates (just no-daemon). - Secrets stay in Rust. Tokens and keys are held by
SecretStore, which prefers the OS keychain and falls back to permission-restricted files when no keychain is available. The WebView never receives one. - Every desktop command is allow-listed.
collect_commands!and both isolation allowlists must list the same commands, or the isolation layer rejects the call before Rust sees it. - Events are hints, queries are truth. The event stream is best-effort. Clients re-hydrate after any gap.
- Delivery is at-least-once. An inbound message is durable before Band is told it is processing, and it is removed only on an explicit ack or reply.
- Unrouted work waits. A message for a room that no session owns is queued, never sent to a guessed runtime.
- Destructive operations are journaled. Workspace operations, whole-host cleanup, checkpoint payload deletion, and continuity resets write durable intent before their side effects, and startup replays them before workers start. Other multi-step flows, such as assigning work, can leave partial results on Band with no local replay; they report what succeeded instead.
- Destructive actions carry their own authority. Archive, force discard, export deletion, continuity reset, and whole-host cleanup each re-inspect at the moment of mutation and require their own confirmation. An inspection never grants reusable permission.
- Unrecoverable provider state fails closed. A bound provider identity with missing or corrupt state does not silently start a fresh thread.
- The manager lock is never held across
.await. - Stock provider executables only. Jam runs the user's installed
codex,copilot, orclaude, or Docker's stock agent image. It never bundles a custom provider binary. - Band owns social state. Rooms, membership, messages, and identities live on Band. Jam caches them.
#Cross-cutting concerns
- Consistency and recovery. Journals, replay order, and the event model are in Consistency and recovery.
- State machines. Every lifecycle and its owner is in State machines.
- Errors.
ControlErrormaps to HTTP statuses injam-daemonand back injam-client, then to user-facingIpcErrormessages injam-ipc. - Observability.
jamdwrites a size-rotatedlogs/jamd.logand one event log per peer. Managed operations log throughjam_core::ManagedOperationLogwith a UUID per attempt and immutable owner IDs, and never log secrets, paths, or provider content. - Testing.
crates/jam-manager/tests/lifecycle.rsexercises a realManageragainst real stores and fake transports and hosts.crates/jam-daemon/tests/roundtrip.rscovers routes through the real client. Host-only tests that need Docker or a real provider are ignored by default and gated byJAM_LIVE_*variables. Packaged desktop journeys run through WebDriver inapps/desktop/e2e/real-agents. - Extending Jam. What adding a harness or a feature touches today is measured in Extension seams.
#Reading paths
- New to the codebase: this file, then Context and boundaries, Runtime topology, Domain and authority, and Key flows. Or run
just architecture-siteand open the interactive report. - Adding a coding-agent harness: Providers and attached agents, then Extension seams, then Execution and lifecycle.
- Adding a desktop feature backed by the daemon: Clients, packaging, and updates and the Control API crosswalk.
- Investigating a stuck message or a missed event: Messaging and room state and Consistency and recovery.
- Changing Docker, workspaces, or cleanup: Sandbox, workspaces, and continuity.
- Planning the refactor: Extension seams, Documentation drift, then the Refactor notes section at the end of every subsystem chapter. Future directions compares the options for each area and recommends one; the interactive report's direction picker lets the team weigh and combine them.
Rules for changing code live in AGENTS.md. Product direction lives in VISION.md.