Skip to main content

Backend reference

The backend is packages/backend-core (src/server.js and the modules next to it), started by apps/backend for local use and by apps/desktop inside Electron. It is a Node HTTP server with a WebSocket endpoint at /ws. It stores projects and sessions on disk, runs Genie tools, and bridges each chat to an OpenAI Codex CLI app-server process.

Start it with pnpm dev:backend (port 8787 on 127.0.0.1 by default). Configuration variables are listed in Configuration.

caution

The backend has no authentication and sends Access-Control-Allow-Origin: *. It is meant to be bound to 127.0.0.1 on a single-user machine.

HTTP routes​

All JSON routes answer with application/json. Project, session and asset ids are limited to letters, digits, _ and -.

RouteMethodsPurpose
/healthGET{ "ok": true }
/genomesGETdefaultGenomeId and genome summaries from the catalog
/projectsGET, POSTList projects (with recent chats); create a project
/projects/:projectIdGET, PATCH, DELETERead, update (for example the title) or delete a project
/projects/:projectId/assetsGET, PUTRead or replace the project's asset catalog
/projects/:projectId/assets/importPOSTUpload or import assets (uploads up to 100 MB)
/projects/:projectId/assets/:assetId/fileGETDownload an asset's file
/projects/:projectId/analysisGET, PATCHAnalysis-mode state
/projects/:projectId/analysis/artifactGETThe current analysis artifact (index.html)
/projects/:projectId/studioGET, POSTList or create Studio outputs
/projects/:projectId/studio/:outputIdGET, DELETERead or delete a Studio output
/projects/:projectId/studio/:outputId/fileGETA Studio output's HTML file
/projects/:projectId/genomeGET, PATCHThe project's browser state (ProjectGenomeState)
/projects/:projectId/genome/results/:fileGET, HEADA result file written by a tool (BED, TSV)
/projects/:projectId/genome/plugins/:pluginIdGETA compiled custom-track plugin bundle (custom tracks are currently disabled)
/sessionsGETAll chat sessions, newest first
/sessions/:sessionIdPATCH, DELETERename ({ "title": ... }) or delete a chat
/sessions/:sessionId/genomeGET, PATCHBrowser state for a chat without a project (older layout)
/sessions/:sessionId/genome/plugins/:pluginIdGETPlugin bundle for a chat without a project
/sessions/:sessionId/artifacts/:artifactId/index.htmlGETAn HTML artifact made by create_html_artifact in Genome mode
/sessions/:sessionId/images/:fileGETAn image pasted into the chat
/modelsGETModels from the Codex CLI (model/list), cached for 5 minutes
/asset-catalogGET, PUTWorkspace-level asset catalog (older layout)

When GENIE_STATIC_ROOT is set, any other GET or HEAD request is served from that folder, and HTML requests for app routes (such as /projects/<id>) get the frontend's index.html. HTML files are sent with Cache-Control: no-store; other static files are cached for a year.

GET /models​

The backend starts a short-lived app-server, sends initialize and model/list, then stops it. The result is cached for 5 minutes. If the call fails and nothing is cached, the route returns a fallback list (gpt-5.5 as the default, gpt-5.4, gpt-5.4-mini).

PATCH /projects/:projectId/genome​

The body may contain tracks (array), hiddenTrackIds (array, filtered to existing track ids), plugins (array) and viewRegion (locus, start, end). genomeId may only repeat the project's genome; any other value is rejected with "Project genome cannot be changed. Create a new project with the desired genome."

WebSocket protocol​

Connect to ws://<host>:<port>/ws. Every message is a JSON object with a type and usually a payload.

Client to server​

TypePayloadEffect
initSee belowStarts the app-server handshake. Sent once per connection.
send{ text, images?, model?, effort? }Starts a turn. model and effort override the session's settings from this turn on.
abortNoneSends turn/interrupt for the current turn.
tool_result{ requestId, callId?, output, success }Answers a tool_call_request that the frontend handled.
tool_input_response{ requestId, answers }Answers a tool_input_request.
approval_response{ requestId, decision }Answers an approval_request.

The init payload sent by the frontend:

