API
A local WebSocket server for reading and writing to your GalaxyBrain workspace. Build scripts, automations, and integrations that interact with your pages, templates, and workspace layout.
Contents
Getting Started
1. Install and start the API server
This downloads the server from npm and starts it on port 1924:
npx galaxybrain-api
To use a different port:
GB_API_PORT=3000 npx galaxybrain-api
2. Open GalaxyBrain in the browser
The app automatically connects to ws://localhost:1924 and registers itself as an instance.
3. Connect your client
const ws = new WebSocket("ws://localhost:1924");
ws.onopen = () => {
ws.send(JSON.stringify({
type: "command",
requestId: "1",
cmd: "ORIENTATION"
}));
};
ws.onmessage = (event) => {
const msg = JSON.parse(event.data);
console.log(msg);
};
Architecture
The API has three moving pieces:
- The API server — a local WebSocket server, started with
npx galaxybrain-api. An HTTPGET /returns the health check string"GalaxyBrain API Server/{protocolVersion}"(e.g."GalaxyBrain API Server/1"). The protocol version increments on breaking changes to the WebSocket command/event format. - GalaxyBrain browser tabs — each tab connects to the server and registers as an addressable instance.
- External clients — your scripts, tools, or integrations connect to the same server and send commands.
External clients do not connect directly to the browser tab. Commands are routed through the server to a specific instance, and responses are routed back.
GalaxyBrain instances can be in one of three states:
| State | Description |
|---|---|
picker | No project is open |
folder | A folder-backed project is open |
demo | A demo project is open |
Always available commands
LIST_INSTANCES, LIST_FOLDERS, OPEN_FOLDER, OPEN_DEMO, CLOSE_PROJECT, REMOVE_RECENT_FOLDER, FILES_WATCH, FILES_UNWATCH, SUBSCRIBE, UNSUBSCRIBE
Require an open project
READ_PAGES, CREATE_PAGES, UPDATE_PAGES, DELETE_PAGES, QUERY, READ_TEMPLATES, CREATE_TEMPLATES, UPDATE_TEMPLATES, DELETE_TEMPLATES, CREATE_FROM_TEMPLATE, PUSH_PAGE_ITEMS, PUSH_TEMPLATE_ITEMS, POP_PAGE_ITEMS, POP_TEMPLATE_ITEMS, READ_WORKSPACE, WRITE_WORKSPACE, MAP, ANCESTORS, ORIENTATION
App URL Parameters
gb_port
API server port to connect to. Default: 1924.
?gb_port=3000
gb_id
Instance ID the browser tab registers with. If omitted, a random 6-character ID is generated.
?gb_id=desk-main
Use a stable gb_id when you want to target a specific browser tab from a script, or run multiple tabs simultaneously. If two tabs use the same gb_id, the older one is evicted.
demo
Startup demo selection. An app convenience — API clients use OPEN_DEMO instead.
Transport
Message Format
All messages are JSON objects with a type field. Five message types on the wire:
identify— sent by the browser app to register with the servercommand— sent by external clients to the serverresponse— sent back for commands and identifyerror— server-level routing or protocol errorsevent— server-pushed subscription events
External clients only use the last four. The identify handshake is documented here because it governs instance registration and affects behavior visible to external clients.
Identify (Browser App → Server)
When a GalaxyBrain browser tab connects, it identifies itself:
{
"type": "identify",
"instanceId": "desk-main",
"protocolVersion": 1,
"state": "picker",
"folder": null,
"demo": null,
"offline": false,
"version": "0.5.0"
}
The server responds with:
{ "type": "response", "requestId": "identify", "ok": true, "serverVersion": "0.2.1" }
The serverVersion field contains the API server’s npm package version string.
protocolVersion does not match the server's version, the connection receives a PROTOCOL_MISMATCH error. If another tab is already registered with the same instanceId, the older tab receives an eviction event and is disconnected — the new tab takes over. External clients never send identify.
Command (Client → Server)
{
"type": "command",
"requestId": "req-1",
"instance": "desk-main",
"cmd": "READ_PAGES",
...
}
| Field | Type | Required | Description |
|---|---|---|---|
type | "command" | Yes | Message type |
requestId | string | Yes | Unique ID for correlating the response |
cmd | string | Yes | Command name |
instance | string | No | Target instance ID |
Response (Server → Client)
{
"type": "response",
"requestId": "req-1",
"cmd": "READ_PAGES",
...
}
Every response includes type, requestId, and cmd.
Error (Server → Client)
{
"type": "error",
"requestId": "req-1",
"code": "INSTANCE_REQUIRED",
"message": "Multiple instances connected. Specify \"instance\" field."
}
Event (Server → Client)
{
"type": "event",
"event": "pages_updated",
"seq": 42,
"instanceId": "desk-main",
"timestamp": 1712766240
}
Instance Routing
- One instance connected:
instanceis optional — auto-routed. - Multiple instances:
instanceis required, otherwiseINSTANCE_REQUIRED. - No instances:
NO_INSTANCESerror. - Unknown instance:
UNKNOWN_INSTANCEerror.
Response Patterns
Commands use three distinct response shapes:
1. Singleton
Top-level ok with command-specific fields. Used by: LIST_FOLDERS, OPEN_FOLDER, OPEN_DEMO, CLOSE_PROJECT, QUERY, MAP, ANCESTORS, ORIENTATION, READ_WORKSPACE, WRITE_WORKSPACE, SUBSCRIBE, UNSUBSCRIBE, CREATE_FROM_TEMPLATE.
2. Batch
Top-level results array aligned to input order, each entry succeeds or fails independently. Used by: READ_PAGES, CREATE_PAGES, UPDATE_PAGES, DELETE_PAGES, READ_TEMPLATES, CREATE_TEMPLATES, UPDATE_TEMPLATES, DELETE_TEMPLATES, PUSH_PAGE_ITEMS, PUSH_TEMPLATE_ITEMS, POP_PAGE_ITEMS, POP_TEMPLATE_ITEMS.
3. Parse failure
When the payload is malformed before execution can begin.
Ordering and Delivery
- Commands are processed FIFO per target instance.
- Batch arrays execute in array order — later operations can observe earlier mutations from the same batch.
- For API-triggered mutations, the response is sent first, then any resulting events are flushed.
IDs, Versions & Snapshots
| Concept | Format | Description |
|---|---|---|
| Page ID | [a-zA-Z0-9]{20} | 20-character alphanumeric string. Templates also use page IDs. |
| Block ID | Non-negative integer | Unique within a page. |
| Variable ID | Non-negative integer | Unique within a page. |
| Page Version | Integer | Independent per page. Used for optimistic concurrency via readVersion. |
| Workspace Version | Integer | Independent from page versions. |
snapshotSeq | Integer | Instance's latest committed event sequence at read time. Use for sync detection. |
readVersion compare it against the current version. Match → proceed. Mismatch → CONFLICT error. Omitted or null → skip check.
Data Model
Writable Schemas
These shapes are used when creating or updating pages and templates.
Page Body (Create / Full Replace)
null entries in the pages array create default blank pages.
Item Input
Unit Input
text and templateValue. Formulas accept only unstyled text, metaRef, and templateValue. Subtitle and text item content accept all unit types.
Surgical Update
UPDATE_PAGES and UPDATE_TEMPLATES support surgical block-level edits as an alternative to full replacement:
blocks (full replacement) is mutually exclusive with all surgical fields.
Serialized (Read) Schemas
Read operations return these shapes. Fields marked (read-only) are computed and must not be round-tripped back into writes.
Page
{
"pageId": "AbcDef1234567890GhIj",
"icon": "📄",
"title": [ /* serialized units */ ],
"subtitle": [ /* serialized units */ ],
"blocks": [ /* serialized blocks */ ],
"blockOrder": [0, 1, 2],
"createdAt": 1712766240,
"updatedAt": 1712766300,
"templateValues": { "nextNumber": "5" },
"counts": { "words": 150, "characters": 820, ... }
}
Read-only fields: pageId, blockOrder, createdAt, updatedAt, templateValues, counts.
Block
{
"blockId": 0,
"linkOrder": "A.M.tt",
"lastSelectedTemplateId": null,
"items": [ /* serialized items */ ],
"createdAt": 1712766240,
"updatedAt": 1712766300,
"counts": {
"words": 50, "characters": 280,
"checkboxes": 0, "checkboxesChecked": 0, "checkboxesUnchecked": 0,
"pageLinks": 2, "listItems": 1
}
}
Read-only fields: createdAt, updatedAt, counts.
Serialized Items
indentLevel is only present when > 0 (default 0). orderedListStart is only present when style is "ol".
// Text (indentLevel omitted when 0; orderedListStart only for "ol" style)
{ "type": "text", "style": "", "content": [ /* units */ ], "indentLevel": 2 }
{ "type": "text", "style": "ol", "content": [ /* units */ ], "orderedListStart": 3 }
// Var (value is read-only, computed from formula)
{ "type": "var", "id": 0, "name": "Total", "formula": [ /* units */ ], "value": "150" }
// Image
{ "type": "image", "imageId": "a1b2c3...64hex.png" }
// Page Link (title is read-only, resolved at read time)
{ "type": "pageLink", "pageId": "AbcDef1234567890GhIj", "title": "My Page" }
Read-only fields: var value, pageLink title.
Serialized Units
// Text
{ "type": "text", "text": "Hello", "unitStyle": "bold" }
// Web Link
{ "type": "webLink", "text": "Example", "url": "https://example.com" }
// Page Link (title is read-only)
{ "type": "pageLink", "pageId": "AbcDef1234567890GhIj", "title": "My Page" }
// Meta Ref — success
{ "type": "metaRef", "ref": "M.tw", "value": "150" }
// Meta Ref — error
{ "type": "metaRef", "ref": "V.AbcDef1234567890GhIj.0", "value": null, "error": "NOT_FOUND" }
// Template Value — on template page (no resolved value)
{ "type": "templateValue", "valueType": "dateToday" }
// Template Value — on regular page (resolved)
{ "type": "templateValue", "valueType": "dateToday", "value": "2026-04-10" }
Read-only fields: metaRef.value, metaRef.error, pageLink.title, templateValue.value on regular pages.
Meta ref errors: NOT_FOUND, VAR_MISSING_REFERENCE, VAR_CIRCULAR_REFERENCE.
Normalization
The parser applies these normalization steps to written content:
- Adjacent compatible text and web-link units are merged.
- A lone page-link unit inside an unstyled text item is converted into a standalone
pageLinkitem. - Bullet, checkbox, and ordered-list items keep a lone inline page-link unit as text content.
Link Order
linkOrder controls automatic sorting of page-link items within a block. Format: "{direction}.{sortKey}".
| Direction | Sort Keys |
|---|---|
A (ascending) | M.tt (title), M.ca (created at), M.ua (updated at), M.tw (total words), M.tc (total characters), M.tb (total blocks), M.tli (total list items), M.tpl (total page links), M.tr (reference count), M.tcb (total checkboxes), M.tcbc (checked), M.tcbu (unchecked), V.{varName} (variable value) |
D (descending) |
Examples: A.M.tt, D.M.ua, A.V.score.
Meta References
metaRef units reference live computed values. The ref string uses dot-separated segments:
| Pattern | Description | Example |
|---|---|---|
CT.{format} | Current time | CT.a.d |
CA.{pageId}.{format} | Page created-at | CA.AbcDef1234567890GhIj.a.d |
CA.{pageId}.{blockId}.{format} | Block created-at | CA.AbcDef1234567890GhIj.0.a.d |
UA.{pageId}.{format} | Page updated-at | UA.AbcDef1234567890GhIj.r.d |
V.{pageId}.{varId} | Variable value | V.AbcDef1234567890GhIj.0 |
PLCV.{pageId}.{blockId}.{fn}.{varName} | Page-link aggregation | PLCV.AbcDef1234567890GhIj.0.sum.score |
M.{type} | Global metadata | M.tp |
M.{type}.{pageId} | Page-level metadata | M.tw.AbcDef1234567890GhIj |
Time formats: a.ut (UNIX), a.d (date), a.dow (day of week), r.s / r.m / r.h / r.d (relative). Relative only valid for CA/UA.
PLCV derivations: cnt, sum, avg, min, max.
Images
Image IDs are content-addressed: {64 lowercase hex sha256}.{ext}. Allowed extensions: png, jpg, jpeg, webp, gif, svg.
Commands that accept new images support a top-level images map with base64 data URIs:
{
"images": {
"cover.png": "data:image/png;base64,iVBORw0KGgo..."
}
}
Commands
LIST_INSTANCES
Lists all connected GalaxyBrain browser instances.
Request
{ "type": "command", "requestId": "1", "cmd": "LIST_INSTANCES" }
Response
{
"ok": true,
"instances": [{
"instanceId": "desk-main", "connectedAt": 1712766240,
"state": "folder", "folder": "Work Notes",
"offline": false, "version": "0.4.0"
}]
}
LIST_FOLDERS
Returns recent folders and available demos.
Request
{ "type": "command", "requestId": "1", "cmd": "LIST_FOLDERS" }
Response
{
"ok": true,
"recentFolders": [{ "id": 1, "name": "My Project", "permission": "granted" }],
"demos": ["Simple", "F1", "The Premier League", "The Universe"]
}
permission: "granted" can open via API immediately. "prompt" requires a user gesture first.OPEN_FOLDER
Opens a recent folder by its numeric ID.
Request
{ "type": "command", "requestId": "1", "cmd": "OPEN_FOLDER", "id": 1 }
Response
{ "ok": true, "folder": "My Project", "skippedFiles": [] }
Errors: PARSE_ERROR, FOLDER_NOT_FOUND, PERMISSION_REQUIRED, FOLDER_UNAVAILABLE, FOLDER_LOAD_FAILED.
OPEN_DEMO
Opens a demo project by name.
Request
{ "type": "command", "requestId": "1", "cmd": "OPEN_DEMO", "name": "Simple" }
Errors: PARSE_ERROR, OFFLINE, DEMO_LOAD_FAILED.
CLOSE_PROJECT
Closes the current project and returns to picker state. No-op if already in picker.
REMOVE_RECENT_FOLDER
Removes a folder from the recent folders list. Does not delete the filesystem folder.
Request
{ "type": "command", "requestId": "1", "cmd": "REMOVE_RECENT_FOLDER", "id": 2 }
Response
{ "ok": true }
Errors: PARSE_ERROR, FOLDER_NOT_FOUND.
File Watching
FILES_WATCH
Subscribes to filesystem change events inside a subfolder of the open project (wraps the Chromium FileSystemObserver API).
Request
{ "type": "command", "requestId": "1", "cmd": "FILES_WATCH", "subpath": "inbox", "recursive": true }
subpath is forward-slash-delimited and must resolve to an existing directory under the project root. Leading /, \, Windows-absolute paths, backslashes, .., ., and empty segments are rejected.
Response
{ "ok": true, "subscriptionId": "fwatch_..." }
Errors: PARSE_ERROR, FOLDER_NOT_FOUND, NO_PROJECT, FOLDER_UNAVAILABLE, UNSUPPORTED_BROWSER.
Subscriptions live on the GalaxyBrain instance (the open browser tab) until FILES_UNWATCH, project close, folder switch, demo switch, app reload, or an errored record.
FILES_UNWATCH
Stops a previously started folder watch. Unknown subscription IDs are intentionally no-ops.
Request
{ "type": "command", "requestId": "2", "cmd": "FILES_UNWATCH", "subscriptionId": "fwatch_..." }
Response
{ "ok": true }
Errors: PARSE_ERROR (subscriptionId missing or not a string).
Sharp edges: unknown means the OS dropped events; errored means observation is dead and the subscription is torn down; Windows emits cross-directory moves as disappeared+appeared; file events are not file-readiness events; per-origin observation limit surfaces as errored.
READ_PAGES
Reads regular pages by ID or by open tabs.
Request (by IDs)
{
"type": "command", "requestId": "1",
"cmd": "READ_PAGES",
"pageIds": ["AbcDef1234567890GhIj"],
"icon": true, "title": true, "subtitle": true,
"blockIds": [0, 4]
}
Request (by tabs)
{ "type": "command", "requestId": "1", "cmd": "READ_PAGES", "tabs": true }
pageIds and tabs are mutually exclusive. Read options (icon, title, subtitle, blocks) default to true. Optional blockIds filters which blocks are serialized.
CREATE_PAGES
Creates one or more regular pages. null entries create default blank pages. returnPages: true includes the serialized page in each result.
Request
{
"type": "command", "requestId": "1",
"cmd": "CREATE_PAGES",
"returnPages": true,
"pages": [null, {
"icon": "📝",
"title": [{ "type": "text", "text": "New Page" }],
"subtitle": [],
"blocks": [{ "blockId": 0, "items": [...] }]
}]
}
UPDATE_PAGES
Updates pages via full block replacement or surgical edits. Pass readVersion for optimistic concurrency.
Surgical example
{
"type": "command", "requestId": "1",
"cmd": "UPDATE_PAGES",
"pages": [{
"pageId": "AbcDef1234567890GhIj",
"readVersion": 3,
"updateBlocks": [{ "blockId": 0, "items": [...] }],
"insertBlocks": [{ "blockId": 4, "items": [...] }],
"blockOrder": [0, 4]
}]
}
DELETE_PAGES
Deletes one or more regular pages.
{ "type": "command", "requestId": "1", "cmd": "DELETE_PAGES", "pageIds": ["AbcDef1234567890GhIj"] }
Errors: PAGE_NOT_FOUND, TEMPLATE_PAGE, LAST_PAGE.
QUERY
Searches and filters pages with sorting, pagination, and field projection.
Request
{
"type": "command", "requestId": "1",
"cmd": "QUERY",
"scope": "pages",
"search": { "text": "invoice", "sections": ["title", "blocks"] },
"fields": ["icon", "title", "counts"],
"sortBy": "updatedAt",
"sortDirection": "desc",
"maxResults": 20
}
Parameters
| Field | Type | Default | Description |
|---|---|---|---|
pageIds | string[] | null | Restrict to specific pages |
scope | string | "pages" | "pages", "templates", or "all" |
search | object | null | Text, error, or references search (see Search below) |
fields | string[] | [] | Fields to include in results |
sortBy | string | null | Sort mode |
sortDirection | string | null | "asc" or "desc" |
offset | number | 0 | Pagination offset |
maxResults | number | null | Max results (null = unlimited) |
Field values
| Value | Description |
|---|---|
"icon" | Page icon emoji |
"title" | Page title as plain text string |
"subtitle" | Page subtitle as plain text string |
"blocks" | Array of block preview strings |
"outboundPageLinks" | Array of linked page IDs |
"inboundPageLinks" | Array of page IDs linking to this page |
"inboundReferences" | Array of page IDs referencing this page in text |
"timestamps" | createdAt and updatedAt |
"vars" | Array of { id, name, value } |
"counts" | Page statistics |
Search
Mutually exclusive โ exactly one of text, errors, or references.
- Text:
{ "text": "query", "caseSensitive": false, "sections": ["title", "subtitle", "blocks"] } - Errors:
{ "errors": "all" | "brokenPageLinks" | "brokenValues" } - References:
{ "references": <target>, "sections": ["subtitle", "blocks"] }
text: "" and sections: [] match nothing. Error search ignores titles.
References search targets
| Target | Shape | Matches |
|---|---|---|
imageId | { "imageId": "<64hex>" } or { "imageId": "<64hex>.<ext>" } | image items in blocks |
varName | { "varName": "...", "prefix": false } | var definitions, V refs (live name lookup), PLCV refs, block linkOrder keys (prefix: true enables trailing-* match) |
metaRef | { "metaRef": <MetaRefQuery> } | metaValue units in subtitles, block text items, and var formulas |
pageLink | { "pageLink": { "pageId": "..." } } | standalone pageLink items and inline pageLink units |
webLinkUrl | { "webLinkUrl": "..." } | webLink units (exact URL match) |
MetaRef query object
head is required; every other field must be valid for the head.
| Field | Applies to heads | Notes |
|---|---|---|
head | all | "CT", "CA", "UA", "V", "PLCV", or "M" |
pageId | CA, UA, V, PLCV, M | 20-char page ID |
blockId | CA, UA, PLCV, M | non-negative integer |
varId | V | non-negative integer |
type | M | metaType code (tp, tb, โฆ) |
derivation | PLCV | one of cnt, sum, avg, min, max |
varName | PLCV | exact match |
format | CT, CA, UA | time-format code; CT accepts absolute formats only |
A meta ref matches iff every non-null field equals its parsed component. { "head": "M" } alone matches every M ref. matchCount counts structural occurrences. Invalid input returns PARSE_ERROR.
Graph filters
linksToPage, notLinksToPage, linkedFromPage, notLinkedFromPage, reachesPage, notReachesPage, reachableFromPage, notReachableFromPage
Sort modes
title, createdAt, updatedAt, outboundPageLinkCount, inboundPageLinkCount, pageReach, pageReachDistinct
Templates Commands
Template commands mirror their page counterparts. Key differences: templates can contain templateValue units, regular page IDs return NOT_TEMPLATE_PAGE errors, and the default blank template icon is "📋".
READ_TEMPLATES
Same as READ_PAGES (page-ID mode only, no tabs mode). Template reads serialize templateValue units without resolved value fields.
Request
{ "type": "command", "requestId": "1", "cmd": "READ_TEMPLATES", "pageIds": ["TplPage1234567890AbC"] }
CREATE_TEMPLATES
Same as CREATE_PAGES. Templates may freely use all three templateValue types. Default blank icon is "📋".
Request
{
"type": "command", "requestId": "1",
"cmd": "CREATE_TEMPLATES",
"pages": [{
"icon": "📋",
"title": [{ "type": "templateValue", "valueType": "dateToday" }, { "type": "text", "text": " — Daily Note" }],
"subtitle": [],
"blocks": [{ "blockId": 0, "items": [{ "type": "text", "style": "#", "content": [{ "type": "text", "text": "Tasks" }] }] }]
}]
}
UPDATE_TEMPLATES
Same structure and options as UPDATE_PAGES.
DELETE_TEMPLATES
Same as DELETE_PAGES, but no "last page" restriction (templates are separate from the required real-page set).
Request
{ "type": "command", "requestId": "1", "cmd": "DELETE_TEMPLATES", "pageIds": ["TplPage1234567890AbC"] }
CREATE_FROM_TEMPLATE
Creates a regular page from a template and inserts a page link into a source block.
Request
{
"type": "command", "requestId": "1",
"cmd": "CREATE_FROM_TEMPLATE",
"pageId": "AbcDef1234567890GhIj",
"blockId": 0,
"templateId": "TplPage1234567890AbC",
"insertAtTop": false,
"returnPage": true
}
Template values resolved at creation: nextNumber (increments from existing links), nextDay (advances one day), dateToday (current date).
Items Commands
Add or remove items from individual blocks without replacing the entire page.
anchor ("top"/"bottom") and offset. top + 0 = before first item. bottom + 0 = after last item. Offsets are clamped automatically.
PUSH_PAGE_ITEMS / PUSH_TEMPLATE_ITEMS
Inserts items into a block.
{
"type": "command", "requestId": "1",
"cmd": "PUSH_PAGE_ITEMS",
"operations": [{
"pageId": "AbcDef1234567890GhIj",
"blockId": 0,
"anchor": "bottom", "offset": 0,
"items": [{ "type": "text", "style": "[ ]", "content": [{ "type": "text", "text": "New task" }] }]
}]
}
Response includes insertedAt, totalItemCount, didReorderPageLinks, and the serialized block.
POP_PAGE_ITEMS / POP_TEMPLATE_ITEMS
Removes items from a block. Optional expectedItemType validates the removal range.
{
"type": "command", "requestId": "1",
"cmd": "POP_PAGE_ITEMS",
"operations": [{
"pageId": "AbcDef1234567890GhIj",
"blockId": 0,
"anchor": "bottom", "offset": 0,
"count": 2
}]
}
Workspace Commands
READ_WORKSPACE
Returns the current workspace layout with tab positions, scroll state, and linked refs expansion.
WRITE_WORKSPACE
Atomically replaces the workspace layout.
| Validation | Rule |
|---|---|
tabs | Non-empty array |
scrollLeftPx, scrollTopPx | Non-negative integers |
displayWidthPx | Integer between 350 and 5000 |
path | Must resolve to existing pages. Template tabs require empty path. |
Discovery Commands
MAP
Builds a recursive page-link tree rooted at one page.
| Field | Type | Default | Description |
|---|---|---|---|
pageId | string | — | Root page ID (required) |
limits | number[] | null | null | Per-layer link budgets. null = auto (~200 pages). [] = leaf only. |
subtitle | boolean | true | Include subtitle text |
blockText | boolean | true | Include text preview (max 100 chars) |
"+" in response indicates truncated links at that block.
ANCESTORS
Walks UP the page-link tree from a target page. The complement to MAP.
Same parameters as MAP but limits is always required (no auto mode). Response includes viaBlocks on each parent entry showing which blocks contain the link.
ORIENTATION
High-level structural overview. Automatically chooses between hub mode (well-connected pages, >= 60% coverage) and listing mode (mostly disconnected pages).
Hub mode returns up to 3 hubs with MAP trees. Listing mode returns up to 200 pages with detail tiers based on page count.
Events Commands
SUBSCRIBE
Subscribes to real-time event notifications. Categories: "pages", "project", "workspace", "files". Subscriptions are additive.
{ "type": "command", "requestId": "1", "cmd": "SUBSCRIBE", "categories": ["pages", "project", "workspace", "files"] }
Response includes activeCategories and current seq.
UNSUBSCRIBE
Removes event subscriptions. Unsubscribing from a non-active category is a no-op.
Event System
All events include type, event, seq, instanceId, and timestamp (UNIX seconds). Exception: eviction omits seq, instanceId, and timestamp.
seq gaps — this is expected.
pageLink.title changes but page A does not emit an event. Cached reads can go stale without a direct event for the dependent page.
Pages Events
All include source ("user", "api", "system") and requestId when API-triggered.
pages_created
Includes icon, serialized title, and sourceTemplateId (page ID or null).
pages_updated
Every changed region includes before/after diffs. Fields: role ("direct"/"cascade"), scope (changed regions), blockChanges with ops: created, deleted, updated, reordered.
pages_deleted
Includes icon and title at deletion time.
Project Events
project_opened, project_closed, concurrent_access_detected (with detectedBy: "workspace_poll", "api_server", or "both").
Workspace Events
workspace_tab_opened, workspace_tab_closed, workspace_tab_navigated, workspace_tab_resized, workspace_scrolled, workspace_changed (coarse fallback, emitted by WRITE_WORKSPACE).
Files Events
Subscribe with category files. files_changed fires for each change record produced by an active FILES_WATCH subscription.
{ "type": "event", "event": "files_changed",
"seq": 99, "instanceId": "desk-main", "timestamp": 1712766500,
"source": "system",
"subscriptionId": "fwatch_...",
"subpath": "inbox", "recursive": true,
"changeType": "appeared",
"path": ["recipes", "curry.png"],
"movedFrom": null }
Fields: subscriptionId, subpath, recursive, changeType (appeared, disappeared, modified, moved, unknown, errored), path (array, relative to subpath), movedFrom (array or null).
Eviction Event
Always delivered regardless of subscriptions. Sent when another tab connects with the same gb_id.
Error Reference
Server-Level Errors
Arrive as type: "error" messages.
| Code | Description |
|---|---|
INVALID_JSON | Message is not valid JSON |
MISSING_REQUEST_ID | Command missing requestId or cmd |
NO_INSTANCES | No GalaxyBrain instances connected |
UNKNOWN_INSTANCE | Specified instance ID not found |
INSTANCE_REQUIRED | Multiple instances connected; must specify instance |
INSTANCE_DISCONNECTED | Instance disconnected while processing |
PROTOCOL_MISMATCH | Browser app protocol version mismatch |
UNKNOWN_MESSAGE_TYPE | Unrecognized message type |
PROTOCOL_MISMATCH errors include additional fields:
serverProtocolVersion— the server's protocol version (number)clientProtocolVersion— the client's claimed protocol version (number or null)
Command-Level Errors
Arrive inside response messages as { ok: false, error, message }.
General
| Code | Description |
|---|---|
NO_PROJECT | Project-level command with no project open |
CONFLICT | readVersion mismatch |
INTERNAL_ERROR | Unexpected exception |
Parse and validation
| Code | Description |
|---|---|
PARSE_ERROR | Invalid payload shape or field types |
INVALID_ICON | Icon is not a valid emoji |
INVALID_STYLE | Unsupported text item style |
NO_BLOCKS | Page has zero blocks |
NO_ITEMS | Block or push payload has zero items |
INVALID_BLOCK_ID | Invalid block ID |
INVALID_VAR_ID | Invalid variable ID |
DUPLICATE_BLOCK_ID | Block ID duplicated within a page |
DUPLICATE_VAR_ID | Var ID duplicated or collides with existing |
INVALID_LINK_ORDER | linkOrder doesn't match grammar |
INVALID_TITLE_UNIT | Title used unsupported unit type or styled text |
INVALID_FORMULA_UNIT | Formula used unsupported unit type or styled text |
EMPTY_TEXT | Text or URL field is empty |
INVALID_META_REF | Meta reference string is malformed |
SELF_LINK | Page link points to itself |
INVALID_TEMPLATE_ID | Template ID invalid or is a regular page |
INVALID_TEMPLATE_VALUE | Template value type invalid |
NO_UPDATES | Surgical update has no actual changes |
DUPLICATE_BLOCK_OP | Same block in multiple surgical operations |
BLOCK_ORDER_MISMATCH | blockOrder doesn't match final block set |
Images
| Code | Description |
|---|---|
IMAGE_NOT_FOUND | imageId is malformed |
IMAGE_FILE_NOT_FOUND | imageFile key missing from images map |
IMAGE_AMBIGUOUS | Image item has both imageId and imageFile |
Page and template targeting
| Code | Description |
|---|---|
PAGE_NOT_FOUND | Page or template not found |
TEMPLATE_PAGE | Regular-page command sent to a template |
NOT_TEMPLATE_PAGE | Template command sent to a regular page |
BLOCK_NOT_FOUND | Block ID doesn't exist |
BLOCK_ALREADY_EXISTS | Inserted block ID already exists |
LAST_PAGE | Cannot delete the last remaining real page |
NO_REMAINING_ITEMS | Pop would remove every item |
UNEXPECTED_ITEM_TYPE | expectedItemType mismatch |
Workspace
| Code | Description |
|---|---|
NO_TABS | Empty or missing tabs array |
INVALID_TAB_PATH | Tab path structurally invalid |
Folder and demo lifecycle
| Code | Description |
|---|---|
FOLDER_NOT_FOUND | Recent-folder ID doesn't exist |
PERMISSION_REQUIRED | Browser requires user gesture for folder access |
FOLDER_UNAVAILABLE | Saved folder handle no longer accessible |
FOLDER_LOAD_FAILED | Folder could not be loaded |
DEMO_LOAD_FAILED | Demo could not be loaded |
OFFLINE | Command unavailable in offline mode |
File watching
| Code | Description |
|---|---|
UNSUPPORTED_BROWSER | Browser does not expose FileSystemObserver |
Recommended Client Flow
- Connect to
ws://localhost:<port>. - Send
LIST_FOLDERSto see available folders and demos. - If you expect multiple app tabs, include
instanceon every command. - Open a project with
OPEN_FOLDERorOPEN_DEMO. - Get your bearings with
ORIENTATION, then drill into specifics withREAD_PAGES,QUERY, orMAP. - Store page versions and workspace version from responses.
SUBSCRIBEtopages,project, and/orworkspacefor real-time updates.- For writes, pass
readVersionfor optimistic concurrency. - If you suspect missed events, issue a fresh read and compare
snapshotSeqwith your latest eventseq.
When to use each read command
| Command | Use for |
|---|---|
ORIENTATION | Fast project overview and workspace context |
QUERY | Filtering, search, or sorted discovery |
READ_PAGES / READ_TEMPLATES | Exact serialized content |
MAP | Recursive page-link structure from a root |
ANCESTORS | Upward ancestor/path discovery |
When to use each write approach
| Command | Use for |
|---|---|
CREATE_PAGES / UPDATE_PAGES | Full page creation or modification |
PUSH_* / POP_* | Precise block-level item manipulation |
CREATE_FROM_TEMPLATE | Placeholder resolution + automatic source-link insertion |
WRITE_WORKSPACE | Replace the entire tab layout atomically |
Protocol Constants
| Constant | Value |
|---|---|
| Default port | 1924 |
| Protocol version | 1 |
| Ping interval | 20s |
| Pong timeout | 10s |
| Reconnect delay | 5s |