Domain logic lives in the library crate codepapr-core. The only process that serves it is the codepapr-server host daemon; the desktop app (Tauri) and the CLI are both JSON-RPC clients. Rationale: docs/adr/ADR-012; full detail: docs/ARCHITECTURE.en.md.
Four kinds of executables: clients (codepapr / codepapr-cli), the host (codepapr-server), the Node agent sidecar (agent-runtime.mjs, launched by the host), and the child processes the host manages (LSP servers, shells, MCP stdio servers).
Desktop client codepapr CLI codepapr-cli
React+Monaco · @codepapr/core ping/doctor/status/git/fs/
src-tauri/src/host.rs (RPC client) shell/lsp/server/chat
│ JSON-RPC 2.0, line-delimited │
│ TCP 127.0.0.1 (or stdio) │
└────────────────┬─────────────────────────┘
▼
codepapr-server (host)
handler.rs → initialize / ping + 159 namespaced methods
tasks.rs → serial task queue
▼
codepapr-core (domain library, no Tauri dependency)
workspace_fs · git_operations · shell · lsp
symbols · db(SQLite) · snapshot · mcp_host · web
agent_runtime → Node agent-runtime.mjs sidecar
CODEPAPR_SERVER_URL set → connect only, no child process. Otherwise locate the binary (CODEPAPR_SERVER_BIN → next to the executable → resourceDir → target/{debug,release} → PATH) and run codepapr-server --port 0 --port-file <temp>/codepapr-server-<pid>.port; the port is read from the port file or the stderr line [codepapr-server] listening on (15s deadline, 40ms poll), then initialize is sent.
lsp/stopAll → agent/stopAll → fs/stopWatcher → shell/stopAllBackground → mcp/disconnectAll → kill + wait (only for a self-spawned child; an external host via CODEPAPR_SERVER_URL is left running). The desktop waits for settings to flush first (polls db/settingsSaveState).
| Layer | Owns | Does not own |
|---|---|---|
| UI (React) | Interaction, panels, driving the agent loop, prompt assembly | Any direct file / process / database access |
| Tauri client | host.rs RPC client, commands.rs proxies; GUI-only: codepapr-app://, embedded/headless browser, TTS, Stronghold vault, file export, app install & port probing | Filesystem, Git, shell, LSP, MCP, snapshots, web, the main SQLite DBs |
| codepapr-server | Method routing, param validation, event broadcast, in-memory secrets, serial task queue | Domain implementation (delegated to core) |
| codepapr-core | Every domain implementation and child-process lifecycle | UI, protocol layer |
Two deliberate exceptions where the desktop crate bypasses RPC via a direct path dependency on codepapr-core: Papr App storage (papr_runtime/app_storage.rs calls db::papr_storage_* directly) and secrets (plaintext lives only in the client Stronghold vault, pushed one-way to the host's in-memory store via secrets/import; the host never persists secrets).
Line-delimited JSON-RPC 2.0, one object per line. --port <N> → TCP (--port 0 lets the kernel pick, --port-file writes the bound port); no --port → stdio. Requests with an id get a response; without one they are notifications. On disconnect, every pending request fails with codepapr-server closed the connection — there is no automatic reconnect.
| Namespace | Methods | Representative |
|---|---|---|
fs/* | 23 | readTextFile · writeTextFile · listFiles · search · startWatcher |
git/* | 8 | status · diff · log · stage · commit · branchCheckout |
shell/* | 17 | execute · startBackground · openSession · sendCommand |
lsp/* | 12 | startServer · request · diagnostics · batchSymbols |
symbols/* | 8 | definition · references · hover · checkSyntax |
db/* | 45 | loadSettings · saveMessageBatch · paprStorage* |
snapshot/* | 11 | ensure · create · diff · restorePlan · restoreExecute |
mcp/* | 10 | listTools · callTool · updateSettings · disconnectAll |
agent/* | 5 | start · send · stop · respondPermission |
web/* | 3 | search · fetchUrl · downloadFile |
task/* | 2 | enqueue · poll |
secrets/import | 1 | Populate in-memory secrets |
159 namespaced methods total plus initialize / ping; anything unrecognized returns Unknown JSON-RPC method. Events ride {"method":"event","params":{"event","payload"}} notifications and are re-emitted verbatim on the Tauri event bus, names unchanged: workspace-files-changed, project-stats-progress, codepapr://lsp-managed-status, agent-runtime://frame/exit/permission-request/permission-cancel/workspace-mutated, mcp-confirm-request.
React → invoke() → commands.rs
→ host::call("fs/readTextFile")
→ TCP → handler.rs → validate
→ codepapr_core::workspace_fs
→ result returns to UIwatcher.emit("workspace-files-changed")
→ server wraps as event notification
→ host.rs dispatch_incoming_line
→ app.emit(event, payload)
→ React listen(event, ...)| Database | Path | Contents |
|---|---|---|
| App DB | ~/.codepapr/codepapr.sqlite | Global settings, provider/model, recent workspaces |
| Project DB | <workspace>/.CodePapr/project.sqlite | Sessions, checkpoints, ProjectGraph cache |
| Papr App DB | <appDir>/db.sqlite | Key-value storage and inbox for one .papr app |
All three are opened by codepapr-core::db, i.e. the host process writes them (app storage is the one in-process exception). Legacy state.json/project.json import into SQLite on first open or save.
.papr apps install from the official registry mmrqwe/codepapr-apps → registry.json into a global or workspace scope.
~/.codepapr/apps/<appId>/Cross-project tools, global permission defaults.
<workspace>/.CodePapr/apps/<appId>/Project-specific, shippable with the repo; same appId overrides the global install so frontend/backend/storage stay on one directory.
Each app gets one origin codepapr-app://<appId>/; CSP is derived from two permission axes (local access × network) — with network off, script-src disallows https:. db.sqlite (and wal/shm) is never served over the protocol. A directory counts as an app only when its name is valid, manifest.json parses, and the entry file exists.
Side effects are delegated to the host; prompt assembly and cache partitioning stay in @codepapr/core (TS side). Full detail: docs/adr/ADR-001…016.
| Layer | Holds | Changes when |
|---|---|---|
| 01 System core | System prompt, tool schemas, AGENTS.md | Frozen for the session |
| 02 Session bootstrap | Skills catalog, plugin inbox summaries, project memory (MEMORY.md) | Next turn after MEMORY.md is saved or compaction |
| 03 Session state | Checkpoint (task progress) + recent turns | Rewritten on compaction |
| 04 Turn recall | Retrieved procedures for this turn | New every user turn |
| 05 Active tail | Current user message, tool results | Append-only at the tail |
Project memory is authoritative in the workspace file .CodePapr/MEMORY.md: the built-in memory curator maintains it at the delivery / pre-compaction checkpoints, and it is injected into 02 in full every turn (effective the next turn after saving); the Agent has no memory tools and direct writes are rejected. The panel is that file's editor.