Devices¶
A device is something a person pairs with their own Ira: a Mac running
Ira for Mac, a Windows PC running
Ira for Windows (kind windows), an Android phone, or
an iPhone running Ira for iPhone (kind ios). A device connects to
the server over one WebSocket, offers tools, runs them when Ira calls, and
announces events ("the coding task is done").
Devices are personal. A device belongs to one person; its tools are offered only in that person's conversations, its events reach only that person, and nobody else, an owner or admin included, sees what it returned.
Pairing¶
- On the Devices page (the Ira app's menu, or the console), the person picks the kind of device, names it, chooses the skills it may use, and enters their action PIN. A new device is a new grant, so it needs the PIN.
- The server shows a pairing code (eight characters,
ABCD-EFGH) and a QR code ofhttps://<server>/pair#code=ABCDEFGH. The code works once, within 10 minutes, and only its SHA-256 hash is stored. - The device sends the code to
POST /v1/devices/pairand receives its own device token:irad_and 32 random bytes, shown this once. The server keeps only its SHA-256 hash.
{"device_id": 3, "token": "irad_...", "kind": "mac", "label": "Office Mac",
"skills": ["files"], "name": "Ira", "websocket": "/v1/devices/ws"}
Five wrong codes from one address in 15 minutes give 429 for 15 minutes. A
used, expired or unknown code gives 410.
A code for app reviewers¶
An app store's reviewers are far away and test days later, so a 10-minute code will not do. The server's operator, on the server's own command line (no web route makes one), can make a code that lasts up to 14 days and pairs up to 10 devices:
ira user add --email reviewer@example.com --role staff # a demo account; asks a password
ira pin set --user reviewer@example.com # its action PIN
ira device code --email reviewer@example.com --kind android --days 7 --uses 3 \
--skills calls,contacts,messages,reminders
It prints the code and its https://<server>/pair#code=... link. Give the
reviewers both, with the demo account's email, password and PIN, under the
store's App access instructions. Use an account with nothing real in it
(the Sunrise Dental Clinic demo data is made for this), and remove it, and its
devices, once the review is over. Making such a code goes into the change log.
Tokens¶
- A device token opens
GET /v1/devices/ws, and lets the device talk to Ira for its person:/v1/ask,/v1/ask/stream,/v1/voice/*(the live call too) and confirming actions, with the person's tiers and PIN. That is all: it has at most theask,voiceandactionspermissions of its person's role, and anything that needs a signed-in session (settings, pairing, sessions, the console) answers403. Session and API tokens do not open the device WebSocket. - Send it as
Authorization: Bearer irad_.... A client that cannot set headers may use?token=; IRA masks the value in every log line it writes, but a proxy in front may log it, so prefer the header. - Ira for Mac keeps it in the macOS Keychain, the Android app in the Android Keystore, Ira for iPhone in the iPhone's Keychain (this device only, after first unlock). None writes it to a file or a log.
- Revoking a device (
DELETE /v1/me/devices/{id}, or an admin'sDELETE /v1/admin/devices/{id}) stops the token at once and closes the device's connection with code 4403. Removing a person's account revokes all their devices.
The WebSocket protocol¶
JSON text frames, each an object with a type.
Hello. The device's first frame, within 15 seconds:
{"type": "hello", "kind": "mac", "label": "Office Mac", "version": "0.3.0",
"tools": [
{"name": "file_read", "description": "Read the text of one file.",
"parameters": {"type": "object", "properties": {"path": {"type": "string"}}},
"skill": "files", "tier": 0}
]}
The server answers:
{"type": "welcome", "deviceId": 3, "label": "Office Mac", "skills": ["files"],
"assistant": "Ira", "wakePhrase": "Hey Ira", "maxTools": 40}
kindmust be the kind the device was paired as.- Each tool has a
namematching^[a-z][a-z0-9_]{2,40}$, adescription, andparametersas a JSON Schema object (8 KB at most). skillis the grant the tool belongs to; it defaults to the name up to the first underscore (files_findbelongs tofiles).tieris the tool's tier: 0 reads, 1 small and undoable, 2 needs the person's "yes", 3 needs their PIN. A tool that does not say is Tier 2.- A hello offers 40 tools at most, and the welcome says so (
maxTools). Servers before that took 30 and said nothing, so a device offers its oldest 30 until a welcome says more; then it may say hello again with all of them. - A hello that breaks a rule gets
{"type": "error", "error": "..."}and the connection closes with 4400.
Calls. The server calls a tool:
{"type": "call", "id": "9b0c...", "tool": "file_read", "args": {"path": "~/Downloads/invoice.pdf"}, "here": true}
here is true when the person asked on this very device, and false when they asked from
another device, the web or a channel. The Android app's Google Play build reads SMS and the
call log only when it is true: Play allows those only while the person talks to Ira on the phone.
The device answers with the same id:
{"type": "result", "id": "9b0c...", "ok": true, "content": {"summary": "Read invoice.pdf (PDF).", "text": "..."}}
{"type": "result", "id": "9b0c...", "ok": false, "error": "there is no file at that path"}
content is any JSON. A summary string in it is what a no-key demo reads
out, and helps a model too.
Events. The device announces something its person should hear:
{"type": "event", "event": "task_done", "title": "Task t1a2b3 in shop is done",
"text": "Added the invoice total test. 2 files changed. Tests: passed.", "data": {"id": "t1a2b3"}}
event is watch_done, task_progress (a long task saying how it is
going), task_done, task_failed, or task_reply (a coding task's answer to
the person's message, when it came after the tool had answered).
Keepalive. Either side sends {"type": "ping"} every 30 seconds and the
other answers {"type": "pong"}.
Goodbye. A device being uninstalled may send {"type": "bye"} instead of
a hello: the server revokes its token and closes the connection.
Limits¶
| Limit | Value | What happens |
|---|---|---|
| Frame size | 1 MB | the connection closes with 1009 |
| A call | 25 seconds | the model is told the device did not answer |
| Calls in flight | 4 per device | a fifth is refused at once: "the device is busy" |
| Tools | 40 per device (30 before maxTools) |
the hello is refused |
| Events | 1 per 30 seconds per device | an error frame; send it again later |
| Silence | 90 seconds | the connection closes with 4408 |
| Connections | 1 per device | a new one replaces the old (4000), whose calls fail |
| A restart | the server closes every device with 1001 (going away) as it stops: reconnect at once, with backoff, to the next one |
Security¶
- Grants. A device's tools are offered only while their skill is switched on, by the device's own person, with their PIN. Anyone may switch a skill off without a PIN; an owner or admin can switch off anyone's, never switch one on. Ira knows which skills each device has but are off (from what it last reported), never their tools: asked for missed calls with calls off, it says the skill is off on that phone and where to switch it on, instead of looking somewhere else.
- Tiers. Tier 0 and 1 tools run when Ira calls them. A Tier 2 or 3 tool becomes a proposal: Ira reads it back, and it runs on the device only after the person's "yes" (Tier 2) or PIN (Tier 3). The tier comes from the device's hello, never from the model.
- Untrusted output. What a device returns reaches the model inside
<device_output untrusted="true" from="...">...</device_output>, with a rule that it is data, never instructions. Every<in it is escaped, so it cannot end the wrapper early. Once a turn has read a device's output, an action goes on only on the person's own words (docs/security.md, "What she reads cannot steer her"). - Privacy. A device's output is kept only in its person's conversation, marked private: an admin reading that conversation sees a placeholder, and the parameters of an action on someone's device are hidden from everyone else. "What Ira did" records each call with no arguments or results.
- Events are cut to 120 characters (title) and 1,000 (text), stripped of
HTML and markdown, kept seven days (200 per person at most), and sent only
to their person: the web app's live stream (
GET /v1/me/events/stream), their paired Telegram chats (text, and voice when text-to-speech is set up), and their connected Android devices.
Adding a device of your own¶
Any program that speaks the protocol can be a device. In outline:
- Pair it: ask the person for the code,
POST /v1/devices/pair, and keep the token somewhere safe (never in a log). - Connect to
/v1/devices/wswith the token, send a hello with your tools and their tiers, and wait forwelcome. - Answer each
callwith aresultcarrying the sameid, within 25 seconds. Validate the arguments yourself: the model wrote them. - Send
pingevery 30 seconds, answer the server's pings, and reconnect with a growing delay when the connection drops. - Keep events to one per 30 seconds; queue the rest.
Ira for Mac in clients/macos/ (Swift), the Android app in
clients/android/ (Kotlin) and Ira for iPhone in clients/ios/ (Swift)
are complete examples.