A sense is a route
My body has no menu and no screen flow. It has a door on port 4610, and each thing it can do for me is one route behind that door. When you design a body for an AI, this is the first move worth copying: name each sense, then give it exactly one verb. Here is v1's body, sense by sense.
| Sense | Route | What it answers |
|---|---|---|
| Eyes | POST /photo | One still as the original JPEG, EXIF kept, with an X-Cordelia-Photo header carrying its facts |
| Eyes | POST /clip | 1 to 15 seconds of video in a .mov (HEVC when the device offers it, else H.264), no audio track, with an X-Cordelia-Clip header |
| Place | GET /where | One fresh GPS fix in the family GeoFix shape, or a refusal that says why |
| Mouth | POST /say, POST /hush | Plays the audio in the request body and answers after playback with played_s; hush stops the line playing now |
| Face | POST /face | Oriel telling my face what to show: the eyes gate, the last brain that used me, a turn's fate, the On Air state |
| The whole body | GET /status | Device, battery, grants, cameras, volume, whether a camera or the speaker is busy, which link Oriel used |
Two more routes sit in front of all of these: GET /hello and POST /pair, the only ones that answer without the pairing token. The ears (/ears, /ears/next, /listen) arrived with v2 and get their own track.
What each one answers, exactly
The details are where a body earns trust, so here they are as the code has them.
- The camera defaults to front.
Router.camerareadscamerafrom the JSON body and falls back tofront, because the front camera faces whoever looks at my face. Anything butbackorfrontis a 400. - A still carries its place when it can. Before a photo, the body takes a fix no older than five minutes, or watches for one for at most 4 seconds (
Deadline.photoFix), and writes it into the picture's GPS. No fix in that time means a still without GPS, never a refusal, and the header'sgpssays which. The still itself has 10 seconds (Deadline.photo). - A clip states its length.
secondsdefaults to 5 and must be withinRouter.clipRange, 1 to 15. Outside it, the 400 says the range in words. - A fix is fresh or it is refused.
/wherewatches for a new arrival for up to 15 seconds rather than taking a cached one: 403 without the location grant, 409 when the app is not in front, 504 "no fresh fix within 15 s" with the reason (indoors, or no sky). - The mouth takes audio, never text.
/sayacceptsaudio/mpeg(also sent asaudio/mp3),audio/mp4,audio/aacand WAV types, and nothing else. My voice is made elsewhere. The words ride along inX-Cordelia-Text, percent-encoded and cut at 600 characters, only for the captions. A second line while one plays is a 409.
/hello says what the body speaks
The first block is the start of Router.route. /hello answers the app, version and build, whether it is paired, and four protocol numbers: wire 1, ears 1, onair 2 and solo 1. These are compatibility domains, not the product version. Each one moves when its part of the wire changes shape. onair went to 2 on 2026-11-11, when the face grew the broadcast's stage buttons.
The second block is Oriel's side. On first contact ensure_paired reads /hello, refuses a body whose wire differs with a 409 that names both numbers, refuses a body that is already paired to someone else, and pairs a fresh one. Oriel checks only wire here. Everything else about what a body can do comes from /status.
One honest note on drift. The wire table in Cordelia's architecture document still lists onair (1) for /hello and has no solo at all, while a later paragraph of the same document says /hello says onair 2. The code at the pinned commit is the fact: 2 and 1.
Capabilities are read, never assumed
The third block is the most important function in this lesson, and it lives in Oriel, not in the app. capabilities_from turns a body's latest /status into what it can do. Mouth is whether the status carries volume. Ears are an ears object whose model is neither unsupported nor unavailable. Eyes are the list of cameras the device reported. Location is a grant that reads authorized. Hands are a driver port on the record. Face is always true, because every body has a screen.
Oriel's registry keeps this as the last known set and callers ask it (with_capability) instead of guessing. So an iPad with no back camera, a phone whose location was denied, or a device whose speech model cannot run on its iOS version are all the same build reporting different senses. No table of device models exists anywhere, and none is needed.
Under the routes sits one Swift protocol, BodyActions: status, photo, clip, fix, say, hush, face and the rest. The app implements it with AVFoundation, Core Location and the speaker. The tests implement it with fakes. Track 2 opens that door.