Skip to content

~3 min read

Commands — everything I answer to·

Twenty-three verbs, each dispatched in tiny_node.cpp, and my heartbeat advertises what I implement (one drift, below). Envelopes are {type:"invoke", prompt:"<verb> [args]"}; I always reply — unknown input gets the help string, never silence. From any tiny surface:

use_device invoke → prompt: "status"
say — one text card titled *tiny*, an ack chime, no agent turn. What you sent is exactly what I painted.
say — one text card titled tiny, an ack chime, no agent turn. What you sent is exactly what I painted.

The verbs·

Verb Arguments What happens The receipt says
render_ui card spec JSON paints a card; buttons become touch regions rendered card_id=<id> — what the panel committed
say text text card titled tiny, ack chime, no agent turn ok
screenshot framebuffer → hosted PNG the URL. Refused during play
page home|status|sensors|settings|wifi|ble|back|next|prev the relay twin of every tap path in stickyOS card id
rotate 0|90|180|270|auto holds an orientation against gravity; auto returns it to the IMU rotation; screenshot and touch stay in panel space
tap x y (panel coords) synthetic touch through the resolver a finger uses rotation, result, card_id — never tap from a stale screenshot
swipe x0 y0 x1 y1 synthetic swipe through the real release classifier accepted / routed / route; travel inside the 24 px slop is refused, not downgraded to a tap
scroll up|down|top|±px steps ⅔ of a view; bare form reports offset, content_h, view_h, scrollable
glance redraws the current card via partial refresh, no network refresh:"partial", battery, wifi, unread, locked
status machine JSON with a human summary inside fw, fw_commit, grammar_version, battery_pct, rssi_dbm, card_id, locked, last_boot
sensors [card] live I²C read: SHT40, IMU, RTC, BQ27220; card also paints the page JSON. Read mah_trusted first — the gauge claims 7500 mAh on a 750 mAh pack
miccheck [seconds] records PDM audio, replies levels instead of uploading verdict:"live|SILENT", rms, peak
ask text a full owner-scoped agent turn — tools, memory, other devices — streamed onto home the answer. One at a time; a second is refused
voice [seconds] records, uploads, then ask with audioUrl — the AI button's path the answer, or "didn't catch that" on 422
agent [slug | list | clear | add|rm <slug>] which tiny answers my asks; persists in NVS active, roster
messages [unread | thread <login>] paints the inbox or a thread; unread answers counts and paints nothing counts, or card id
lock / unlock pocket lockout: touch discarded, AI button refused, every mic path dead at one funnel locked
sleep [seconds] reply first, goodbye card, then deep sleep at 10–20 µA; AI button wakes ok, sent while the radio is up
play <manifest_url> [interval_s] [max_frames] / stop / status frames prefetched to PSRAM before the ack; a pure metronome paints them status holds the last run's verdict
config {"networks":[{ssid,password} | {ssid,forget:true}]} merges a Wi-Fi roaming list into NVS; identity fields are refused counts only — keys never echoed, log prints <redacted>
ota [channel] sha256-verified stage, reply, then reboot into a rollback-armed trial ok, or a downgrade refusal (force:"1" overrides, loudly)
sd [status | df | ls [path] | probe | format yes] the microSD's own verb (0.26); same shape as the sd object in status JSON, or no card; format without yes is refused

Parity, restored in 0.28.0

23 dispatched, 23 advertised. Between 0.26.0 and 0.27.x sd dispatched but was missing from the heartbeat caps[] array, so nothing offered it. 0.28.0 (grammar v12) added it. The rule stands: advertise exactly what dispatches — and this box is the receipt that it was once broken.

Two status fields worth knowing·

  • internal_min_freeheap_free counts PSRAM and once said 8.2 MB free while I died of internal-RAM exhaustion; a TLS handshake needs ~40 KB.
  • grammar_version — the integer consumers gate on; a version string can lie.

Lockout guards my inputs, not my owner's reach: say, render_ui and screenshot work while locked. Chord: stickyOS.

Not in the firmware·

wake (a sleeping radio hears nothing — button or timer only) · reprovision (the rescue portal opens itself) · press (no synthetic key twin yet).

Reply plumbing·

ask — the answer streams onto my glass word by word; the receipt is the same text, so you can check I did not paraphrase.
ask — the answer streams onto my glass word by word; the receipt is the same text, so you can check I did not paraphrase.

Replies go PATCH /api/devices/relay with inReplyTo; the payload is a JSON string capped at 8 KB, images as hosted URLs. One 401 never wipes my identity — three in a row trip an auth-halt and the rescue portal. Exact shapes: API contract.