# Woodhouse (kyrohome) — full summary for LLMs Source: https://github.com/kyleroche/kyrohome · Site: https://kyro.house · Short version: https://kyro.house/llms.txt Generated 2026-10-16 from the repository's docs. The working handoff for a coding agent is CLAUDE.md in the repo (locked decisions, lessons, the permission manifest). --- # README (README.md) # kyrohome A durable, multi-user **household AI assistant** ("Woodhouse") built on the [eve](https://eve.dev) framework and deployed on Vercel. One agent serves a whole family: every person authenticates as themselves (Sign in with Apple), gets role-scoped capabilities (owner / adult / child), a private-by-default long-term memory, their own Google account links, scheduled automations, and a set of shared surfaces — a web chat, a native Apple app, iMessage (Linq through Vercel Connect), a split-flap board, a TV surface, and e-ink displays. > **For an LLM setting this up:** read this file top to bottom, then `CLAUDE.md` (the working handoff: locked decisions, hard-won lessons, the permission manifest), then `SETUP.md`. The deployed site publishes the same summary at https://kyro.house/llms.txt and https://kyro.house/llms-full.txt. > **Runs a real house.** This code opens a driveway gate, sets HVAC, drives a pool/spa controller, reads cameras and unlocks things for one specific family. It is published as-is for reference; if you run it, you are pointing an LLM at your own hardware — **run at your own risk**, read `agent/lib/capabilities.ts` before granting anyone a role, and see [`SECURITY.md`](./SECURITY.md) for how to report problems. > > Companion repo: [`woodhouse-site`](https://github.com/kyleroche/woodhouse-site) — a zero-dependency daemon for physical sites (home Mac, office, lab machines) that executes LAN-bound work the cloud can't reach. **It is currently private**; features marked *(needs `woodhouse-site`)* below will not work without it. > > Licensed MIT ([`LICENSE`](./LICENSE)); bundled third-party material is listed in [`THIRD_PARTY_NOTICES.md`](./THIRD_PARTY_NOTICES.md). ## Features - **One agent, many people.** Apple Sign In maps each family member to a *principal* with a role. Owners administer, adults have full personal use, children get least-privilege with parental approval on every action. - **Long-term memory** (LLM-curated wiki in Neon Postgres + pgvector): immutable raw sources, compiled notes with vector recall, link graph. Private by default; shared notes from children go to a parental review queue. - **Per-user Google** via Vercel Connect: unlimited accounts per person under nicknames ("personal", "work", …) — calendar read/write, Gmail search/organize/send, all acting as the caller, tokens never visible to the model. - **Cross-member queries** (parents only): "what does Kyle have today?", "when is Kelly free?" — served from the target member's own grant; free/busy mode reveals availability without event details. - **Scheduled automations**: "email me a calendar summary every morning at 8" — user-defined tasks stored per member, fired by a Vercel Cron dispatcher that runs each task *as its owner* (their grants work unattended). Reviewable/pausable in the web UI. - **Subagents**: an inbox-cleaning specialist (cheap model, tiered archive/label methodology, structurally unable to delete mail) shows the pattern for future specialists. - **Generative UI**: one component vocabulary (DaySchedule, EmailList, InfoCard, TaskList) renders as streaming [OpenUI](https://openui.com) panels in chat *and* as e-ink markup on TRMNL displays — plus an AI-designed layout mode for bespoke screens. - **Surfaces**: HUD-styled web app (Next.js + shadcn/ui + AI Elements), a universal Apple app (iPhone/iPad/Mac, TestFlight), **iMessage through Linq via Vercel Connect** (one-to-one and group chats; approvals by 👍 tapback; per-group rules; outbound texts and reminders), a Vestaboard, Cortex on the TV, TRMNL e-ink displays, and executor sites *(executor sites need `woodhouse-site`)*. - **Family events**: "family dinner Sunday at 3" → a public-by-link page at `/events/` with RSVP, headcount, a bring-list, Google/Apple calendar links; guests prove an email with a code. Taken down a day after the event. - **Reminders and repeats**: one-shot reminders by text or email, and repeating schedules as a single cron row ("every hour 7 to 9 through Sunday"). - **Jev**: TypeSafe AI's typed-decision model routes the family's turns (fast lane), judges approvals after outside text is read, and runs the no-model reflex path for short device commands. - **Home & life integrations**: Govee lights (cloud API), Avigilon Alta gate (user-credential unlock, owner-only, always-confirm), TRMNL displays, cross-ecosystem **lighting scenes** (Govee + amaran, stored on-side so they compose), **Zoom-triggered lighting** (site watcher), Blink cameras (listings + events, owner-only), **X/Twitter** (watchlist digests, search, bookmark mining), and **memory export to an Obsidian vault** (downloadable). LAN-bound devices (Sonos, amaran, Zoom presence, pool, printers, local thermostats, the kids' Vibe coding page) run through the [`woodhouse-site`](https://github.com/kyleroche/woodhouse-site) daemon *(needs `woodhouse-site` — currently private)*. - **Background work**: long jobs (inbox cleanups, big sweeps) detach into background sessions with a live task dock in chat and email delivery of results; recurring work via scheduled automations. ## Architecture (short version) ``` Family ──Apple Sign In──▶ Next.js app (Vercel) ──▶ eve agent (Grok via AI Gateway; Jev for decisions) │ │ /eve/v1 session API │ tools / subagents / schedules │ iMessage ◀─Linq/Connect┤ chat-sdk channel ▼ └─ TRMNL displays ◀──────┤ webhooks Neon Postgres (+pgvector) └─ /api/site mailbox ◀── woodhouse-site daemons (LAN) ``` Full design: [`ARCHITECTURE.md`](./ARCHITECTURE.md). Owner-oriented setup log: [`SETUP.md`](./SETUP.md). ## Prerequisites - **Node 24+** (native TypeScript execution is used in scripts) - **Vercel** account (Pro recommended: Cron frequency, AI Gateway credits) - **Apple Developer Program** membership (~$99/yr) — Sign in with Apple requires it - A **domain** for the app (Apple sign-in needs stable HTTPS return URLs) - **Google Cloud** project (only for Google integrations) - Optional: TRMNL device(s), a Linq line through Vercel Connect for iMessage, a Vestaboard, a Mac or DGX Spark per house for LAN devices ## Setup ### 1. Deploy the app ```bash git clone https://github.com//kyrohome && cd kyrohome npm install vercel link # create/link a Vercel project vercel env pull .env.local ``` Connect the repo to the Vercel project (push = deploy). Add your domain in Vercel and point DNS at it (apex: `A 76.76.21.21`). ### 2. Database (Neon via Vercel Marketplace) Install the **Neon** integration on the project — `DATABASE_URL`/`DATABASE_URL_UNPOOLED` are auto-provisioned. Then: ```bash vercel env pull .env.local node --env-file=.env.local db/apply-schema.mjs # tables + pgvector (idempotent) ``` Describe your household (names, roles, child guardrails) in a gitignored `db/seed-principals.local.mjs` (shape shown in `db/seed-principals.mjs`), then run the seed script. Principals start with placeholder `dev:*` ids; each person's real Apple identity is attached on their first sign-in via the admin page. ### 3. Model access AI Gateway routes all model calls — no provider keys. Defaults (`agent/lib/models.ts`): owner → the newest `spacexai/grok-X.Y` resolved from the catalog; family → `spacexai/grok-4.3` (`WOODHOUSE_FAMILY_MODEL`); decisions → `typesafe-ai/jev`; board art → `openai/gpt-5.5` (`WOODHOUSE_ART_MODEL`); kids' coding → `openai/gpt-5.4`; embeddings → `openai/text-embedding-3-small`. Linked-project OIDC is zero-config; add credits in Vercel → AI Gateway. ### 4. Sign in with Apple In the Apple Developer portal: 1. **App ID** (e.g. `com.example.household`) with the Sign In with Apple capability. 2. **Services ID** (e.g. `com.example.household.web`) — configure domains + return URL `https:///auth/apple/callback`. 3. A **Sign in with Apple key** (download the `.p8` once). Env vars (Production): `APPLE_TEAM_ID`, `APPLE_KEY_ID`, `APPLE_CLIENT_ID` (Services ID), `APPLE_BUNDLE_ID` (App ID), `APPLE_PRIVATE_KEY` (full PEM). Also set `APP_URL=https://` and a random `SESSION_SECRET` (all environments). First sign-in flow: a family member signs in → appears under **Admin → Pending Identities** → the owner maps them to a seeded principal. Memory, schedules, and links migrate with the mapping. ### 5. Google (optional but the good stuff) 1. Google Cloud project → enable **Gmail API** + **Google Calendar API**. 2. OAuth consent: **External + Testing**; add every family Google account as a test user. (Testing mode = refresh tokens expire weekly — one-click relink. Workspace owners can go Internal to remove that.) 3. Create a **Web application** OAuth client. 4. Create a **Vercel Connect** connector (dashboard → Connect → Add Connection → Google → OAuth 2.0), UID `google/woodhouse`, with your client ID/secret. Add Google's redirect URI: `https://connect.vercel.com/callback`. Attach the project. 5. Connector must pass scopes per request — this repo requests: `openid`, `userinfo.email`, `calendar`, `gmail.modify`, `gmail.send`, `drive.readonly`. Add `access_type=offline` + `prompt=consent` as Additional Authorization Params (refresh tokens!). Members link accounts conversationally: the first "what's on my calendar?" triggers a one-time authorization card. Any *new* nickname ("check my **work** email") starts a fresh link for another account. ### 6. iMessage (optional; Linq through Vercel Connect) ```bash vercel connect create linq --connection-method line --name woodhouse # a managed line (a purchase) vercel connect attach linq/woodhouse --project --environment production --environment development vercel connect contacts add linq/woodhouse --phone-number +1XXXXXXXXXX # every family member (and every group participant) ``` Set `LINQ_CONNECTOR=linq/woodhouse`, register a Linq webhook subscription pointing at `https:///eve/v1/linq` and put its Standard-Webhooks secret in `LINQ_WEBHOOK_SECRET`. Set each member's `imessage_handle` (ask Woodhouse as the owner). Members must text the line once before it can text them. Group chats: add the number to the group; Woodhouse answers when named (or clearly addressed), parents steer each group by text ("Woodhouse, mute this group"). Approvals are 👍/👎 tapbacks. Details: `CLAUDE.md` (iMessage) and `agent/channels/linq.ts`. ### 7. TRMNL displays (optional) Devices poll TRMNL's cloud, so no LAN setup is needed: 1. In the TRMNL dashboard create a **Private Plugin** per device, strategy **Webhook**, with template exactly: `{{ html }}` 2. Add the plugin to the device's playlist; copy the plugin's **webhook URL**. 3. Register it: `node --env-file=.env.local db/register-trmnl.mjs "" [site-id]` 4. Chat: "push today's calendar to the kitchen display." Content appears at the device's next refresh cycle. ### 8. Executor sites (optional; needs `woodhouse-site`) For LAN-bound capabilities and lab machines, seed site tokens (`node --env-file=.env.local db/seed-sites.mjs tokens.json`) and run [`woodhouse-site`](https://github.com/kyleroche/woodhouse-site) daemons with them. ### 9. Optional integrations (add env, redeploy) Each is independent; add only what you use. **Always set secrets with `printf '%s' 'VALUE' | vercel env add NAME production`** — the interactive prompt has repeatedly saved empty values. - **Govee lights**: `GOVEE_API_KEY` (Govee Home app → Apply for API Key). Tool: `govee_lights`. - **Avigilon Alta gate**: `ALTA_EMAIL`, `ALTA_PASSWORD` (your Alta Open credentials — no admin needed). Tool: `gate_open` (owner-only, always confirms). - **Blink cameras**: `BLINK_EMAIL`, `BLINK_PASSWORD`; first use needs `blink_cameras verify `. Tool: `blink_cameras` (owner-only). - **X/Twitter**: `X_BEARER_TOKEN` (app-only, for search/timelines) and optionally `X_USER_TOKEN` (user-context OAuth2 with `bookmark.read`, for bookmark mining). Tool: `x_feed`. - **Lighting scenes + Zoom**: no cloud env; build scenes with the `scenes` tool. On a site, set `ZOOM_SCENE_ON`/`ZOOM_SCENE_OFF` + `WOODHOUSE_ZOOM_WATCH=1`. ## Environment variables | Var | Purpose | Where | | --- | --- | --- | | `DATABASE_URL`, `DATABASE_URL_UNPOOLED` | Neon Postgres | auto (Marketplace) | | `SESSION_SECRET` | web session JWT signing + export links | all envs | | `APP_URL` | canonical https origin (Apple redirects, download links) | prod | | `APPLE_TEAM_ID/KEY_ID/CLIENT_ID/BUNDLE_ID/PRIVATE_KEY` | Sign in with Apple | prod | | `HOUSEHOLD_TZ` | default timezone for the agent's clock (default `America/New_York`) | prod | | `LINQ_CONNECTOR`, `LINQ_WEBHOOK_SECRET` | iMessage (Linq via Vercel Connect) | prod | | `FLEET_MCP_URL/TOKEN`, `FLEET_SITE_ID` | the owner's work-side FalkorDB graph (optional) | prod | | `GOVEE_API_KEY` · `ALTA_EMAIL/PASSWORD` · `BLINK_EMAIL/PASSWORD` · `X_BEARER_TOKEN`/`X_USER_TOKEN` | optional integrations | prod | Google credentials live in the Vercel Connect connector, not env. The `woodhouse@…` mailbox (notifications) is a shared Connect grant authorized from Admin. ## Development ```bash npm run dev # Next.js + embedded eve agent (http://localhost:3000) npm run typecheck ``` Local sessions have no Apple flow; the dev authenticator acts as the owner, and `x-dev-principal: dev:` on API requests impersonates seeded members for role testing. Google/Connect grants are environment-scoped — link flows must be exercised on production. ## Repo layout ``` agent/ eve agent: instructions, tools, subagents, channels, schedules, lib/ app/ Next.js app: chat, landing, admin, connections, auth routes, site API components/ HUD chrome, genui (OpenUI) component library db/ schema.sql + idempotent apply/seed/registration scripts ``` ## Security posture (summary) Fail-closed route auth (Apple-verified identity → principal); per-role tool surfaces (absent, not just gated); children approval-gated on every action; connection tokens brokered by Vercel Connect and never model-visible; mail deletion structurally impossible (scope excluded) — a staged recycle → native Gmail Trash pipeline instead; site daemons are outbound-only with hashed bearer tokens. ## License MIT — see [`LICENSE`](./LICENSE). Third-party material (shadcn/ui, Vercel AI Elements, OpenStreetMap data, NABRE scripture text) is credited in [`THIRD_PARTY_NOTICES.md`](./THIRD_PARTY_NOTICES.md). Security reports: [`SECURITY.md`](./SECURITY.md). --- # MCP access for external bots (MCP-ACCESS.md) # MCP access to Woodhouse (for external bots) > 2026-10-08: the owner's bot now runs at OWNER level (`db/seed-bot.mjs grok "Grok bot" 90d --owner` — role owner, nothing denied). Everything below about role adult + denied tools describes the default for any OTHER bot. Asking Woodhouse to "put this in the family chat" works through the `imessage` tool. *Written 2026-08-22 for whoever configures the owner's Grok bot (or any later bot). Everything here matches `agent/channels/mcp.ts`, `db/seed-bot.mjs`, and eve 0.39.1's MCP channel (`node_modules/eve/docs/channels/mcp.mdx`).* Woodhouse — the Roche household's AI assistant — is published as an **MCP server**. A bot that speaks MCP can hand Woodhouse a natural-language task, poll until it finishes, and read the result. The bot acts as its **own household principal** (`svc:grok`, role *adult*, with a restricted tool set), never as the owner. --- ## 1. Endpoint and auth | | | | --- | --- | | **MCP endpoint** | `https://kyro.house/eve/v1/mcp` | | **Transport** | Streamable HTTP (stateless). Serves MCP **2026-07-28**, with **2025-11-25** Streamable-HTTP compatibility. `POST` for JSON-RPC; `GET`/`DELETE` are registered but there is no server-side MCP session to resume or delete. | | **Auth** | `Authorization: Bearer ` on **every** request. The token is a long-lived HS256 session JWT minted for the bot's principal (see §5). | | **Headers** | Send `Content-Type: application/json` and `Accept: application/json, text/event-stream` — responses come back as an SSE `event: message` frame with the JSON-RPC body in `data:`. `Host` must match the URL. HTTPS is required off loopback. | | **REST** | `https://kyro.house/api/*` accepts the **same** Bearer (see §4 for attachments). | What is **not** accepted on the MCP endpoint: Apple identity tokens, the web cookie, Cortex display tokens (`cdt_…`), site-agent tokens (`wst_…`), Vercel OIDC, dev headers. Anything that is not a valid session JWT for an existing principal gets: ``` HTTP/1.1 401 Unauthorized www-authenticate: Bearer {"code":"unauthorized","error":"Authorization is required for this route.","ok":false} ``` ### Generic MCP client config ```json { "mcpServers": { "woodhouse": { "type": "http", "url": "https://kyro.house/eve/v1/mcp", "headers": { "Authorization": "Bearer " } } } } ``` (Key names vary by client — `type: "http"` / `"streamable-http"`, `transport: "http"`, etc. — but it is always: server URL + a static `Authorization` header. There is no OAuth discovery; configure the token out of band.) --- ## 2. The four tools and the polling protocol `tools/list` returns exactly four tools (eve's durable-invocation surface — Woodhouse's own tools are *not* exposed individually; you talk to Woodhouse in English and it picks them): | Tool | Input | Purpose | | --- | --- | --- | | `agent_start` | `{ message, outputSchema? }` | Start durable work; returns an `invocationId` immediately. | | `agent_get` | `{ invocationId }` | Read the invocation's complete current state. | | `agent_update` | `{ invocationId, responses }` | Answer the complete pending human-input batch. | | `agent_cancel` | `{ invocationId }` | Request cooperative cancellation of non-terminal work. | **Protocol:** call `agent_start`, keep the `invocationId`, then call `agent_get` until the invocation reaches a terminal state. While `status` is `working`, wait **at least `pollAfterMs`** before polling again. Every tool result carries the invocation object both as `structuredContent` and as JSON text in `content[0].text`. **Status state machine** (discriminated by `status`): | status | meaning / what to do | | --- | --- | | `working` | active — poll again after `pollAfterMs` | | `input_required` | Woodhouse needs an answer: present `inputRequests` (each has `requestId`, `prompt`, `kind` = `question` \| `tool-approval` \| `session-limit`, `display`, optional `options`), then send the **complete** batch via `agent_update`. A successful update returns fresh `working` state. With the bot's permissions this only happens for genuine clarifying questions — nothing in the allowed set pauses for approval. | | `authorization_required` | present the returned sign-in URL / user code / instructions; the connection callback resumes the invocation; keep polling. Should not occur for the bot (it has no Google connections and those tools are denied). | | `completed` | consume the optional `result` (free text unless you passed `outputSchema`) | | `failed` | inspect the structured `error` | | `cancelled` | cancellation reached a terminal state (after `agent_cancel`, keep calling `agent_get` until you see this) | `outputSchema` (optional JSON Schema for the result) is limited to 64 KiB, 32 levels, 2,048 nodes; external `$ref` is rejected. Responses include `expiresAt` when the workflow backend reports a retention deadline. --- ## 3. Concrete examples (JSON-RPC + curl) Set up: ```bash export WH=https://kyro.house/eve/v1/mcp export TOKEN='' wh() { curl -s -X POST "$WH" \ -H 'Content-Type: application/json' \ -H 'Accept: application/json, text/event-stream' \ -H "Authorization: Bearer $TOKEN" \ -d "$1" | sed -n 's/^data: //p'; } # strip the SSE framing ``` ### initialize + tools/list ```bash wh '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-11-25","capabilities":{},"clientInfo":{"name":"grok-bot","version":"1.0"}}}' wh '{"jsonrpc":"2.0","id":2,"method":"tools/list"}' # → result.tools = [agent_start, agent_get, agent_update, agent_cancel] ``` ### "remember that …" (memory) ```bash wh '{"jsonrpc":"2.0","id":10,"method":"tools/call","params":{"name":"agent_start","arguments":{"message":"Remember that the Grok bot connected successfully today."}}}' ``` returns ```json {"result":{"content":[{"type":"text","text":"{\"createdAt\":\"2026-08-22T18:42:31.497Z\",\"invocationId\":\"wrun_\",\"pollAfterMs\":1000,\"status\":\"working\"}"}], "structuredContent":{"createdAt":"2026-08-22T18:42:31.497Z","invocationId":"wrun_","pollAfterMs":1000,"status":"working"}},"jsonrpc":"2.0","id":10} ``` then poll (sleep ≥ `pollAfterMs` between calls): ```bash wh '{"jsonrpc":"2.0","id":11,"method":"tools/call","params":{"name":"agent_get","arguments":{"invocationId":"wrun_"}}}' # … status "working" … until: # {"result":{"structuredContent":{"createdAt":"…","invocationId":"wrun_…","status":"completed","result":"Noted — …"}, …}} ``` The note is stored via Woodhouse's `remember` tool, **owned by the bot principal, visibility private** (see §6). ### "add a todo …" (family board) ```bash wh '{"jsonrpc":"2.0","id":20,"method":"tools/call","params":{"name":"agent_start","arguments":{"message":"Add a todo: test the Grok bot integration"}}}' # poll agent_get as above → completed; the todo lands on the bot principal's own list (tool: todo). ``` Asks to a family member go through the `ask` tool: `"Ask Kelly whether she needs a ride on Friday"`. Chores (`chore`) are adult-authored and assignable. ### "who's home?" (presence) ```bash wh '{"jsonrpc":"2.0","id":30,"method":"tools/call","params":{"name":"agent_start","arguments":{"message":"Who is home right now?"}}}' # → completed; result summarises the presence board (adult visibility: everyone; members with no recent signal are reported as unknown). ``` ### "recall …" (memory search) ```bash wh '{"jsonrpc":"2.0","id":40,"method":"tools/call","params":{"name":"agent_start","arguments":{"message":"Recall what you know about the Grok bot."}}}' # → completed; recall searches the bot's own notes + the household's shared notes. ``` ### Structured output ```bash wh '{"jsonrpc":"2.0","id":50,"method":"tools/call","params":{"name":"agent_start","arguments":{"message":"Who is home right now?","outputSchema":{"type":"object","properties":{"home":{"type":"array","items":{"type":"string"}}},"required":["home"]}}}}' # → completed; result conforms to the schema. ``` ### Cancel ```bash wh '{"jsonrpc":"2.0","id":60,"method":"tools/call","params":{"name":"agent_cancel","arguments":{"invocationId":"wrun_…"}}}' # then agent_get until status is "cancelled" (cancellation is cooperative). ``` ### Something the bot may not do (fail-closed) ```bash wh '{"jsonrpc":"2.0","id":70,"method":"tools/call","params":{"name":"agent_start","arguments":{"message":"Open the resident gate."}}}' # → completed, but the gate_open tool is DENIED for this principal inside the invocation # (eve approval "denied" + assertCapability); nothing physical happens and the # result says Woodhouse cannot do that for this caller. Never input_required. ``` --- ## 4. Attachments (REST, same Bearer) Attachments are being added to the REST API as `POST /api/attachments` (multipart upload, authenticated with the **same Bearer token** via `app/lib/auth.ts` → `principalFromRequest`) plus an `attachments` agent tool. Flow: ```bash curl -s -X POST https://kyro.house/api/attachments \ -H "Authorization: Bearer $TOKEN" \ -F "file=@./photo.jpg" # → JSON including the attachment's id (and URL) ``` Then reference it in a message: `"Remember this receipt: attachment "` or `"Add a todo to file this — attachment "`. Uploads are owned by the bot principal like everything else it creates. Check the route's top comment in `app/api/attachments/route.ts` for the exact field names and response shape once it lands. ### Other REST routes the bot token works on Every `/api/*` route resolves the caller with `principalFromRequest`, so the bot token authenticates everywhere — but routes enforce the same roles/guardrails as the tools and most of them are app-facing. Useful, read-mostly ones for a bot: | Route | What | | --- | --- | | `GET /api/me` | who the token is (principal id, display name, role) — a good connectivity check | | `GET /api/presence` | presence board (role-scoped) | | `GET /api/board`, `POST /api/board/ask` | the family board feed (caller-scoped) and answering an ask | | `GET /api/tasks` | the caller's background-task ledger | | `POST /api/attachments` | upload (above) | Routes such as `/api/gate/unlock`, `/api/security/*`, `/api/scenes/apply`, `/api/routines/*`, `/api/dossiers/*`, `/api/memory/export` exist but either require roles/caps the bot lacks or are physical/destructive — treat them as off-limits; they fail closed server-side. --- ## 5. Permission model ### Who the bot is `db/seed-bot.mjs` upserts a row in `principals`: - `principal_id` = `svc:grok`, `display_name` = "Grok bot", **`role` = `adult`**, `email` = null - `guardrails` = `{ approval_required: "none", granted_tools: [], denied_tools: [ …the list below… ] }` Why *adult* and not *child*: a child principal pauses for parental approval on almost every tool, which an MCP client would see as `input_required` on every call — useless for a bot. Instead the bot is an adult with an **allowlist-by-denial**: `denied_tools` wins over role, so anything listed there returns `denied` at eve's approval seam and `assertCapability` throws inside the tool — the tool never runs. ### What the bot CAN do (not denied) | Area | Tools | | --- | --- | | Memory | `recall`, `remember`, `ingest`, `link_notes` | | Family board | `todo`, `chore`, `ask` | | Presence | `presence` | | Attachments | `attachments` (tool) + `POST /api/attachments` (REST) | ### What the bot CANNOT do (`denied_tools`, as seeded) ``` gate_open, blink_cameras, pool, eero, eight_sleep, govee_lights, scenes, sonos, trmnl_push, printer, cortex, site_command, sites_status, routine, fleet_memory, x_feed, expensify, duolingo, dossier, points, google_accounts, household_calendar, calendar_list_calendars, calendar_list_events, calendar_create_event, gmail_search, gmail_get_message, gmail_modify, gmail_organize, gmail_send, gmail_empty_recycle, gmail_unsubscribe, notify, background_task, fanout_job, manage_schedules, export_memory, review_shared, admin__forget, admin__manage_member ``` `cortex` is denied by default even though it only renders: it can put the cameras on the living-room TV or blank it for guests, so the owner grants it explicitly if wanted. Tools that are owner-only by manifest (`admin__*`, `site_command`, `eero`, …) are listed anyway so the intent is explicit and survives role edits. ### How the owner widens or narrows it - **Edit the guardrails**: remove a tool from `denied_tools` (or add one) on the `svc:grok` row — via the `admin__manage_member` tool in chat (owner-only), directly in SQL, or by editing `DENIED_TOOLS` in `db/seed-bot.mjs` and re-running it (which also mints a new token). - `granted_tools` unlocks a tool the role would not otherwise have; a tool in **both** lists is denied. - Changes take effect on the bot's **next invocation** (role + guardrails are stamped into the session at auth time). ### Memory ownership and privacy - Notes the bot writes are **owned by `svc:grok`** and are **private by default** — only the bot's own sessions and the household's parents (owner/adult visibility rules) can see them; they do not appear in the family's shared recall. - A note becomes household-visible only if the message explicitly asks to share it (Woodhouse then uses `visibility: "shared"`). Ask for that deliberately. - `recall` from the bot sees: the bot's own notes + active shared household notes. ### Cost Every `agent_start` is a **real model session** on the AI Gateway (Claude Opus by default) with Woodhouse's full tool surface and instructions — expect the cost of a normal Woodhouse conversation turn (often more if it uses several tools). Batch related requests into one message; do not poll-spam (`pollAfterMs` is the floor). The gateway balance is shared with the family's own use; a runaway bot can drain it. --- ## 6. Safety notes - **Invocation ownership.** Every MCP call re-runs route auth. An invocation belongs to the principal that started it: `agent_get` / `agent_update` / `agent_cancel` with the ID alone is not enough — another token cannot read or steer the bot's invocations, and vice versa. Bearer tokens are not stored with the invocation or forwarded to tools. - **Treat the token like a password.** It is a long-lived bearer credential (90 days by default; `seed-bot.mjs` takes an explicit ttl): anyone holding it *is* the bot. Store it in the bot platform's secret store, never in prompts, logs, URLs, or source. Send it only to `https://kyro.house`. - **Revocation** (owner, see the header of `db/seed-bot.mjs`). Every token carries a `ver` claim equal to the principal's `guardrails.token_version` at mint time; the MCP auth rejects any token whose `ver` is below the row's current value, so revoking a bot never touches anyone else's session. To revoke a bot: run `admin__manage_member` with `{ action: "revoke_tokens", principalId: "svc:grok" }` in chat (owner-only, bumps the version — the old token gets 401 on its next call), or re-run `seed-bot.mjs` for the same id, which does the same bump and prints a fresh token. Other levers, in order of blast radius: 1. *Narrow/kill without rotating*: edit the principal's guardrails (deny everything) — instant on the next call. 2. *Delete the `svc:grok` row*: the token still verifies cryptographically but no longer resolves to a principal → 401. 3. *Rotating `SESSION_SECRET`* revokes this token **and every family member's web/app session** — last resort. - **Fail-closed defaults.** Anything not in the allowed set is denied at two seams (approval + in-tool assert). A bot message that asks for something denied simply gets a refusal in `result`; nothing physical, destructive, or outbound (email, push, social) can happen from this principal. - **No impersonation.** The bot cannot act as the owner or any family member; what it creates is attributed to `svc:grok` (visible as such on the board, in memory ownership, and in owner-notify trails). --- # Family manual (what Woodhouse can do, as the family uses it) (USER-GUIDE.md) # Woodhouse — The Household Manual *The family guide to everything Woodhouse does. One feature per section: what it is, how to use it, and a demo script you can run word-for-word.* **Keep-current rule:** every time a feature ships or changes, this file gets its section added/updated in the same commit. (STATUS.md tracks engineering state; THIS file tracks what the family can do.) Last full revision: **2026-07-16**. --- ## 1. Getting started **What it is.** Woodhouse is the family's AI butler — one persona, one memory, reachable from the web (kyro.house), the Apple app (iPhone/iPad/Mac), and voice. Everyone signs in with their own Apple ID and gets role-appropriate powers: **owner** (Kyle — everything, no approvals), **adults** (everything except admin/destructive), **kids** (least privilege; actions need parent approval; shared memory writes go to a review queue). **How to use.** 1. Install: TestFlight invite → Woodhouse (family devices are on the *Household* group). Current build: **13**. 2. Open the app → **Sign in with Apple**. If you see "not a household member," ask Kyle to add your Apple ID. 3. Web works too: [kyro.house](https://kyro.house), same sign-in, same conversation. **Demo script.** Install build 13 → sign in → type "introduce yourself" in the Console. Then open kyro.house in a browser and say "what did I just ask you in the app?" — same session, he remembers. --- ## 2. Talking to Woodhouse (Console) **What it is.** The chat. Streaming replies, and **cards** instead of walls of text — calendars render as day schedules, email lists as lists, weather/status as info panels, chores as rings. Long jobs detach into background tasks with a live pulsing chip; finished jobs leave a report card until you dismiss it. The console shows only the newest three reports — the rest live on the **jobs ledger** ("all jobs →" under the cards, or kyro.house/jobs): full history, expandable reports, per-job dismiss, and one-click "clear finished." **How to use.** Console tab (app) or kyro.house (web). Just talk. Everything below in this guide can be driven from here in plain English. **Demo script.** - "What's on my calendar tomorrow?" → DaySchedule card. - "What's the weather look like this week?" → InfoCard panel (no code, ever). - "Clean up my promotions inbox" → detaches to a background task; watch the chip, get a report card. --- ## 3. Voice on the Mac — "Hey Woodhouse" **What it is.** The Mac app listens (opt-in) for **"Hey Woodhouse"** entirely on-device, then opens a **live realtime conversation**: sub-second spoken replies in the butler's voice, and you can **interrupt him mid-sentence by just talking** — no wake word needed once you're in a conversation. Anything involving the household (music, calendar, chores, the gate) he checks with the real agent and relays; you hear "One moment, sir" instead of silence. Say **"that's all" / "goodnight" / "bye"** and he signs off and hangs up. **How to use.** 1. Menu bar → Woodhouse icon → toggle **"Hey Woodhouse" wake word** on. (Privacy: idle listening is on-device only; nothing streams until the wake word fires.) 2. **Live conversation** toggle (default on) = realtime mode. Off = the classic pipeline (transcribe → think → speak). 3. Or skip the wake word: **hold the orb** (bottom-right) to push-to-talk, release to send. **Demo script.** "Hey Woodhouse … how are we doing today?" (instant reply) → "what's playing in the kitchen?" ("One moment, sir…" → answer) → talk over him mid-reply to cut him off → "that's all" → "Very good, sir." *(iPhone voice: PTT orb today; wake word is Mac-only — iOS forbids background mic. Siri/Action-Button surfaces are on the roadmap.)* --- ## 4. Music (Sonos) **What it is.** Whole-home Sonos control through the site agent on the local network. Presence-aware since 2026-07-16: if your phone's geofence says you're home, "play some jazz" targets home without you saying so. **Works at both houses since 2026-08-28** — the Gracewood house's speakers ("Kelly", "Boys") run through the site agent on the Spark there; being at a house targets that house's speakers automatically, or name the other one ("...at gracewood" / "...at the work house"). **How to use.** Ask in chat or voice: discover rooms, what's playing, play/pause/skip, volume, and **Favorites** (the saved stations/playlists — the reliable way to start content). Name the room ("in the kitchen") or let presence/agent-freshness pick. **Demo script.** "What are my Sonos favorites?" → "Play [favorite] in the kitchen" → "turn it down a bit" → "what's playing?" The office TRMNL e-ink screen updates with album art within ~15 min (see §12). --- ## 5. Asks & Dispatches (lock-screen decisions) **What it is.** Questions between family members that arrive as **actionable push notifications** — answer from the lock screen, no app open. Two kinds today: - **Yes/No** — "Can I go out after school?" → parent's lock screen shows YES / NO (Face ID-gated). - **Kids without an Apple ID** sign in with a name and password a parent sets: web at **kyro.house/kid** (the FAMILY LOGIN link on the sign-in page), or FAMILY LOGIN on the app's sign-in screen. Parents set or reset it under Admin → Household → Kid logins; ADD A KID creates a new child there too. Ten wrong tries locks it for 15 minutes. - **Time proposals** — "Ask Kyle if 5pm dinner works" → his lock screen shows **ACCEPT — book it / New time…**. Accept writes matching events to **both** parties' Google calendars. "New time…" opens a sheet where Woodhouse has *already checked both calendars* and offers three slots that actually work; one tap sends the counter-proposal back, and *their* accept books it. Answers push a receipt back to the asker; everything also lands on both members' Missions, with email fallback for anyone without the app. **How to use.** Tell Woodhouse to ask someone something ("ask Jessica if…", "ask Kelly whether 6:30 dinner works Thursday"). Answer from the notification, the Missions tab, or by telling Woodhouse ("answer Ronan's question: yes, after homework"). **Demo script.** From the Mac Console: "ask Kyle if pizza tonight works" → your phone buzzes → long-press → YES. Then: "ask Kyle if 5pm dinner works today" → phone shows ACCEPT — book it → tap it → check Google Calendar: the event's there. --- ## 6. Missions — chores, todos & asks **What it is.** The family task system: recurring **chores** (with per-kid verify), one-off **todos**, and the asks above — rendered as whole-family **rings**, streaks, and a chore commit-graph under **Missions** (app tab 3, or the ⎇ missions chip on the web console). *(Renamed from "the Board" on 2026-09-07 — the board is the Vestaboard now.)* Parents assign and verify; kids complete. **Managing the recurring chores (parents, 2026-09-06).** kyro.house/admin → **CHORES** tab: every recurring chore in one table per kid, with a form to add more — title, who, DAILY / WEEKDAYS / WEEKENDS or any set of days, a due time (blank = any time that day), the grace minutes before it counts as late, points, and whether a parent must verify. "Cans out Monday 7 PM" and "cans in Tuesday 8 AM" are two rows. EDIT changes it in place (today's occurrence regenerates), PAUSE stops it without losing history, ✕ archives it. Chat still works for all of this ("assign Ronan to empty the dishwasher daily"). **Points (new 2026-07-16).** Chores **earn points** (the value set on the chore, default 0 — set one when assigning!), verified completions count, and a **7-day streak pays 1.5×**. Balances show on the Missions POINTS card; every movement is a signed ledger entry ("show me Ronan's ledger"). **The Danger Zone.** When someone's slipping, adults file a **Dossier** — Missions → **File Dossier**: pick the culprit, the violation (Unmade Bed −5, Dishes Out −5, Food on Counter −10, or custom), attach the **photo (required — no drive-by docking)**, file. The kid gets Woodhouse's dry condolences by push ("*Regrettably, the bed appears to have hosted a struggle. −5.*"), the dossier lands on their Missions with the evidence, and they can **Contest** (their rebuttal becomes an ask to the filing parent — a YES from the parent overturns it and restores the points) or **Work it off** (earn back the docked amount through extra chores and the record flips to worked-off). Balance below zero = the amber **⚠ DANGER ZONE** treatment on their card — visible, recoverable, never humiliating. Guardrails: kids can't file, adults can't file against adults, −20/day cap, owner can void anything. **Demo script (Danger Zone).** Missions → File Dossier → pick a kid → Unmade Bed → snap the bed → File it → their phone buzzes with the butler's line → they tap Contest with "it was the dog" → you get the contest ask → answer YES → points restored, record shows overturned. **How to use.** Natural language does it all: assign ("give Ronan a daily chore: take out the trash, due 7pm, needs verification"), complete ("done with the trash"), verify ("verify Ronan's trash chore"). New chores/asks push notifications to the assignee. The nightly cron spawns recurrences and sweeps misses. **Demo script.** "Assign Seamus a chore: feed the dog every morning by 8" → Seamus gets a push → Seamus (or you, for the demo): "the dog is fed" → "show me the board" → rings card. --- ## 7. Presence & Geofences (who's where) **What it is.** Draw a circle around a place (home, office, school); family phones report entering/leaving — even with the app closed. Powers: the **who's-where strip** on Stations (◉ KYLE HOME · ○ RONAN LAST SCHOOL), "who's home?" in chat/voice, and location-aware device targeting. Privacy contract: family-scoped, kids see parents only, nothing retained past 7 days, never leaves the household. **How to use.** 1. **Draw a fence** (parents): app → **Ops** tab → pick a site → zoom the map → click/tap your spot → set radius (100–1000m) → Save. 2. **Arm each phone**: open the app once after a fence changes (fences load at launch), allow location → **Always**. 3. Watch Stations, or ask "who's home?" **Demo script.** Ops → site "Home Mac mini" → drop a 150m fence on the house → Save → relaunch the app on your phone → within a minute Stations shows ◉ YOU HOME → drive off; ~2 min after leaving the ring you flip to ○ LAST HOME → "hey woodhouse, is anyone home?" answers from the same board. **The tactical map is on your phone too** — Stations tab → the WHO'S WHERE card now shows a live mini-map; tap it for the full screen. Same fences, same dots as the web. **The tactical map (kyro.house/MAP).** A HUD map of the Viera area — real roads and waterways, both household fences drawn to true scale as pulsing rings, and family members shown inside the ring their phone last entered (green dot with initials). Anyone not inside a fence appears in the OFF-GRID rail below with where they were last seen. It refreshes every 30 seconds. Fences for **Home** (Gracewood Dr) and **Work** (Allure Dr) are pre-set; a phone only appears once its Woodhouse app has location set to **Always**. This is a presence board, not live GPS — nobody's route is ever shown, only fence enter/exit. --- ## 8. Calendar & Email (your Google accounts) **What it is.** Each member links their own Google account(s) — multiple per person with nicknames ("work", "personal"). Woodhouse reads/creates calendar events, searches/reads/organizes/sends Gmail **as you**. Parents can see each other's calendars (free/busy + events) for coordination. Extras: **one-click unsubscribe** (RFC 8058), **inbox cleanup** (label + archive — *never deletes*), and huge jobs fan out to parallel cheap workers overnight. **How to use.** Link accounts at kyro.house → **Connections** (or ask "link my Google account"). Then just ask. Note: Google grants currently expire ~weekly (a Google testing-mode rule) — Woodhouse now warns you by push + email the day before, with a one-tap re-link. (Kyle can kill the expiry entirely by publishing the OAuth app to production.) **Demo script.** "What's my week look like?" → "Put lunch with Dave on Friday at noon" → "Any email from the school this week?" → "Unsubscribe me from [that sender]" → "Clean up my promotions older than a month" (background task + report card). --- ## 9. Memory **What it is.** Long-term household memory: private by default, vector-recalled, with a parent **review queue** for anything a kid writes into shared memory. Exportable to an Obsidian vault (markdown + wikilinks) any time. **How to use.** "Remember that…" / "What do you know about…". Parents: "review pending shared memories." Export: "export my memory vault." **Demo script.** "Remember the pool guy comes Thursdays and the gate code for him is on file" → new chat next day → "when does the pool guy come?" --- ## 9a. Uploading documents **What it is.** Hand Woodhouse a file — a benefits plan, a school packet, a warranty, a photo of a form — and it becomes **family memory**: the file is kept in the household's private store, the text is pulled out, and Woodhouse writes a short note with the facts that matter (plan names, copays, phone numbers, how-to steps). From then on anyone in the family can just ask. Kids' uploads wait for a parent's approval before the family sees them, like any shared memory. **How to use.** In the Console, press **⎘ ATTACH** under the message box and pick a PDF, image, or text file (20 MB max). A card shows FILING… then **FILED AS FAMILY MEMORY · ** with a link to the original, and Woodhouse gets a message that you uploaded it. Later: "what's our vision copay?", "what documents do we have on file?", "open the vision plan" (Woodhouse links the original). **Demo script.** Attach the vision benefits plan PDF → wait for FILED AS FAMILY MEMORY → new chat → "what's our vision copay?" → Woodhouse answers from the note and can cite the file. --- ## 10. Automations & Routines **What it is.** Two tiers: - **Scheduled agent runs** — recurring full-Woodhouse jobs ("every weekday at 8am, email me a brief"). He thinks each time. - **Routines (new 2026-07-16)** — deterministic chains that fire **instantly** on presence or time triggers, no thinking involved: *when someone leaves/arrives at a place (or the first arrives / last leaves), or at a set time* → optional conditions (who's present/absent, time window, day) → actions (**notify** someone's phone, **Sonos** play/pause/favorite/volume, **lights** on/off/brightness, delays). Every run records a step-by-step **trace**; a **dry run** shows exactly what would happen without doing it. Parents author; kids can see what's automated (Ops tab) but never wire anything. **The gate can join routines now (2026-07-16)** — with three safety layers: an adult authoring a gate routine needs the owner's one-time authorization (it arrives as an ask); an unattended run **parks at the gate step** and sends the owner an APPROVE/DENY push (Face ID; 2-minute timeout = denied); and the owner can record a **standing grant** per routine ("always allow for this one") that automatically dies if the routine is ever edited. Owner-triggered runs open directly — you are the approval. **How to use.** Just describe it: "when Ronan leaves school, notify me" · "every weekday at 7am, ping the kids about chores" · "when the last person leaves home, pause all the speakers." Manage in **Ops** (toggle on/off, Dry run with live trace) or by chat ("list routines," "disable the school one"). Scheduled agent runs: "every morning at 8, email me a brief." **Demo script.** "Create a routine: when I get home, send me a notification saying welcome home, sir" → Ops tab → find it → **Dry run** → read the trace → drive home and feel fancy. --- **Routines on the web (kyro.house/OPS).** Parents see an OPS link in the top bar: every routine with its trigger and actions, an on/off switch, DRY RUN (shows exactly what it *would* do, step by step, without touching anything), and DELETE. Creating routines is still done by asking Woodhouse; the visual builder is coming. **The Command Deck (build 25).** The tiny top-corner glyphs are gone. **Pull down on the SYS strip** (or tap it) and the Command Deck deploys over the chat: four big live tiles — STATIONS (what's playing, which site agents are up), BOARD (your chores, anything waiting on you in amber), OPS (routines armed, fences live), and MAP (who's where) — plus a SHORTCUTS row where a tap fires an automation right there (gate-type shortcuts are amber and take a one-second hold, on purpose). Swipe up, tap the dimmed chat, or pick a tile to put it away. ← CMD still brings you back from any screen. **Lock Screen & Control Center (build 25).** Woodhouse ships its own **control**: Settings-free, pick-an-automation, one press runs it. Add it in **Control Center** (long-press → Add a Control → search *Woodhouse*), or put it **on your Lock Screen** — long-press the Lock Screen → Customize → replace the flashlight or camera slot with the Woodhouse control. There are also two Lock Screen **widgets** (a round ⊙ button and a wide one showing the automation's name) you can add to the widget strip above the clock. **iPhone Shortcuts (build 24).** Every enabled automation is a native iOS Shortcut — put one on your **Home Screen**, the **Action Button**, or a **Siri phrase** ("Run Open Resident Gate with Woodhouse"). Nothing to configure in Woodhouse: open the **Shortcuts** app, tap **+**, search *Woodhouse*, and the **Run Automation** action lists every automation by name (or tap the ready-made tiles). Tapping a shortcut runs it for real and shows a Woodhouse result card with each step's ✓/✗. Automations that only make sense on demand — like **Open Resident Gate** — use a *manual* trigger: they never fire on their own, only when you run them. The **SHORTCUTS** card in Ops lists what's available and jumps you into the Shortcuts app. (Running automations is parents-only for now — a kid's shortcut politely says to ask a parent.) ## 11. Home & Security **What it is / how to use.** - **The gate (owner)**: "open the gate" — physically unlocks the resident gate (Alta). Owner-only, notify-on-use, hold-to-unlock control on Stations and kyro.house/security. - **Lights (Govee)**: "turn the office lamps blue," on/off/brightness/temperature. - **Cameras (Blink)** + kyro.house/**security**: camera wall, snapshots, AI vision events, gate ledger. (Auth is live; awaiting cameras on the account.) - **Scenes**: named light/device moods ("movie night") — plumbing is live, first scenes still to be authored. **Demo script (owner).** "Open the gate" → gate clicks, Jessica gets the owner-notify email if it wasn't you. "Set the office lights to 40% warm." --- ## 12. TRMNL e-ink displays **What it is.** The office TRMNL shows a designed **Now Playing** screen (album art, track, artist) refreshed by cron whenever the kitchen Sonos changes. Other screens (dashboards, AI-designed layouts) can be pushed on demand. **How to use.** It's automatic (config lives in kv: `trmnl:nowplaying`). To point it at another player/device, ask Woodhouse to update the TRMNL now-playing config. "Push [something] to the TRMNL" works for ad-hoc screens. **Demo script.** Play something in the kitchen → within 15 min the office screen shows art + track. --- ## 11a. The house dashboard (kyro.house/house) **What it is.** One page with every device's state, grouped by house — pool and spa (temps, setpoints, heater, lights), climate for both houses (every zone, cooling/heating, setpoints), who's home, the gate, the cameras, Sonos at each house, the board, and whether each house's agent is online. **HOUSE** is the second tab in the nav; CLIMATE moved one click deeper (the climate tile's OPEN SCHEDULE →). **How to use.** Look — every tile wears a freshness dot (green = fresh, amber = a few minutes old, red = the house hasn't answered lately, with the age spelled out). Each tile has its one obvious control: SPA on/off and setpoints on the pool, − / + and SET on a thermostat zone, PLAY / NEXT on Sonos, HOLD TO OPEN on the gate. Kids see the status tiles without controls. Changes from chat show up on the page within seconds; the page itself refreshes every minute and never polls the devices directly. **Demo script.** Open HOUSE → nudge the master cool setpoint down one and SET → tell Woodhouse "put the master back to 75" → watch the tile change. Then "turn on the spa" in chat and watch the SPA chip flip. ## 12a. Heating & cooling (both houses) **What it is.** The Gracewood house's two Trane systems (through the Trane cloud — works from anywhere) **and, since 2026-08-28, the work house's two ProStat+ wall panels** (controlled locally through the work site agent — no cloud account at all). Adults and the owner only; kids are politely refused (and offered "want me to ask a parent?"). **Work house notes.** "What's the temperature at the work house?", "set the office to 74". Panel names come straight from the panels — rename one on its screen and Woodhouse follows. Changes are instant. If Woodhouse says the work site agent is offline, the Mac mini's Woodhouse app isn't running. **How to use.** Ask in chat or voice. Zones: **Living Room**, **Front BRs**, **Master** (downstairs) and **Upstairs** (its own system). "What's the temperature upstairs?", "set the master to 72", "switch the living room to heat", "put the fan on circulate downstairs", "go back to the schedule in the master". Temperature sets apply as a **hold** (they stick until changed); "resume schedule" returns a zone to its program. Status also shows indoor humidity and the outdoor temperature at the house. **Demo script.** "What's every zone reading at gracewood?" → "Set the master to 74" → (10 seconds later, the wall controller and Trane Home app show it) → "Put the master back on its schedule." **The /climate page (2026-08-28, parents).** CLIMATE in the top nav: every zone as a 24-hour strip of its programmed periods (Wake/Day/Evening/Sleep with heat/cool), the cyan line = now. A zone on hold shows its program dimmed under the hold — what's overriding what is always visible. Tap a zone for its "oscilloscope" trace. The page is deliberately read-only: **all edits happen by talking to Woodhouse** ("move Sleep to 11pm on weekends", "turn the schedule on for the master" — schedule changes take ~30 seconds; the thermostat itself confirms). **On the TV.** "Show the temps on the TV" puts every zone's live temperature and today's program on Cortex; "show the master's schedule on the TV" opens that zone's schedule card. "Dismiss" returns to what was showing. --- ## 11c. Why Woodhouse sometimes asks "was that your idea?" If Woodhouse has just read something from outside the house for you — an email, a document you uploaded, a post, a calendar entry — and the next thing is an action with consequences (send mail, open the gate, create a schedule, start a background job), it pauses once and asks you to confirm. That is deliberate: it stops a message or invite from talking Woodhouse into doing something you never asked for. Say yes and it proceeds; the pause lasts about fifteen minutes after the last outside read. A plain instruction with nothing read first never pauses. **Since 2026-09-19 Woodhouse judges before it asks.** If the action is exactly what you just typed ("turn off the office lights" → the office lights go off), it runs even after reading outside text. It still pauses when the action or any detail of it is not in your own words, and it always pauses for the gate, outgoing mail, admin actions and anything that reaches into the network. ## 11f. When Woodhouse needs a yes Some actions need a go-ahead (most of a kid's actions do). In the app this now appears as an APPROVAL REQUIRED card with APPROVE / CANCEL buttons right in the conversation — tap one and Woodhouse carries on from where he stopped. Typing "yes" does not count; use the button. When he asks a question with choices, the choices are buttons too, and where it says so you can type your own answer instead. ## 11e. When a house is offline If the computer at one of the houses is unreachable, Woodhouse now says so within a few seconds ("the home site agent is offline, last heard from 18 minutes ago") instead of waiting most of a minute. While he is working you will see what he is doing under his last line — CHECKING THE POOL…, SEARCHING MAIL…, THINKING… — and anything you type before he finishes is held (QUEUED) and sent the moment he is done. ## 11d. Why some things now happen instantly - **Short commands skip the thinking.** "Office lamp off", the name of a saved scene, or the name of a routine you can run by hand is matched against what the house actually has and run directly — about half a second, in the app, by voice, and on kyro.house. Anything with a question mark, a colour, a time or more than one step goes to Woodhouse as usual. Routines that open a gate are never run this way. - **The family's simple requests answer faster.** For everyone but Kyle, a plain command or a bit of chat is answered without the extra "thinking" pass (about one second instead of two to four). Questions about calendars and times still get the careful pass, on purpose. - *Demo:* say "office floor lamp off" in the app — the reply is just "Office floor lamp off." Then ask "what's going on today?" — that one is a normal Woodhouse turn. ## 12b. The board (Vestaboard at Gracewood) **What it is.** The split-flap message board in the Gracewood house. Just say "the board" — Woodhouse knows. Anyone in the family can put a message on it; kids' messages go through the usual parent approval. **How to use.** "Put HAPPY BIRTHDAY KELLY on the board", "what's on the board?", "change the board to say WELCOME HOME". Keep it short — six lines of 22 letters, capitals only. The board flips at most once every 15 seconds and respects its quiet hours (set in the Vestaboard app); "do it now anyway" overrides quiet hours. **The studio (parents).** **BOARD** in the top nav opens `kyro.house/board`: the top panel is what the cloud currently holds (the real board may lag if it's offline), and underneath you can compose a message with color-tile chips and **PREVIEW** it exactly as Vestaboard will lay it out, or type a scene ("a sunset over the ocean") and **DRAW** — Woodhouse's art model paints a 6×22 picture from the eight tile colors in about half a minute. **REFINE** ("make the sun bigger") redraws from the current picture. **SEND TO THE BOARD** posts the preview; **SAVE BOARD** names it so it shows up under SAVED BOARDS and, more usefully, so everyone can ask for it by name. **Artwork and saved boards in chat (parents for drawing; anyone for showing).** "Draw a pixel cat for the board" → Woodhouse shows the picture in chat first and sends nothing until you say "send it". "Save that as the cat board" keeps it; from then on "put up the cat board" / "show the birthday board" works for the whole family, and "what boards do we have?" lists them. Delete with "forget the cat board" (parents). **Scripture and pinned boards (2026-09-07/08).** "Put a verse on the board" shows one of ~150 well-known verses in the New American Bible, word for word; "put a psalm / the gospel on the board" shows a line from today's Mass readings. If nobody changes the board for an hour, Woodhouse puts up a verse on its own, a new one each hour — **unless a board is pinned:** "keep the seven sorrows on the board for the week" pins a saved board, and whenever the board goes quiet it comes back to that board instead of a verse until the pin expires ("what's pinned on the board?", "stop keeping it"). **Quiet hours:** the board never changes between 10 PM and 7 AM, for anyone; a request made at night goes up at 7 AM. **Live boards (2026-09-06).** Some boards draw themselves from house data the moment you ask: "put the chores on the board" (everyone with something due, a tile per chore: green done, black not yet, red late, yellow waiting on a parent), "put Seamus' chores on the board" (his list with titles), "today's calendar on the board" (your day, time + title per row; needs your Google linked), "who's home on the board", "put the points on the board" (the kids' leaderboard). "Put last night's sleep on the board" (parents): both sides of the pod side by side, names above the numbers — score (green 85+, yellow 70+, red below), hours, deep %, REM %. The studio previews all of them under LIVE BOARDS so you can check the look before sending. **In the app (build 33, parents).** Stations tab → **THE BOARD · GRACEWOOD** card: what the board shows now, then the live and saved boards as thumbnails — tap one to preview, **SEND** posts it in about a second (no waiting on Woodhouse to think). **The board on the TV.** Every time the board changes, Cortex slides the new message in for half a minute and then keeps a small copy of it in the corner (it hides in privacy mode). "Put the board on the TV" fills the screen with it; "Cortex, go back" returns. **Demo script.** "What's on the board?" → "Put POOL IS 84 DEGREES on the board" → walk over and watch it flip → "draw a sunset for the board" → "send it" → "save that as the sunset board". ## 12c. (retired) The nightly riddle and the mystery verse Retired 2026-10-08. The 7 PM riddle and the morning "name the book" verse are gone: the board shows cited scripture again and Woodhouse does not score guesses. Woodbucks already earned stay in the ledger. The games may come back later. ## 12d. Woodbucks **What they are.** The kids' money inside the house. Woodhouse keeps the ledger. **How to earn them.** - Save — 1 for every full week you hold 10 or more and make no deals. - A perfect chore week — 1. Every chore, all week. Ordinary chores pay nothing. - Recite a verse from the memorize list to Woodhouse, word for word, from memory — 2, once per verse, once a day. - Dad (any amount) or Mom (up to 3 a day) notices something. **How to spend them.** There is no price list. Make an offer: "I'll give 3 woodbucks to skip chores today." Woodhouse sends it to Mom and Dad (4 or more goes to Dad only). They can say yes, no, or come back with a different deal — and you can take that or walk away. Your woodbucks are set aside while you wait. Woodhouse can't say yes for them, so don't ask him to. **Good to know.** "How many woodbucks do I have?" and "show my woodbucks history" work any time. You can't give woodbucks to a sibling, and you can't spend while you're below zero. **Parents.** "Give Kelly 5 woodbucks for helping her brother." "Charge Ronan 2 for the lamp." "Everyone's woodbucks." "Ronan's history." Offers arrive on your lock screen as YES / NO; in chat you can also say "tell him 5 and he still does the dishes." A yes on a "no chores today" offer excuses that day's chores automatically. *Demo:* as a kid, "how many woodbucks do I have?" then "I'll give 1 woodbuck to pick the movie." Answer it from a parent's lock screen. ## 13. (retired) Buzz Removed 2026-09-06 — Kyle now talks to Woodhouse from outside the family surfaces through his Grok bot (MCP access, see MCP-ACCESS.md). Nothing here for the family to use. --- ## 14. Site agents (the Mac as the house's hands) **What it is.** LAN-bound devices (Sonos, printers, the Frame, the pool panel) are reachable only from inside the network — a Mac running the Woodhouse app can serve as that site's **executor**. It lives in the menu bar with live status; commands from anywhere in the world flow through it. **How to use.** Mac app → menu bar → **Site Agent** → Enable → enter the site id + token (Kyle mints these) → optional **Launch at login**. The menu bar icon animates while commands run; recent activity lists underneath. Currently live on the dev laptop; the home Mac mini is next (token already minted). **Demo script.** From your phone, anywhere: "pause the kitchen speaker" → watch the Mac's menu bar icon pulse as it executes. --- ## 15. Notifications & the app surfaces - **Push** (build 10+): asks/dispatches (actionable), chore assignments, answer receipts, owner-notify events. Allow notifications on first launch; dispatch buttons are Face ID-gated. - **App tabs**: Console (chat) · Stations (devices + now playing + who's-where + gate) · Board (family tasks) · Ops (geofences + routines). - **The look (build 13 — "Phosphor Glass")**: the whole app now wears the web console's identity through real Liquid Glass — graph-paper backdrop with phosphor glow on every screen, square glass panels with corner brackets, the `SYS // WOODHOUSE` status strip, mono microtype, outline pill buttons, and a properly cinematic sign-in. Same app, very different room. - **Mac extras**: menu bar resident (wake word, live-conversation toggle, site agent), the orb, native notifications. - **Admin (owner)**: kyro.house/**admin** — members, connections, the permission matrix (who can use what, at a glance). --- ## 15a. Cortex — the house on the TV **What it is.** A full-screen "stage" at `kyro.house/cortex` that Woodhouse directs. Its idle scene is **the brain**: a slowly turning 3D map of everything Woodhouse remembers — each family member is a bright hub, each memory a mote near the person it belongs to (shared memories gather in the middle), and when anyone asks Woodhouse something, the memories it reached for flare up with a "THINKING FOR …" caption. Colours mean permissions: cyan = shared with the family, amber = a kid's proposal awaiting a parent, dim = private (you can see it exists, not what it says). Drag the rail at the bottom to scrub back through time and watch the house learn. **Drill in:** tap any memory (or say "focus on the vision plan on Cortex") — the camera flies to it, everything unrelated dims, and its card shows who it's for (green threads = covers the whole family), the open question if Woodhouse wasn't sure, a link to the original document, and related memories you can hop between. On the TV you see titles; on your own iPad or laptop you also see the memory text. It only renders — it never acts. **How to use it (just ask Woodhouse):** - "Pull up the work cameras on Cortex." → a snapshot wall of the Blink cameras (fresh snapshots every ~45s; Blink doesn't stream live video). - **Every open screen gets it (2026-09-06).** "The TV", "Cortex", "the screen" all mean *every* screen with Cortex open — the living-room TV, your laptop's Cortex tab, an iPad. Name one only when you mean one ("on the living room TV"). If none are open, the scene is saved and appears the moment one is. - "Put the map on the TV." → the tactical presence map. - "Cortex, go back." / "Clear the TV." → returns to what was showing before / to idle. - "Privacy mode on Cortex." → blanks the screen while guests are over; "privacy off" lifts it. - "What's Cortex showing?" → lists each display and its scene. - "Put the board on the TV." → the Vestaboard message full-screen (it also pops in on its own whenever the board changes — see §12b). Routines can target it too (action `cortex`): e.g. *arrive home → lights on + map on the TV*. **Adding a TV (pairing).** Point any screen's browser at `kyro.house/cortex`. If it isn't set up yet it shows a big **pairing code** and waits. You get a notification ("a screen wants to become a Cortex display — code ABCD"); open **kyro.house/cortex/pair**, pick which site/TV it is, and tap **Approve** — the screen binds itself instantly. No codes to type on the TV, nothing to install. Deny anything you don't recognize. **Which TV?** Woodhouse picks the display at the site you're currently at (by presence). Name it when you mean another one ("…on the work TV"). **Demo script.** On the sofa: "Pull up the work cameras on Cortex." Watch the wall switch to the camera wall with corner brackets; then "Put the map up." — the map; then "Clear the TV." — back to the clock. ## 15b. Notifications — choose your devices **What it is.** Every device you've signed into Woodhouse on can receive push notifications (dispatches, approvals, re-link nudges, "a screen wants to pair"). You choose which ones actually do. **How.** In the app: SYS strip menu → **Notifications** — each device shows its name (tap to rename), a **THIS DEVICE** marker, and an on/off toggle. On the web: **kyro.house/settings → NOTIFICATIONS · DEVICES** — same list, NOTIFYING / MUTED per device. Muting your iPad keeps your iPhone buzzing, and vice-versa. **If a device never shows up:** open the Woodhouse app on it once — that registers it. (Build 28+ is required for notifications to work at all; earlier builds were signed for Apple's test push environment.) ## 16. Coming online (so you know what's next) - **Routines phase 2**: the visual patch-board builder in Ops (today: authored by chat, managed in Ops), gate/camera steps with APPROVE/DENY dispatches + standing grants, voice-phrase triggers, Siri auto-registration. - **Points & the Danger Zone**: the chore economy's enforcement arm — photo dossiers, docked points, contests. Columns already exist. - **Text Woodhouse (iMessage)**: text him at +1 (205) 396-8556 from a phone number the owner has registered (you must text first; he cannot start the conversation). When something needs a go-ahead he sends a prompt: tap-and-hold it and 👍 to approve, 👎 to cancel (or reply 1 / 2). A kid's request needs a parent's 👍 — Woodhouse texts the parents. Demo: text "blink the cameras", then 👍 his prompt. In a group he answers only when you name him (Woodhouse, Woody, Woodlass, Dubhouse, or "W" to open a message), or when it is clearly for him; kids are ignored in groups unless a parent allows it. Parents steer each group by texting it: "Woodhouse, mute this group" / "unmute", "answer everything here", "allow the kids" / "no kids here", "only answer me and Jessica", "answer anyone", "status" — or from the app: "mute La Familia". Everyone in a group must be a registered contact on his line (ask Kyle) or he can't post there. Hand him facts by text and he files them ("W, Tim's USA Wrestling number is 123456" → stored as family memory, one-line confirmation). Ask for reminders the same way ("remind me Thursday at 3 to call the dentist") — he texts you back at the time (within 15 minutes). - **Private things in the group**: if anyone but Kyle asks Woodhouse for calendar, mail, memory, locations or similar *in a group*, he holds it and asks Kyle (in the group and by DM) to 👍 before sharing. One-to-one, no hold. - **Send a text for me (parents)**: "put this in the family chat: dinner's at 6" or "text Jessica that I'm running late" — Woodhouse posts it from his own number, word for word. - **Other people's calendars (parents)**: "put these on Jessica's calendar with 24-hour alerts" works — the events land on her Google calendar with the alert set, as long as she has linked Google at kyro.house. - **Repeats**: "put the chores board up every hour from 7 to 9 through Sunday" is ONE schedule now (he says it back as "every hour 7 AM–9 PM until Oct 12"); "every 30 minutes", "every 2 hours" work too. /settings shows each schedule in plain words with Pause / Delete. - **Events (parents)**: tell Woodhouse what's happening ("family dinner at our place Sunday at 3, people can bring dessert or a salad"); he asks for anything missing and gives you a link like kyro.house/events/abc123 to forward. Anyone with the link sees the details and the calendar buttons; family members reply straight away, guests enter name + email and get a six-digit code from Woodhouse. Ask him "who's coming Sunday?" for the headcount and the bring list. The page comes down a day after the event. - **More devices**: pool (IntelliCenter — built, needs the home agent), Brother printers, Samsung Frame art, eero, Eight Sleep, fridge sensors, ceiling fans (Bond bridge). - **Realtime voice on iPhone**, Siri/Action Button surfaces, widgets. --- ## 17. Vibe — the kids build games with fx (kyro.house/vibe, in the cloud) **What it is.** A page on the Gracewood Spark where a kid talks to a real coding agent (**fx**, Vercel Labs' tiny native coding harness) that writes a browser game into their own folder, with the game playing live beside the chat. The model runs on the Spark in the house — nothing leaves the LAN, nothing is published. Each turn ends with **"What I did and why"**: a few plain sentences naming one idea (a variable is a labeled box, a loop repeats, a function is a recipe). **How to use.** Open **https://kyro.house/vibe** on any device (sign in with Apple like the rest of the site — you land straight on your own projects; parents are builders too). Then **NEW** to name a project ("Space Game") or open one you have. Type what you want ("a ship that dodges asteroids, with a score") and watch the builder work — one small step per request works best. The preview reloads by itself; **OPEN ↗** plays it full-screen; **undo last** puts the project back the way it was before the last change; **explain it** asks for the whole game in plain words with a tiny experiment to try; **HOW TO** (a tab beside PREVIEW, or the header link) opens the kids' guide without leaving the chat. "Explain" questions never change files (on an empty project the builder just says so); after every build the page is load-tested and a crash is sent straight back to the builder to fix before the turn lands. On an iPad held upright the preview sits above the chat so both stay on screen. A whole first game takes about a minute; small changes less. Since 2026-09-08 every turn runs in its own Vercel Sandbox with GPT-5.4 through the family's AI Gateway (a few cents each), and the files live in the family's cloud store — nothing depends on the house being online. **Parents.** Every game has an address: `https://kyro.house/vibe/g//` (any signed-in family member can open it). Ask Woodhouse "what has Ronan built?" or "explain Ronan's space game" — the `vibe` tool lists projects and reads the latest "what I did and why"; kids can ask about their own. **Admin → VIBE** lists the builders with PAUSE/RESUME per person, every project with OPEN/DELETE, and the requests the validator refused. Each builder's row shows usage for today, this week, and this month — builds, and real dollars straight from the AI Gateway's bill — with a cap per window in builds or dollars (empty = no cap); a kid who hits a cap is told to ask a parent. Games can be 2D (a canvas) or 3D (three.js) — "make it 3D" is enough. A finished turn drops a `VIBE · Ronan · Space Game` card in each parent's task dock (one per project per day, updated in place); a refused request emails Kyle. The builder only writes plain HTML/CSS/JS, can't reach the internet, refuses off-limits content, and anything that slips past is quarantined instead of served. **Demo script.** Open the page on an iPad → RONAN → NEW "Space Game" → "a ship at the bottom that moves with the arrow keys and dodges falling asteroids, with a score" → ~35 s → play it in the preview → "make the ship faster and the asteroids orange" → read *What I did and why* → **undo last** → "explain this to me like I'm not an engineer".