JSON-RPC 2.0 over a Unix domain socket. Used by the seqforge CLI and
by external agents to drive a running SeqForge GUI process. Stage 2.5d.
- Protocol: JSON-RPC 2.0, newline-delimited (one request per line, one response per line).
- Transport: Unix domain socket. Path is published in the
SEQFORGE_SOCKETenvironment variable (set in the SeqForge embedded terminal automatically; agents launched outside the GUI must read it from a wrapper or be told). - Path format:
/tmp/seqforge-<pid>.sock. One socket per GUI process; the path is removed when the GUI exits. - Concurrency: each accepted connection runs in its own thread. Requests on a single connection are processed in order; the GUI's applier serializes everything to one mutation site so cross-connection request ordering is well-defined.
Request envelope:
{"jsonrpc":"2.0","id":<any>,"method":"<method-name>","params":{...}}Response envelope (success):
{"jsonrpc":"2.0","id":<echoed>,"result":{"kind":"<variant>",...}}Response envelope (error):
{"jsonrpc":"2.0","id":<echoed>,"error":{"code":<int>,"message":"..."}}id is echoed verbatim and supports any JSON value (numeric, string,
null). Notifications (no id) are not supported — every request
gets a reply.
Open a file in the viewer. Workspace-scoped: creates a new View and
its Buffer (or switches to an existing tab if the file is already
open by path). Replaces any Tab::Welcome placeholder.
{"method":"open","params":{"path":"/abs/path/plasmid.gb"}}Response: {"kind":"ok"}.
Close the active view. Drops the underlying buffer if no other view references it. ⌘W equivalent.
{"method":"close","params":{}}Response: {"kind":"ok"}. Errors with NoActiveView if nothing's open.
Navigate to a 1-based sequence position. Scrolls + places a cursor.
{"method":"goto","params":{"position":1234}}
{"method":"goto","params":{"position":1234,"view":17}} // explicit targetResponse: {"kind":"navigated","position":1234}. Errors with
OutOfRange if position is 0 or exceeds the sequence length.
IUPAC pattern search on both strands; the first hit becomes the selection.
{"method":"find","params":{"pattern":"GAATTC","mismatches":0}}
{"method":"find","params":{"pattern":"GAATTC","view":17}} // explicit targetResponse: {"kind":"search_results","count":N,"hits":[{...}]}.
Empty pattern clears results.
Show restriction sites for the named enzymes. Names matched case-insensitively against the bundled enzyme library; unknown names are silently skipped.
{"method":"enzymes","params":{"enzymes":["EcoRI","BamHI"]}}
{"method":"enzymes","params":{"enzymes":["EcoRI"],"view":17}} // explicit targetResponse: {"kind":"cut_sites","count":N,"sites":[{...}]}. Empty
enzymes clears the cut-site overlay.
View-scoped methods (goto, find, enzymes) accept an optional
view: <ViewId> parameter:
- Omitted (default): operates on the workspace's currently active
view (
workspace.active_view). Equivalent to clicking that tab and invoking the action by hotkey. - Provided: operates on the view with that id explicitly. Returns
ViewNotFound(error code-32000, messageview ViewId(N) not found) if the view has been closed since the agent enumerated it.
Agents that operate across multiple open files should:
- Track view ids returned from prior interactions (or extracted from future enumeration RPCs — not yet exposed).
- Pass
view: <id>explicitly to avoid races against user tab switches. - Be prepared to handle
ViewNotFoundand re-enumerate.
There is no pane targeting. After Stage 2.5c/e, panes are a layout
concept owned by egui_dock (split-view tab groups in the dock tree),
not addressable identity. The set of open views is the source of truth
for "what files are open"; how the user has arranged them spatially is
not part of the protocol surface.
Standard JSON-RPC codes plus one app-specific:
| Code | Source | Meaning |
|---|---|---|
-32700 |
Parse | Body wasn't valid JSON. |
-32600 |
Invalid | Couldn't deliver to the running viewer. |
-32601 |
Method | Unknown method name. |
-32602 |
Params | Params didn't deserialize into the variant. |
-32000 |
App | DispatchError from seqforge_core::dispatch. |
DispatchError variants surfaced under -32000:
NoActiveView— request needs a view, none is active.ViewNotFound(ViewId(N))— explicitviewtarget doesn't exist.OutOfRange { position, seq_len }— forgoto.PoisonedLock— buffer'sRwLockpoisoned (panic in a previous writer; should never happen in single-threaded apply path).BioError(msg)— load / search / cut-site computation failed.Unimplemented(name)— placeholder for future variants.
Each socket connection waits up to 5 seconds for the GUI's applier to process a request. If the GUI is busy beyond that (heavy paint, modal dialog), the client gets:
{"error":{"code":-32000,"message":"viewer did not respond within timeout"}}The request may still complete inside the GUI; clients should not retry a non-idempotent request after a timeout.
The socket is a local control plane, not a network endpoint.
- The socket path is in
/tmp/seqforge-<pid>.sock. On a multi-user Unix host, anyone with read access to/tmpcan see the path; access is gated by filesystem permissions on the socket file itself, which defaults to the owner's umask (typicallysrwxr-xr-x, so write access is owner-only on a normal setup). - A connecting process is implicitly trusted: it can
openarbitrary files (subject to GUI process's filesystem access), trigger arbitrary searches, and read sequence data. Any process running as the same user can do this. - The protocol does not authenticate. There is no shared secret, no capability handshake. Adding one would block agent interoperability for the MVP and offers little real protection on a single-user dev host (the attacker can ptrace the GUI anyway).
- The socket exposes no shell escape: methods take typed params parsed via serde; method dispatch is a closed-enum match in Rust. Adversarial JSON cannot reach arbitrary file paths, exec syscalls, or untyped fields.
Open { path }does not validatepath. A malicious agent could load a 10 GB file and hang the GUI. This is a denial-of-service surface, not a privilege-escalation one — the attacker already had read access to the file.
If/when SeqForge ships a multi-user or networked variant, this
threat model needs revisiting: capability tokens, per-method
allow/deny, sandboxed buffer loaders, rate limiting on find /
enzymes for sequences over some threshold.
The bundled seqforge CLI is the canonical client.
$ seqforge open /path/to/plasmid.gb
$ seqforge goto 1234
$ seqforge find GAATTC
$ seqforge find GAATTC --mismatches 1
$ seqforge enzymes EcoRI BamHI
$ seqforge closeView targeting:
$ seqforge goto 1234 --view 17
$ seqforge find GAATTC --view 17File commands (info, digest, annotate) run locally without a
socket — they read sequence files directly from disk.