Low-level protocol appendix
This page completes the reference for the variable **fields and **params accepted by command() and rpc(). Named package wrappers remain the preferred interface.
HTTP request and response rules
The running app listens only on 127.0.0.1. Connection discovery and the
X-Heeler-Token header use the same token shown by the scripting connection;
requests without the matching token are refused. Send one HTTP POST per
connection with a UTF-8 JSON object containing a nonempty method string
and optional params. The response closes the connection. The Python package
handles this transport for you.
The request body limit is 8 MiB (8,388,608 bytes). This counts JSON bytes,
including all decoded chunks when using Transfer-Encoding: chunked.
Content-Length requests over the limit are refused before the body is read.
Use either Content-Length or chunked encoding, never both. Duplicate length
headers and unsupported transfer encodings are refused. Headers, chunk size
lines and trailers share a 16 KiB (16,384 byte) limit. Send the body
directly; Expect: 100-continue is not supported.
The complete request must arrive within five seconds of acceptance,
including its headers and body. Sending occasional bytes does not extend
that deadline. This is a request-upload deadline, not a limit on execution:
frontend calls retain their ten-second timeout, with ten minutes for
stack.create, stack.bake and pano.create, and one hour for export.run.
A response also has a five-second write deadline once it is ready to send.
| HTTP status | Meaning |
|---|---|
| 200 | A complete request reached the dispatcher. Check ok for method success or failure. |
| 400 | Malformed JSON, an invalid method field, truncated input or invalid HTTP framing. |
| 403 | Missing, incorrect or duplicate token header. |
| 405 | The request does not use POST. |
| 408 | The request read deadline expired. |
| 411 | Neither a content length nor chunked encoding is present. |
| 413 | The request body exceeds the limit. |
| 417 | An unsupported Expect header is present. |
| 431 | Headers or chunk framing exceed the metadata limit. |
| 503 | The dispatcher cannot accept the request. |
Responses use Content-Type: application/json. Success is
{"ok":true,"data":...}; errors are {"ok":false,"error":"explanation"}.
Malformed JSON names the parsing error and never dispatches an empty method.
The Python package reports HTTP refusal messages as HeelerError with the
app's explanation.
Socket reads and response writes run separately from the serial command dispatcher. A client that stalls during upload or stops reading its response does not block other accepted clients. At most 16 connections are active; excess connections are closed immediately. Complete requests enter a bounded queue and execute one at a time in queue order. A long-running command can therefore delay another command, even though socket stalls cannot.
Turning scripting off closes the listener and active sockets, cancels waits for frontend replies and drops requests still queued. It does not undo a command already dispatched or forcibly interrupt native rendering already in progress. A disconnected script must check app state before retrying a mutation because a command that already started may have completed.
Scriptable command() types
The running app accepts the following command dictionaries. command(type, **fields) constructs {"type": type, **fields}.
| Type | Required and optional fields | Result |
|---|---|---|
set_param |
id: str, param: str, value: number |
Set one numeric node parameter. |
set_params |
id: str, values: dict[str, number]; optional text: dict[str, str], harmonize: bool |
Set several numeric/text parameters as one command. |
set_curve |
id: str, channel: str, curve: list[[x, y]]; optional tangents: list[number] |
Replace one node curve. |
set_text_param |
id: str, param: str, value: str |
Set a text or enum parameter. |
connect |
wire: {from, to, toPort, kind} |
Connect graph ports. |
disconnect |
to: str, toPort: str |
Remove an input wire. |
splice_node_into_wire |
id, from, to, toPort |
Insert an existing node into a wire. |
extract_node |
id: str |
Remove a node from its chain and heal around it. |
delete_nodes |
ids: list[str]; optional heal: bool |
Delete selected nodes with optional neighbor healing. |
set_enabled |
id: str, enabled: bool |
Enable or bypass a node. |
rename_node |
id: str, name: str |
Rename a node. |
node_outside |
id: str |
Build the masked node's Outside branch. |
set_node_tint |
id: str, `tint: str |
None` |
set_node_note |
id: str, note: str |
Set the node note. |
select_nodes |
ids: list[str]; optional additive: bool |
Change graph selection. |
group_selection |
name: str; optional note: str |
Group the current node selection. |
add_region |
id: str, region: dict |
Add one region to a selection-mask node. |
clear_regions |
id: str |
Remove all regions from a selection-mask node. |
set_regions |
id: str, regions: list[dict] |
Replace selection regions. |
set_rating |
ids: list[str], stars: int |
Rate catalog images. |
set_flag |
ids: list[str], flag: str |
Pick, reject, or clear catalog images. |
select_image |
id: str |
Open an image. |
new_take |
optional name: str, note: str |
Branch the current take. |
update_take |
takeId: str, name: str; optional note: str |
Rename or annotate a take. |
switch_take |
takeId: str |
Activate a take. |
delete_take |
takeId: str |
Delete a take. |
set_prefs |
prefs: dict |
Update preferences. |
set_mode |
mode: str |
Switch workspace. |
begin_gesture |
key: str |
Begin undo coalescing. |
end_gesture |
no fields | End undo coalescing. |
Batch accepts only select_image, set_param, set_enabled, set_rating, and set_flag from this table.
Selection-region dictionaries use normalized image coordinates. A path region has kind="path", op, and points, with optional smooth, via, and off. A key region has kind="key", op, x, y, tolerance, and optional space/off. Sample regions use kind="samples", op, points, tolerance, and optional space/off. op is replace, add, subtract, or intersect.
Bridge rpc() methods
| Method | Parameters | Returned data |
|---|---|---|
app.ping |
none | Protocol/app identification. |
app.state |
none | Session-state dictionary. |
app.open_folder |
path |
Opened path, name, and image count. |
registry.nodes |
none | {heeler, nodes} registry manifest. |
graph.get |
none | Graph dictionary. |
graph.serialize |
none | App-internal serialized graph and image id. |
graph.command |
command: dict |
Command acknowledgement. |
graph.save |
none | Save acknowledgement. Refuses, naming the photograph, when it has no edits saved by the app to update: open it in Heeler and change something once. |
catalog.images |
none | Catalog image list. |
catalog.collections |
none | Collection list. |
takes.list |
none | Take-list dictionary. |
prefs.get |
none | Preference dictionary. |
hotkeys.export |
none | {json: str}. |
hotkeys.import |
json: str |
Import report. |
export.run |
dir, plus export settings |
Export report. |
stack.create |
ids, mode |
New library entry. |
stack.info |
id |
Stack recipe. |
stack.configure |
id, optional recipe fields |
Updated stack recipe. |
stack.bake |
id, optional format, quality |
New baked library entry. |
pano.create |
ids |
New panorama library entry. |
pano.info |
id |
Panorama recipe. |
pano.configure |
id, optional recipe fields |
Updated panorama recipe. |
render.preview |
optional node, quality |
{format, width, height, base64}. |
noise.estimate |
none | Batch-only {luma_sigma, chroma_sigma}. |
registry() unwraps registry.nodes["nodes"]; hotkeys_export() unwraps the json field; render() decodes the base64 field. Direct rpc() callers receive the unwrapped method data but must perform these additional transformations themselves.
Protocol names are additive but lower-level than the package wrappers. Code intended for long-term compatibility should prefer named functions and registry() discovery.