Literium — lit Protocol

Literium — lit Protocol

About this document. Normative reference for the lit programmatic API: the operation catalog, message envelope, session lifecycle, and error model that editor integrations and other tools bind against. This is the API contract; the format it operates on (the .lit repository, objects, segmentation) is SPEC.md. Rationale and rejected alternatives live in HISTORY.md, the internal decision log, §16; when this document and HISTORY.md disagree on a normative point, this document wins.

Status: implementation in progress (first slice landed 2026-07-08). lit serve — the JSON-RPC/stdio transport (§3.2) — carries the session lifecycle (§4) and the methods log, show, status, diff, add, and commit; the remaining surfaces (histories and merge beyond log/show, the review methods, blame, checkout/tabula, maintenance, exchange, and remote) and the napi in-process embed are the ongoing build, both over the one operation layer (§11). Sections marked (open) are deliberately unresolved. The method names are the git peer register of TERMINOLOGY.md §1.1 (HISTORY §15).


0. Conventions

0.1 Terminology (English, set in stone)

The protocol's English is its stable technical vocabulary — deliberately plain, drawn from what each practice already calls things: git verbs for version-control operations (§7; HISTORY §15), and standard copyediting / proofreading terms for the editorial surface that git has no words for. The Latin/Greek register (SPEC, TERMINOLOGY) is a parallel philological layer, recorded but not yet settled; nothing in this protocol depends on it. These English terms are fixed:

Protocol term Meaning Register (deferred)
comparison (diff) the differences between two versions collatio / antibolē
change one difference
move an atom or paragraph relocated transpositio
insertion an atom present on one side only additio
deletion an atom removed omissio
substitution an atom whose content changed substitutio
split a paragraph divided divisio
join paragraphs merged coniunctio
variant two sources' competing readings of one slot varia lectio / diaphōnia
reading one source's text at a variant lectio
source the hand behind a reading or change siglum
review working through the variants recensio
keep / accept / both / revise resolve a variant (revise = write a new reading) stet / adopt / — / emendatio

Kind (the change kinds) is exactly move | insertion | deletion | substitution | split | join (plus unchanged). The editorial method names follow: variants, resolveChange.


1. Purpose and scope

This protocol is the stable surface between the Literium engine and the programs that drive it — editor extensions foremost, but equally any script, service, or daemon that needs repository operations without parsing human CLI output.

In scope: every repository and remote operation the lit CLI performs, expressed as typed request/response methods; the session handshake that fixes the workspace and negotiates version and capabilities; progress and cancellation for long operations; a stable error model.

Out of scope: (a) the repository format — object types, segmentation, hashing — which is SPEC.md and which this protocol never re-specifies, only exposes; (b) the in-editor text-intelligence surface — atom-boundary highlighting, diff/blame decorations — which is an LSP-flavored layer powered client-side by a WASM build of literium-core, specified separately (HISTORY §16); (c) the Litorium platform HTTP APIs, which the remote methods here consume but do not expose.


2. Design principles

  1. Contract over transport. The operation catalog (§7) is defined once, independent of how bytes travel. §3 lists the transports; a conformant server MAY implement any subset but MUST implement the same catalog and types over each.
  2. git verbs as method names. The method set is the git peer register (HISTORY §15). A developer who knows git knows the API surface without learning literary vocabulary.
  3. Typed and prompt-free. A method never prompts, never writes to a terminal, and never returns human prose as its payload. Every result is a schema (§6–§7). Interactive CLI behaviors are lowered to parameters and progress (§9).
  4. Deterministic to the engine. A method's result is a function of the repository state and its parameters, matching the engine the CLI uses. The two surfaces call the same operation functions (§11).
  5. Versioned and negotiated. The session handshake (§4) returns a protocol version and a capability set; clients target those, not undocumented behavior (§10).

3. Transports

A transport carries JSON-RPC 2.0 messages. Three are defined; in-process and stdio are normative for v1, the socket form is (open).

