TempleOS Desktop Docs

sheet service (host half of Spreadsheets)

Edited 2026-10-27apps-host/hub/SHEET.md

svc_sheet.py registers service sheet in the hub (one line in hostd.py build_hub). It drives GridCraft (gridcraft-cli mcp, MIT OR Apache-2.0, alpha, third party) as one long-lived process. The workbook lives in that process; the guest only paints and types. Wire is the hub's: REQ id sheet token op arg... -> RES id OK + records (TAB fields, \t \n \\ escaped, ASCII only).

Ops and replies (args are strings)#

op args reply records
open file in workspace, or budget/sales/grades OPEN src title nsheets active dirty, then SH idx name used r0 c0 nrows ncols per sheet
new [sample] same as open
sheets / activate [name or index] SH records
read range [sheet] (empty range = used range) RNG sheet range r0 c0 nrows ncols truncated, R rownum cell..., then ST records
cell A1 [sheet] CELL ref input shown type flags bold italic (input is the formula text)
eval formula, = optional (needs no open sheet) EVAL type text (errors: type e, text #DIV/0!; a bad formula is ERR)
set cell text (text starting = is a formula) SET ref shown nsheets capped, then CH ref shown for every cell whose display changed (dependents included, max 200)
format range bold=1 italic=0 fill=RRGGBB\|none align=left\|center\|right FMT range options
save / export_csv path.xlsx / path.csv\|tsv [sheet] SAVED relpath bytes; first try returns APPROVE fs:write:<abs path>
close, status CLOSED; STAT binary_found running pid rss_kb open title workspace

Read layout. A window is at most 100 rows x 26 columns. Row record R n f1 f2 ...: field j is column c0+j; an empty cell is an empty field and trailing empty fields are cut. A field is one type char then the shown text (30 chars max, ASCII): s text, n number, c currency, p percent, d date/time, b bool, e error. Numbers are not re-sent as raw values; use cell for the formula/input. ST dcol drow flags align fill is sent only for cells that differ from the default (flags bit1 bold, bit2 italic; align l c r, default r for numbers else l; fill RRGGBB or -); dcol/drow are offsets in the window. Styles come from the engine's edit.copy html; if the window has merged cells the ST records are omitted.

Config (config.json, git-ignored; see config.example.json)#

gridcraft_cli absolute path (else env GRIDCRAFT_CLI, else PATH; the service answers ERR "gridcraft-cli was not found" without it), sheet_workspace (default ~/.cache/kosmos-hostd/sheets, mode 0700), sheet_timeout_s (default 10, per engine call).

Security#

  • Engine always under sandbox-exec -p <profile> (profile built in memory by make_profile, not a file): deny network*, writes only in the workspace, no reads under /Users or $HOME except the workspace and the binary's folder; env -i, HOME and TMPDIR in <workspace>/.sandbox/, cwd there. Tests prove a raw open_workbook ~/.zshrc, a write to ~, and mcp --connect <local port> fail.
  • GridCraft MCP has no root confinement (docs/mcp.md), so the service checks every path first: no .., no hidden component, no symlink at any level, must be inside the workspace, extension xlsx/xlsm/csv/tsv/json (save: xlsx, export: csv/tsv), 20 MB read limit. It exposes fixed ops only, never execute_command.
  • File writes ask the guest (APPROVE naming the full path); the approval is used up by that one write. set/format need none.
  • Limits: arg 1000 chars, window 100x26, engine line 4 MB, call timeout 10 s (the engine is killed, next call restarts it), at most 5 starts a minute. Supervision: started on first call; a dead or timed-out engine is replaced and the guest is told once that unsaved edits were lost; close() and atexit kill only the Popen pid. sys hello closes the workbook.

Measured (M-series Mac, gridcraft-cli 0.12.1, tests/bench_sheet.py)#

First call (start + new + eval) 50 ms; open sample 8 ms; read 20x12 2.4 ms; set with changed cells 0.8 ms; save 5 ms; reopen 2 ms. RSS 4.5 MB at start, 15 MB after the bench. Reply for a dense 20x12 window (240 cells, bold and fill rows): 2595 bytes; the budget sample: 923 bytes. Compare the hub's own cost: about 0.6 s for 20 KB on the serial link.

What the CLI cannot do (or we do not use)#

No bulk style read (we parse edit.copy html; fonts, borders, number-format strings are not sent). No headless PNG; charts are not rendered (the sheet reports them in inspect_workbook only). Save writes xlsx only; xlsb/ods/xls open via gridcraft-cli convert, not this service yet. Sheet-qualified style reads work on the active sheet only. Alpha: function coverage is self-reported. Tests: python3 -I tests/test_sheet.py (fake engine always, real with GRIDCRAFT_CLI=...).

TempleOS Desktop Docs. Built from the Markdown sources; offline, no external requests. Back to top