Notes Browser Remote Control API

The notes browser (both basic and runnable versions) provides a JSON-RPC 2.0 remote control interface accessible via TCP, UDP, or Unix domain sockets. This allows programmatic control of browser navigation, document manipulation, sheet execution, and UI operations.

Protocol Overview

The control interface uses JSON-RPC 2.0 protocol with line-delimited JSON messages (one JSON object per line, terminated by newline character).

Request Format

{
  "jsonrpc": "2.0",
  "id": <request-id>,
  "method": "<method-name>",
  "params": {<parameters>}
}

Fields: jsonrpc must be exactly "2.0". id is any JSON value used to correlate responses (can be null for fire-and-forget). method is the RPC method name. params is an object containing method parameters.

Response Format

{
  "jsonrpc": "2.0",
  "id": <request-id>,
  "result": <result-value>
}

Success response contains result field. Error response has error field instead with code (integer) and message (string).

Transport Modes

Unix Domain Socket (Recommended for Local)

Start browser with control socket:

notes-browser/notes_browser_runnable.py \
  --url http://localhost:8021 \
  --control-unix-enabled \
  --control-unix-socket $PWD/socket

Send request with socat or Python socket:

echo '{"jsonrpc":"2.0","id":1,"method":"status.ping","params":{}}' | socat - UNIX-CONNECT:socket

TCP Socket

Start browser with TCP enabled (default port 8711):

notes-browser/notes_browser_runnable.py \
  --url http://localhost:8021 \
  --control-tcp-enabled \
  --control-host 127.0.0.1 \
  --control-tcp-port 8711

Send with netcat:

echo '{"jsonrpc":"2.0","id":1,"method":"status.ping","params":{}}' | nc 127.0.0.1 8711

UDP Socket

Start browser with UDP enabled (default port 8711):

notes-browser/notes_browser_runnable.py \
  --url http://localhost:8021 \
  --control-udp-enabled \
  --control-host 127.0.0.1 \
  --control-udp-port 8711

Send with ncat:

echo '{"jsonrpc":"2.0","id":1,"method":"status.ping","params":{}}' | nc -u 127.0.0.1 8711

API Methods by Category

Status Methods

status.ping status.capabilities

Navigation Methods

navigate.go_to_page navigate.back navigate.forward navigate.home navigate.refresh

Page Methods

page.get_current page.get_document_json page.put_document_json page.get_rendered_text

Sheet Methods

Available when a runnable sheet (runnable: true) is displayed. All methods error if sheet is busy executing.

sheet.run_all sheet.cell.run sheet.clear_outputs sheet.restart sheet.export_state sheet.import_state sheet.get_state sheet.save_page

UI Methods

ui.capture_screenshot ui.set_window_size ui.quit

Python Socket Client

import json, socket

def call_api(socket_path, method, params=None):
    sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
    sock.connect(socket_path)
    
    req = {
        "jsonrpc": "2.0",
        "id": 1,
        "method": method,
        "params": params or {}
    }
    
    sock.sendall(json.dumps(req).encode() + b'\n')
    response = b''
    while True:
        chunk = sock.recv(4096)
        if not chunk:
            break
        response += chunk
        try:
            return json.loads(response.decode())
        except json.JSONDecodeError:
            continue
    sock.close()

# Example: run all cells in current sheet
result = call_api('socket', 'sheet.run_all')
print(json.dumps(result, indent=2))

Shell Examples with socat

# Test connection
echo '{"jsonrpc":"2.0","id":1,"method":"status.ping","params":{}}' | socat - UNIX-CONNECT:socket

# Navigate to page
echo '{"jsonrpc":"2.0","id":1,"method":"navigate.go_to_page","params":{"key":"test/page"}}' | socat - UNIX-CONNECT:socket

# Get current page
echo '{"jsonrpc":"2.0","id":1,"method":"page.get_current","params":{}}' | socat - UNIX-CONNECT:socket

# Run all cells in sheet
echo '{"jsonrpc":"2.0","id":1,"method":"sheet.run_all","params":{}}' | socat - UNIX-CONNECT:socket

# Capture screenshot
echo '{"jsonrpc":"2.0","id":1,"method":"ui.capture_screenshot","params":{}}' | socat - UNIX-CONNECT:socket

Common Workflows

Automated Sheet Execution with Results Export

#!/bin/bash
SOCKET="socket"

# Navigate to sheet
echo '{"jsonrpc":"2.0","id":1,"method":"navigate.go_to_page","params":{"key":"test/video-brightness-analysis"}}' | socat - UNIX-CONNECT:$SOCKET

# Run all cells
echo '{"jsonrpc":"2.0","id":2,"method":"sheet.run_all","params":{}}' | socat - UNIX-CONNECT:$SOCKET

# Export results
echo '{"jsonrpc":"2.0","id":3,"method":"sheet.export_state","params":{"path":"results.json"}}' | socat - UNIX-CONNECT:$SOCKET

Document Update Workflow

Read document JSON, modify it, and write back:

# Get current document JSON
echo '{"jsonrpc":"2.0","id":1,"method":"page.get_document_json","params":{}}' | socat - UNIX-CONNECT:socket > doc.json

# Modify with Python, then write back via API
python3 << 'PYTHON'
import json, socket

# Load current document
with open('doc.json') as f:
    response = json.load(f)
doc = response['result']['document']

# Modify document (add cell, etc)
# ...

# Write back
req = {
    "jsonrpc": "2.0",
    "id": 1,
    "method": "page.put_document_json",
    "params": {"key": "test/page", "document": doc}
}

sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect('socket')
sock.sendall(json.dumps(req).encode() + b'\n')
print(json.loads(sock.recv(4096).decode()))
sock.close()
PYTHON

Practical Usage with nc

Always give nc a -w timeout — without it nc hangs after the response arrives, waiting for more data:

# -w 2 closes 2 seconds after last data
echo '{"jsonrpc":"2.0","id":1,"method":"status.ping","params":{}}' | nc -w 2 127.0.0.1 8711
echo '{"jsonrpc":"2.0","id":1,"method":"navigate.go_to_page","params":{"key":"Derek"}}' | nc -w 2 127.0.0.1 8711
echo '{"jsonrpc":"2.0","id":1,"method":"page.get_current","params":{}}' | nc -w 2 127.0.0.1 8711

Launching the Browser with Control Enabled

Control must be enabled at launch — it cannot be toggled on a running instance. Check first with pgrep -af notes_browser and nc -z 127.0.0.1 8711.

# Launch with TCP control (default port 8711)
DISPLAY=:0.0 python3 ~/py/gdata-server/notes-browser/notes_browser.py \
  --url http://localhost:8021 \
  --control-tcp-enabled &

# Or via environment variable
DISPLAY=:0.0 NOTES_BROWSER_CONTROL_TCP_ENABLED=1 \
  python3 ~/py/gdata-server/notes-browser/notes_browser.py \
  --url http://localhost:8021 &

# Note: passing a positional socket path (old style) is rejected — use --url
version 2  ·  updated 2026-05-29