3.1 In-process (napi) — primary

The engine is embedded as a native Node addon (napi-rs). Each method is an async function whose single argument is the params object and whose resolved value is the result object; a JSON-RPC error becomes a thrown error carrying code, message, and data. Notifications (progress, §8) are delivered through a callback registered at initialize. This is the path the VSCode extension uses: no framing, no serialization boundary beyond the napi marshaling, lowest latency.

3.2 JSON-RPC 2.0 over stdio (lit serve) — normative

lit serve reads requests from stdin and writes responses to stdout, each message framed with an LSP-style header block:

Content-Length: <N>\r\n
\r\n
<N bytes of UTF-8 JSON>

stderr carries only diagnostics and is never part of the protocol. This is the path for editors that cannot load a native addon, for remote/sandboxed engines, and for language-agnostic tooling. lit serve speaks exactly the §7 catalog.

3.3 Socket / WebSocket — (open)

A framed TCP or WebSocket transport for out-of-process or networked engines. Framing and authentication are unspecified in v1.


4. Session lifecycle

A session is stateful: it fixes the workspace root and holds the negotiated capabilities. The lifecycle is initialize → (methods…) → shutdownexit, mirroring LSP's shape (the one borrowing this protocol makes).

4.1 initialize (request)

MUST be the first message. Calling any other method first fails with NotInitialized.

Params

{
  "clientInfo": { "name": "lit-vscode", "version": "0.1.0" },
  "workspaceRoot": "/home/writer/novel",
  "capabilities": { "progress": true, "cancellation": true },
  "protocolVersion": "0.1"
}

workspaceRoot is the directory the session operates in; the engine walks upward to the nearest enclosing .lit/ exactly as the CLI does (SPEC §1.3), so workspaceRoot MAY be any path inside a repository, or an as-yet-repository-less directory (for a first add). protocolVersion is the highest the client supports.

Result

{
  "serverInfo": { "name": "literium", "version": "1.0.0" },
  "protocolVersion": "0.1",
  "engine": {
    "formatVersion": "1",
    "segmentationVersion": "uax29-16.0.0"
  },
  "repository": { "found": true, "root": "/home/writer/novel" },
  "capabilities": {
    "methods": ["add", "commit", "status", "…"],
    "remote": true,
    "progress": true,
    "cancellation": true
  },
  "account": { "signedIn": true, "email": "…", "handle": null }
}

