Design note: devices, Chrome and search (phase 1)¶
Superseded
The Python companion and the Chrome extension this note describes are retired: Ira for Mac does their work natively (files, the screen, browser tabs, coding tasks and the routine skill). The note stays as the record of the first design.
IRA so far answers over a business's own tools. Phase 1 makes it a personal assistant too: a person pairs their own Mac, Ira reads a Chrome tab when asked, watches it and says when it is done, and searches the web and the news. Everything here is a generic IRA feature, off until a person turns it on, and private to that person.
Pieces¶
Chrome extension --native messaging--> in.ira.mac host --unix socket--> ira-mac daemon
|
WebSocket /v1/devices/ws (irad_ token)
|
web app, console, Telegram <--events-- IRA server: device hub, assistant, search
- Server (
src/ira/devices/,src/ira/channels/devices.py): devices and pairing, the WebSocket hub, per-user device tools, events, and the user's own settings, grants, audit, export and delete. - Mac companion (
clients/mac/): a LaunchAgent daemon holding the WebSocket, and a thin native-messaging host that Chrome starts. - Chrome extension (
clients/chrome-extension/): no network of its own, no resident content script; it injects a reader into one tab on request. - Search (
src/ira/search.py):web_searchthrough the Brave Search API andnews_searchover RSS feeds named inira.yaml.
Data¶
New tables, all keyed by the user:
| Table | Holds |
|---|---|
devices |
id, user, kind (mac, android), label, token hash, created, last seen, revoked, client version, the tools of its last hello, the skills the user switched on |
device_pairings |
one-time pairing codes (hashed), kind, label, skills, 10-minute expiry |
device_events |
watch_done, task_done, task_failed: title and text (markup stripped), kept 7 days |
tool_log |
"What Ira did": who, channel, which tool, which device, tier, outcome, seconds. Never arguments or results |
user_settings |
per-person preferences: assistant name, wake phrase, languages, reply language, web search on or off |
messages.private marks a tool result that came from a device: only the
conversation's own user sees it; the admin transcript shows a placeholder.
pending_actions gains tier, user_id and device_id.
Pairing and tokens¶
- In the web app or the console, the person picks the kind, a name and the skills to allow, and confirms with their action PIN (a new grant is Tier 3).
- The server answers with a one-time code (eight characters, 10 minutes, kept
hashed) and a QR code of
https://<server>/pair#code=.... - The device posts the code to
POST /v1/devices/pairand receives its own token,irad_and 32 random bytes, once. Only its SHA-256 is stored. - The token works only on
GET /v1/devices/ws; the rest of the API refuses it, and the WebSocket refuses every other credential. Revoking a device closes its connection at once.
The WebSocket¶
JSON text frames, as the brief specifies: hello and welcome; call and
result; event; ping and pong. Limits: 1 MB frames, 25-second calls,
4 calls in flight, tool names ^[a-z][a-z0-9_]{2,40}$, 30 tools, one event
per 30 seconds, 90 seconds of silence closes the connection. A new
connection for the same device replaces the old one, whose calls fail. A
hello with a bad tool list is refused with an error frame and close code
4400. Each tool in a hello may say its skill (default: the name up to the
first underscore) and its tier (default 2).
Tools per person¶
Each turn of the assistant gets the registry's tools plus that user's own:
the tools of their connected devices whose skill they switched on, and the
search tools if they turned web search on and the server has it set up.
Nobody else's conversation sees them. When two of a user's devices offer the
same tool, it gains a device argument listing them.
Tiers (the security model)¶
Every tool has a tier in code; the model's arguments can never change it.
| Tier | What | How it runs |
|---|---|---|
| 0 | reading inside a grant | at once |
| 1 | small and undoable | at once; Ira says what it did |
| 2 | outward or costly | a proposal Ira reads back; runs on the user's "yes" (typed, spoken or tapped) within 120 s |
| 3 | money, deletion, sharing, push, merge, deploy, a new grant | the PIN-confirmed action, never a spoken PIN |
@toolis Tier 0 and@actionTier 3 by default; both taketier=.- A device declares each tool's tier in its hello.
- Untrusted content cannot start Tier 2 or 3. Device output and web
results reach the model wrapped as
<device_output untrusted="true">or<web_results untrusted="true">, with a system rule that they are data. If a turn has read any of it, a Tier 2 or 3 call in that turn is refused, and the person has to ask in their own words. - Switching a skill on, pairing a device and turning web search on need the PIN. Switching anything off never does; an owner or admin can switch off anyone's grant, never switch one on.
Events¶
A device's event is cut to 120 and 1,000 characters, stripped of markup, and
sent to its user only: the web app (server-sent events at
/v1/me/events/stream), their paired Telegram chats (text, and voice when
text-to-speech is set up), and their connected Android devices. One event per
30 seconds per device; the Mac companion queues to respect it.
Privacy¶
- Page text and device output are read only when asked and kept only inside the conversation; an admin's transcript shows "(private device output)".
- "What Ira did" keeps no contents. "Export my data" returns a JSON file of the person's conversations, devices, events, settings and tool log. "Delete my data" removes them and revokes the devices, after the password.
- The Mac companion logs tool names, tab ids and timings, never page text or URLs with a query string. URLs it returns lose their query and fragment.
Language¶
The system prompt says that people speak Hindi (Devanagari or romanised),
English and Hinglish, often in half sentences; to ask back when unsure; and
to reply in the language spoken, or in the person's chosen reply language.
With Polly, a reply with Devanagari is spoken as hi-IN, otherwise in the
configured code (Kajal speaks both). A small fictional evaluation set and
ira eval language measure intent and reply language against the configured
models; the numbers go in the docs before any claim.
The Mac companion¶
- Why two processes. Chrome starts a native-messaging host itself, only while the extension holds a port, and speaks to it over stdio. The server connection must stay up when Chrome is closed, hold the Keychain token and later run tasks. So the daemon (a LaunchAgent) owns the WebSocket, and the host only relays frames to it over a Unix socket in a 0700 folder. The token never enters a process Chrome starts.
- TCC. launchd processes may not read
~/Documents, so the runtime is installed under~/Library/Application Support/ira-mac/and phase 1 reads nothing under~/Documents. - Keychain. Service
ira-mac.securityreceives commands on stdin (security -i), so the token is never in argv, a file or a log. - Watches run in the daemon: it reads the tab every few seconds and ends the watch on the text, idle time, 100%, a newly shown "Completed" or "Failed", the tab closing or the timeout. At most 5.
Downloads¶
/v1/downloads/ serves the Chrome extension zip and the Mac installer zip
from the package, and a release workflow attaches the same files to GitHub
Releases. Nothing is signed yet; no signing key is in the repository.