{
"type": "init",
"payload": {
"sessionId": "<chat id>",
"projectId": "<project id>",
"sessionMode": "regular",
"model": "<model id or null>",
"effort": "<effort or null>",
"tools": [{ "name": "genome_navigate", "description": "...", "inputSchema": {} }],
"developerInstructions": "<mode instructions>",
"autoApprove": true
}
}

sessionMode is regular (Genome mode), analysis (plan is accepted as an alias) or asset-import. tools are the mode's declared tool schemas; the backend passes them to the app-server as dynamic tools. The backend appends its own instruction to the base instructions: work only inside the session's directory when creating or modifying files.

Server to client​

TypePayload
connected{ sessionId }, sent as soon as the socket opens
ready{ sessionId }, sent after the handshake (and thread resume, if any)
eventA chat event, see below
tool_call_request{ requestId, callId, tool, arguments, threadId, turnId } for tools the backend does not handle
approval_request{ requestId, kind, params } where kind is command, file, patch or exec; only sent when autoApprove is off
error{ message }

Event payload types: thread_started, message_started, message_delta, message_completed, tool_call, tool_progress, tool_result, tool_input_request, tool_input_resolved, status (thinking or idle) and error.

The frontend's WebSocketChatTransport waits up to 30 seconds for ready. If it does not arrive, the chat shows "The assistant runtime did not finish initializing within 30 seconds."

CodexSession lifecycle​

Each WebSocket connection creates one CodexSession, and each CodexSession creates one CodexAppServerClient, which spawns <GENIE_CODEX_COMMAND> app-server with piped standard input and output. Messages are newline-delimited JSON-RPC.

StepJSON-RPCDetails
Handshakeinitialize, then notification initializedclientInfo { name: "genie", title: "Genie Chat" }, capabilities.experimentalApi: true
Resumethread/resume, then thread/readOnly if sessions.json holds a thread id for this chat. Replayed messages get their stored citations reattached.
First messagethread/startmodel, cwd (the session folder), approvalPolicy, sandbox, baseInstructions, developerInstructions, dynamicTools. approvalPolicy and sandbox are sent as null unless set; config is always null.
Each messageturn/startinput (text and images), model, effort, and sandboxPolicy { type: "workspaceWrite", networkAccess: true, writableRoots: [session folder] }
Stopturn/interruptFrom the abort message
Modelsmodel/listSeparate short-lived client for GET /models

Notifications the backend translates into events include thread/started, turn/started, turn/completed, item/started, item/completed, item/agentMessage/delta, item/commandExecution/outputDelta, item/fileChange/outputDelta, item/mcpToolCall/progress and error. These cover the model's messages and the runtime's built-in items (shell commands, file changes, MCP tool calls), which appear in the chat as tool cards.

Approvals​

Server requests item/commandExecution/requestApproval, item/fileChange/requestApproval, applyPatchApproval and execCommandApproval are answered with accept when autoApprove is true. The frontend always sends autoApprove: true, so no approval request reaches the user. Genie does not turn off the runtime's built-in shell or web search. See Runtime and safety.

Tool execution​

The app-server sends item/tool/call with callId, tool and arguments. CodexSession.handleServerRequest handles it:

  1. Sends a tool_call event (status started) to the frontend and logs tool_call_received.
  2. Dispatches by tool name. Most genome tools (genome_navigate, genome_add_track, genome_describe_viewport, genome_signal_stats, genome_call_peaks, genome_quantify_at_features, genome_correlate_tracks) and public_data_search go through runGenomeToolForProject, which reads the project's browser state (after pulling it from the cloud project API if GENIE_CLOUD_API_URL is set), runs the handler in src/genome/toolHandlers.js, and adds citations. The other genome tools and the asset, analysis and artifact tools have their own handlers in server.js.
  3. Writes any new citation records to the chat's citations.json.
  4. For genome_navigate, genome_add_track, genome_call_peaks and genome_quantify_at_features, pushes the new browser state to the cloud project API when one is configured.
  5. Answers the app-server with { contentItems: [{ type: "inputText", text: <JSON> }], isError } (plus legacy output and success fields) and sends a tool_result event to the frontend.
  6. Logs tool_call_completed with the elapsed time.