The server returns the protocolVersion it will actually speak (≤ the client's; §10). repository.found is false for a directory with no enclosing .lit/ — legal, since add creates one. capabilities.remote gates the §7.6 methods. account reflects ~/.config/lit/credentials.

4.2 initialized (notification)

The client sends this once after processing the initialize result. The server MAY defer expensive setup until it arrives.

4.3 shutdown (request) / exit (notification)

shutdown asks the server to stop accepting new work and returns null; exit ends the session. Over stdio, exit terminates the process; in-process, it releases the engine handle.


5. Envelope and errors

Requests, responses, and notifications are standard JSON-RPC 2.0. Errors use the standard object { "code": <int>, "message": <string>, "data"?: <any> }.

Standard codes (JSON-RPC): -32700 parse error, -32600 invalid request, -32601 method not found, -32602 invalid params, -32603 internal error.

Literium codes occupy -33000 … -33099. data carries a machine-readable detail object where noted. The catalog:

Code Name Meaning
-33000 NotInitialized a method arrived before initialize
-33001 NotARepository no enclosing .lit/ and the method needs one
-33002 NoDocuments add found nothing to snapshot
-33003 NothingPending add/commit with no pending change
-33004 DirtyTree an operation would overwrite unrecorded working-tree changes; data.paths lists them (use force)
-33005 UnknownVersion a version hash or prefix resolves to nothing; data.given
-33006 AmbiguousVersion a hash prefix matches more than one; data.matches
-33007 UnknownHistory no history by that name
-33008 InvalidHistoryName an experiment name breaks the rules (SPEC §9.2); data.reason
-33009 (reserved) was Conflict; a merge's variants are a structured result (merge.apparatus), never an error — HISTORY §17
-33010 NoCoordinate a blame/stemma path query lacks a coordinate
-33011 SegmentationMismatch the repository's segmentation version differs from the engine's; data.repo, data.engine
-33012 IntegrityError fsck found a broken object; data.objects
-33020 NoRemote a pull/publish/share on a repo with no .lit/litorium
-33021 NotSignedIn a remote method needs an account; run account.login
-33022 RemoteRejected the platform refused; data.status, data.message
-33023 Diverged pull is not a fast-forward; push first
-33024 NoHandle opus needs an account handle
-33030 Cancelled the request was cancelled (§8)
-33031 Unsupported the method is not in this server's capabilities

An error MUST be one of these or a standard code; servers MUST NOT invent ad-hoc codes.


6. Common types

VersionHash   = string          // 64 lowercase hex
VersionRef    = string          // a 64-hex hash, a 6–63-hex unique prefix,
                                 // or a history name (resolved: SPEC §8, latest)
HistoryName   = string          // "historia" or an experiment name
DocPath       = string          // repository-relative, forward slashes
Mode          = "prose" | "poetry"
Coordinate    = string          // "gNaM" (grex N, atom M) or "aK" (flat atom K)
EditCategory  = "formatting" | "structural" | "substantial" | "re-segmentation"
Timestamp     = string          // RFC 3339 UTC
Inscriptio   = { version: VersionHash, history: HistoryName,
                 timestamp: Timestamp, message: string, ordinal: number }

PendingRevision = { grex: number, category: EditCategory,
                    summary: string }        // one prepared revision on the tabula

RepoStatus   = { repository: DocPath, historiaTip: VersionHash | null,
                 pending: PendingRevision[], manifest: ManifestChange[] }

ManifestChange = { kind: "add" | "delete" | "rename",
                   path: DocPath, from?: DocPath }

DocumentTrio = { path: DocPath, volumen: VersionHash,
                 spina: VersionHash, atomSpina: VersionHash }

Version      = { hash: VersionHash, segmentation: string,
                 documents: DocumentTrio[], manifest: Record<DocPath, VersionHash> }

AtomLine     = { coordinate: Coordinate, hash: VersionHash, text: string }

Kind         = "unchanged" | "move" | "insertion" | "deletion"
             | "substitution" | "split" | "join"    // change kinds; §0.1, SPEC §7.4

Change       = { kind: Kind, left?: Coordinate, right?: Coordinate,
                 hash?: VersionHash, text: string,
                 words?: Change[] }   // a substitution may carry a
                                      // word-level comparison (SPEC §7.4)

Reading      = { source: string, text: string, hash?: VersionHash } // a source's
                                                                     // reading (source = the hand)
Variant      = { location: Coordinate, kind: Kind,
                 current: Reading, readings: Reading[] }  // a substitution two
                                                          // sources contest

StemmaRecord = { version: VersionHash,
                 firstInscribed?: Inscriptio, lastInscribed?: Inscriptio,
                 path?: DocPath, coordinate?: Coordinate }

Coordinate grammar and the diff line taxonomy (move/split/join detected exactly by atom hash) are SPEC §7.4; edit categories are SPEC §7.2.


7. Operation catalog

Each method: ParamsResult, then notable errors. Absent optional params take their default. Every method MAY additionally raise NotInitialized, SegmentationMismatch, or -32603 internal.

7.1 Working tree and versions

addconscribe

Snapshot the working tree onto the tabula. Creates the repository on first use.

commitinscribe

Finalize the tabula (or a named source version) onto a history.

status

checkout / restoreexscribe

Materialize a version into the working tree (whole version, or one document). Not a pointer move.

tabula

Inspect or discard the tabula.

7.2 Histories and merge

logshow (history)

The inscriptiones of a history, in order.

showshow (version)

A version's structure, or one document's atoms.

branchexperiment

Manage experiment histories.

mergecontexe

Combining merge: weave a source into the working tree, then the caller adds and commits.

variants (git peer of the CLI's conflicts)

List the open variants (the changes still to review), or clear them.

resolveChange

Resolve one variant. The four moves: keep the current reading, accept the other, both, or revise (supply a new reading).

7.3 Inspection

diff

Compare two versions, or the tabula / working tree against the latest.

blamestemma

A version's life-path, or an atom's first/last inscription. (The peer keeps stemma's argument shape, not git blame's — HISTORY §15.)

7.4 Maintenance

gc

fsck

7.5 Correspondence

exemplar

Pack a version's documents (+ .exemplar metadata) for correspondence, or repack an unpacked exemplar.

collateconfero

Ingest a returned exemplar into the working tree.

7.6 Remote (litorium) — capability-gated

All require capabilities.remote; otherwise Unsupported. All but clone/account.login require a bound remote (NoRemote) and, unless anonymous, a signed-in account (NotSignedIn). Long ones report progress (§8).

clone

push

pull

publish

Contribute the historia's (or one version's) atoms to the public corpus. Permanent.

share

shares

opus

opera

retract

account.login / account.handle / account.status / account.logout

The sign-in surface. account.login is progress-driven (§9): it emits the user-code and the verification link as progress, and returns when the link is clicked or the poll times out.


8. Long operations: progress and cancellation

Methods that can take noticeable time — clone, push, pull, publish, account.login, merge on a large tree — MAY emit progress and MUST be cancellable when the client advertised progress / cancellation at initialize.

Progress is the notification $/progress:

{ "token": "<request-id>", "value": { "kind": "report", "message": "uploading pack 3/7", "percentage": 42 } }

with kind"begin" | "report" | "end"; token is the id of the in-flight request. In-process, the same values arrive on the initialize-registered callback.

Cancellation is the notification $/cancelRequest { "id": <request-id> }. On receipt the server SHOULD stop and answer the original request with the Cancelled error. Cancellation is best-effort: a push already committed on the remote completes; the response then reflects the true state.


9. De-prompting rules

The CLI is interactive; the protocol is not. The lowering is mechanical and normative:


10. Versioning and capabilities

protocolVersion is MAJOR.MINOR. A server MUST speak any MINOR ≤ its own within the same MAJOR and MUST return in initialize the version it will actually use (the min of client and server). New methods and new optional fields are MINOR bumps; removing a method, renaming a field, or changing a result shape is a MAJOR bump. A client MUST tolerate unknown fields in results (forward-compat) and MUST consult capabilities.methods rather than assume a method exists — a server built without the litorium feature omits §7.6 and sets capabilities.remote: false, and calling an absent method yields Unsupported, never a silent no-op.

This document is protocol 0.1: unstable, pre-implementation. The first shipped binding freezes 1.0.


11. Relationship to the CLI and the engine

The CLI and this protocol are two front-ends over one operation layer in literium-rs — a set of functions that take typed inputs and return typed results (the §6–§7 shapes). The CLI adds argument parsing, prompts, and text rendering on top; a transport binding (§3) adds marshaling on top. Neither owns the operations. Consequences the implementation MUST preserve: a method's result is the same data the CLI renders (so the surfaces cannot drift); the git method names are the CLI's git peers (HISTORY §15), so the vocabulary is shared end to end; and the engine's determinism (SPEC §5) means a method is a pure function of repository state and params, with the sole side effects being the documented writes to .lit/, the working tree, and the remote.

The conformance oracle (literium-perl) scopes to the format and the extraction/derivation objects (HISTORY §14), not to this protocol; protocol conformance is checked against this document by the binding's own tests.