A thrown error becomes a failed tool result whose text is the error message. Unknown names starting with genome_, asset_catalog_ or project_ fail with "Unsupported … tool". Any other unknown name is forwarded to the frontend as a tool_call_request.

The tools themselves are described in the Tool reference.

On-disk layout​

The session data root is genie/session-data/ by default (GENIE_SESSION_DATA_ROOT), or session-data/ in Electron's user-data folder for the desktop app.

session-data/
projects.json project list
sessions.json chat list (title, preview, mode, thread id, project id)
assets/asset-catalog.json workspace-level catalog (older layout)
projects/
<projectId>/
assets/
asset-catalog.json project asset catalog
<assetId>/
metadata.json
artifacts/ files written by project_write_asset_artifact
analysis/
state.json Analysis-mode state
artifact/index.html
studio/<outputId>/index.html Studio outputs (reports, tables, saved views, ...)
genome/
browser-state.json ProjectGenomeState
results/ peaks-*.bed, quantify-*.tsv
plugins/ custom-track plugin sources and bundles (disabled)
sessions/
<sessionId>/
app-server.log JSON lines: every app-server message and tool event
citations.json citation records for this chat
artifacts/<artifactId>/index.html
input-images/ images pasted into the chat

Chats created without a project use session-data/<sessionId>/ with the same per-chat files.

app-server.log​

One JSON object per line with timestamp, sessionId, projectId, direction (outbound, inbound or internal), channel (app-server or backend) and either the JSON-RPC message or a backend event (tool_call_received, tool_call_completed with elapsedMs). The log records every request and response exchanged with the app-server, so it is the primary record of a session.

caution

The log contains the full conversation, tool arguments and results, and runtime details such as local file paths. Review and redact it before sharing.

citations.json​

{
"schemaVersion": 1,
"updatedAt": 1767225600000,
"citations": [
{
"citationId": "cite_...",
"citationToken": "[[cite:cite_...]]",
"sourceType": "genome-computation",
"projectId": "<projectId>",
"title": "Genome call peaks result",
"excerpt": "{ ...result JSON, up to 1,800 characters... }",
"genome": { "genomeId": "hg38", "locus": "chr8:127700000-127760000", "start": 127700000, "end": 127760000 },
"createdAt": 1767225600000
}
]
}

Other fields a record may carry: assetId, url, browserUrl, location, fileName, lineStart, lineEnd, genome.trackId and metadata. The file is rewritten atomically (temporary file, then rename). See Citations and evidence.

browser-state.json​

The project's ProjectGenomeState, served by GET /projects/:projectId/genome and changed by PATCH and by the mutating genome tools.

FieldTypeNotes
schemaVersionnumber1
genomeIdstringFixed when the project is created
viewRegion{ locus, start, end }locus is a display string such as chr8:127,700,000-127,760,000
tracksTrackModel[]id, type, name, url, optional label, heightPx, options, metadata, indexUrl, ...
hiddenTrackIdsstring[]Optional; tracks hidden in the panel
pluginsarrayCustom-track plugin records (empty while custom tracks are disabled)
updatedAtnumberMilliseconds since the epoch

A new project's state is created from the genome catalog: the default region and default tracks of its genome. The file is written atomically.

Result files​

genome_call_peaks writes genome/results/peaks-<trackId>-<8 characters>.bed, a tab-separated file with chr, start, end, peak_<n> and score, and adds it to the browser state as a bed track whose url is the backend-relative path /projects/<projectId>/genome/results/<file>. The track's metadata records the parameters (trackId, scope, method, threshold, minWidth) and the source track ids.

genome_quantify_at_features writes genome/results/quantify-<signalTrackId>-at-<featureTrackId>-<8 characters>.tsv with a header row:

rank chr start end name featureScore mean

The last column is named after the chosen metric (mean, sum or max). The quantify tool does not add a track.

note

In earlier testing, the BED tracks added by the peak tool did not draw in the eg3 panel ("Error detecting chromosome naming. Check URL and file format."). The files themselves are complete and can be downloaded from the results route.