Say It Once
A voice-first AI product, designed and built end to end
01
Year — 2026
Categories
Role
- Independent product — ASIS, live on the App Store
- Product and UX, SwiftUI client, realtime voice agent, Bun/Hono API
- Memory, calendar sync, contacts, reminder calls, subscriptions, privacy and legal surfaces
- Released on the App Store — September 2026
iOS 17+ · iPhone · App Store · ASIS Pro & Max
Every calendar app can store an event. None of them can take the sentence you actually say — “push Friday’s dinner to eight” — and do everything it implies. ASIS is a voice-first life assistant for iPhone: you talk, a realtime agent grounds what you said in your real schedule and in what it already knows about you, acts through fourteen bounded tools, and puts the result where you can see and correct it. Then it carries the consequences: it asks whether to tell the two friends at that dinner, and when a reminder truly matters, it calls. I designed the product and built all of it — the SwiftUI app, the voice agent, the API, the memory system, calendar sync, reminder calls, subscriptions and the public site. Live on the App Store since September 2026; people on plans, Tell them and reminder calls arrive with 0.9.2, submitted in October.
Problem
Life organisation is scattered across a calendar, a task list, a reminders app, a notes file, a group chat and the person’s own head. Every one of those tools can store information. Not one of them removes the actual work, which is translating a messy intention into structured entries, reconciling it with what is already there, carrying the context that made it matter, and letting the people it affects know.
Voice looks like the obvious fix and mostly is not. Dictation moves the typing without moving the work: something still has to know that “move it to after the gym” means Thursday, that the gym block ends at seven, that the thing being moved repeats every week on a Google calendar, that shifting it collides with something already promised — and that two friends are expecting you at the old time. The hard part was never transcription. It is everything that has to be true before ten seconds of speech is safe to act on.
The bet
The bet is that a life assistant only becomes useful when three things hold at once: conversation fast enough for real life, actions reliable enough to trust, and memory selective enough to feel familiar without becoming invasive. Miss any one and the other two are worthless — a fast assistant that books the wrong thing is worse than a form, and an assistant that remembers everything is a surveillance product with a friendly voice.
Scoping it was mostly deciding what not to build. It is not a project-management tool for teams, not a chatbot with no action layer, not a coach, and not a clinical, legal or financial adviser. It has no streaks, no badges and no urgency theatre, because the failure mode of productivity software is making the user feel behind. The persona is specified as carefully as any schema — the user’s companion for the whole of their life: observant, quick-witted and sunny, a friend who notices everything and says little of it — and the spoken contract is two or three sentences with no markdown, no emoji and no narration of its own machinery.
Everything that follows is the cost of that promise. Voice is the fastest way in, but Plan, Memory and Settings exist so nothing the assistant does is only knowable by asking it again.
Similarity is not permission. A private memory can score highest in retrieval and still stay silent until you name the topic yourself.
The voice loop
A turn runs through a realtime pipeline, not a request cycle. The iPhone streams encrypted audio into a LiveKit room; Soniox transcribes it in the one language the session is pinned to; DeepSeek V4.1 Flash reasons over a grounded prompt — local time, the merged plan, policy-allowed memory — and either answers or calls a tool; Soniox speaks the reply, with Alibaba’s Qwen voicing Korean, Japanese and Chinese. Every stage has a backup wired behind a setting — Deepgram to hear, Gemini to reason, Google’s Chirp to speak — so a provider outage is a configuration change, not a redesign. Moving to this stack in September cut the cost of a minute of talk by more than half.
The split that took longest to accept is the boring one. The API runs on Bun + Hono; the agent is a separate Node 22 process, because LiveKit’s native rtc-node client fails under Bun’s runtime. Rather than compromise the API for one dependency, the two ship as separate Fly apps, and a shared TypeScript schema package keeps their contracts honest.
Realtime tuning is where the product is felt. When a turn stalls, a staged watchdog forces a commit at 8 s, bypasses the stuck turn at 16 s and answers from what it heard, and closes the room at 24 s — bounded silence beats a spinner, because a voice interface with no timeout simply looks broken. A session closes after 45 seconds of quiet and none runs past 15 minutes, limits set only after measuring 263 production sessions, the longest 8.1 minutes. Hold the sphere to mute and the room stays open while you hold, and the captions are a record of what was heard: a reply cut off mid-sentence keeps exactly the words that played.
The system
- Agent tools in a session
- 14
- Speech languages
- 22
- Canonical memory types
- 11
- Event intents
- 14
- Ordered migrations
- 46
- Backend tests
- 4,100+
Bounded tools
The model is never allowed to improvise an action. Eleven core tools with explicit schemas are the entire surface it can touch: five run as RPC on the phone — read the merged plan, find real free time, write events, to-dos and reminders — and six run on the server, against memory and conversation history. The current build adds three when the app can honour them — prepare a message, tell the people on a plan, close a card the user waved away aloud. A reminder call gets one more, to hang up, and onboarding swaps in a pair of its own so a setup conversation cannot pretend the user is already in a normal session.
Three rules inside those schemas do most of the safety work. A conflicting write is never silently committed: the allow-conflict flag turns true only after the person says so. Creating an event on a connected Google or Outlook calendar requires the user to name that calendar; otherwise it lands in ASIS’s own, where a mistake is cheap. And an edit to a repeating event never guesses its reach — a card asks whether it means this one, this and every later one, or the whole series, whether the change was tapped or said.
Memory that has to earn it
Memory is the part of the product most likely to be faked and the part I spent longest making real. It is not a transcript archive presented as personalisation — it is a governed dossier of durable facts, preferences, routines, priorities, people, projects and commitments. Every line is searchable, editable, archivable and deletable by the person it describes, and every line says where it came from.
A candidate has to pass two independent gates before it exists. An evidence score decides whether it may be written at all: 0–4 is discarded, 5–7 stays an invisible candidate, 8–10 is only proposed unless you said it outright, and 11 and above may go active. A separate confidence band decides what the user ever sees — below 0.40 nothing, below 0.91 a proposal at most. So an inference has to ask, and the confirm card is the moment the system admits it is guessing.
The exclusion list is the part I would defend hardest, because it is where most memory systems quietly overreach. Passing moods, finished tasks, reminders, unconsented health detail and a user idly testing the interface in another language are all explicitly not memories — and nor are the assistant’s guesses: memory must come from the user’s evidence, not from the model reinforcing itself. Credentials are stopped twice. The prompt forbids them, and because a prompt-injected turn can talk a model out of a rule, a deterministic detector — card numbers checked by Luhn, identity numbers, one-time codes — drops any candidate carrying one before scoring ever sees it.
Writes are operations, not appends: the extractor proposes adding, updating or contradicting one specific memory, and a judge reviews every semantic neighbour above a 0.75 similarity floor before anything new may stand beside it. A contradiction about who you are, the people in your life, your health or your logistics never overwrites — it waits for you to confirm — and when a preference simply changes, the earlier version is kept as a revision rather than erased. Recall fuses dense similarity (Voyage 4 Lite at 512 dimensions), Postgres full-text search and person-mention retrieval through reciprocal rank fusion. Housekeeping runs nightly, weekly, monthly and quarterly, and a separate review reads one category at a time for duplicates and contradictions — never health, never a private entry, never a transcript. Hygiene is never permission to rewrite the person.
The people in it
People are where a life assistant becomes useful or becomes creepy, so they came last and slowest. You add someone to an event from Apple’s own contact picker, or just by name — “add Tar to Friday’s dinner” finds the Tar in your contacts. Names and numbers stay on the iPhone only: contacts are never uploaded, names reach the AI only once you accept one plain card — “Names of people on your plans go to the AI that hears and answers you” — and phone numbers never leave the phone at all.
Then the part the store frame calls Tell them. Move an event that has people on it, by hand or by voice, and ASIS asks: dinner’s at eight now — want me to let Maya and Leo know? Tell them opens Messages once per person with the change already written, and you tap Send. ASIS never sends a message itself; iOS would not let it, and it should not want to. Later leaves a line under the event with a tap per name, and No need lets it go.
Identity is the hard part underneath. The contact picker and a name lookup can hold different identifiers for the same person, and address books are full of duplicates, so two entries count as one person when they share an identifier, or a name and the last eight digits of a number. When a name still fits several people, a “Which one?” card lists each with their number, answerable by tap or aloud — “the second one”, “never mind”.
A plan that cannot lie about time
Plan is where automation becomes inspectable. It merges ASIS’s own events, to-dos and reminders with Apple Calendar and Apple Reminders, both read on the phone, and with mirrored Google and Outlook calendars — across Today, Week and Month, each source in its own colour and its provenance intact all the way. A mirrored event is never silently promoted into an ASIS event, every event carries one of fourteen intents, and a user-set intent locks out the AI: inference outranks nothing the person has stated.
Apple Calendar stays on the device through EventKit — there is no server-side copy of anybody’s Apple calendar, which is a real constraint to work under and the right one. Google and Outlook are mirrored server-side across a rolling window from 60 days back to 400 ahead, through Google’s change probe and Microsoft’s delta links with their tombstones, on a 20-minute loop with a two-minute debounce. Writes are conditional on the provider’s ETag or change key, so a lost update surfaces as a 412 that becomes a 409 the app can resolve rather than a silent overwrite, and OAuth tokens sit in a versioned AES-256-GCM envelope that is destroyed with the account.
The synchronisation rule I am most attached to is a deletion rule. Mirrored events are swept only after a successful full listing of that calendar — never on a delta tick, and never past the first failure. The alternative is an assistant that reads a sync gap as intent and quietly empties a calendar to stay in step, and no amount of interface polish recovers from that. Dates get the same care: every daily key is built on a pinned Gregorian calendar, so a Thai phone running the Buddhist era cannot shift anyone’s week.
A delta gap is not a deletion. The mirror only sweeps after a full listing succeeds — anything less is an app that empties your calendar to stay in sync.
When it matters, it calls
A reminder now comes in three strengths. Normal is a notification. Alarm is iOS’s own alarm, so on iOS 26 it rings with ASIS closed and straight through Silent and Focus, with Stop and a ten-minute Snooze. Important does something no calendar does: it rings like a phone call. Answer it and it is a conversation — ASIS says what is due, hears whether you have already done it, and marks it done or snoozes it for you.
That call is the only thing ASIS’s server ever pushes. Everything else — the morning brief, the heads-up before an event, every ordinary reminder — is composed on the server but scheduled and fired locally on the phone, which is why ASIS ran with no push infrastructure at all until the call needed one. When it did, the privacy policy changed on the day the build went to Apple: a VoIP push token is held only for iPhones left on for calls, and signing out on an iPhone removes it.
Widgets follow the same instinct. The extension never touches the network: the app writes a versioned snapshot into a shared container, stamped with the owning account, the local day and an expiry at local midnight, and a sign-out, an account change or a lost subscription writes a redaction envelope before the widget can render again. Under uncertainty the widget shows nothing, because the failure mode of guessing is showing one person’s day to another.
What a spoken turn costs
Realtime AI bills by the second, so the product carries a ledger. Every provider’s usage — model tokens, audio heard and spoken, room time, embeddings — is converted into internal micro-dollars against pricing held in code and charged against a daily allowance reset at the user’s local midnight. None of that arithmetic is ever shown as dollars; the user sees an allowance, because a person mid-sentence should not be doing unit economics.
The allowance is derived, not picked: the annual price × 0.85 ÷ 365, so every plan pays for the minutes it promises. When the September stack brought a minute of talk down to about a cent and a half, the prices came down with it — ASIS Pro to $9.99 a month or $109.99 a year, Max to $19.99 or $219.99 — and Apple carried the cut to existing subscribers automatically. Pro now holds roughly 13 to 18 minutes of conversation a day and Max 26 to 35, depending on the hour.
The accounting deliberately fails open: if the ledger itself errors, the conversation continues, because stranding a paying user on an infrastructure fault is worse than an unbilled turn. The two tiers differ by allowance, not by features — segmenting a life assistant by capability would mean shipping a version that deliberately forgets things — and there is no free tier and no free trial, which is uncomfortable for a consumer app and honest: every conversation costs real money at the instant it happens. Reaching the limit pauses conversation only; the plan, the calendars and the memory stay open.
Build
- Minimum iOS
- 17.0
- Latest build — October 2026
- 0.9.2
- Stored voice recordings
- 0
- Phone numbers sent to the server
- 0
- ASIS Pro — monthly, annual
- $9.99 · $109.99
- ASIS Max — monthly, annual
- $19.99 · $219.99
Auditing my own product
Before writing any of this down I reconciled ASIS against itself — the live site and legal pages, the running code and my own planning notes — with implementation outranking marketing and marketing outranking my notes, and I kept the disagreements instead of resolving them quietly.
The useful findings are the uncomfortable ones. Both the website and the code claim 22 speech languages and they are not the same 22: the code carries Czech, Romanian and Russian where the site lists Hebrew, Norwegian and Finnish. The iOS app now carries more than 1,700 unit tests, plus journey tests that drive Apple’s real contact picker on two simulators, a current iPhone and a small iOS 17 one — but iOS CI is still a placeholder. The backend’s tests run on every push to main and the iPhone’s run on my machine, which is exactly the wrong way round for the half of the product a reviewer actually touches.
Not every mismatch is a defect. The interface presents twelve memory sections over eleven canonical types because Commitments and Moments are two views of the same episodic row; writing that down as intentional is worth as much as writing the bugs down. The remedy, still to build, is one owned launch registry — devices, languages, providers, tiers, prices, permissions — that the app and the site both read, so a public claim cannot drift away from the build again. A register you keep is worth more than a number you defend.
Outcome
ASIS reached the App Store in September 2026 and has not stood still: 0.9.0 brought the new voice stack and lower prices, 0.9.1 reworked the Plan, and 0.9.2, submitted in October, adds the people on your plans, Tell them, alarms and reminder calls. It is an iPhone app on iOS 17 and later, behind asis.life with its own privacy policy, terms and support, served by a Bun API and a separate Node voice agent on Fly over Neon Postgres with pgvector — 46 ordered migrations, more than 4,100 backend tests, and a subscription that pays for the seconds it spends.
What the project taught me is that the model is not the hard part of an AI product. Ten seconds of speech is the easy part. The difficulty is everything that has to be true for those ten seconds to be safe to act on: whose account this is and how the server knows without asking the client; which calendar owns the event and what happens when the provider disagrees; whether a sentence deserves to be remembered at all, and who gets to overrule that; who else a change touches, and who decides whether they hear about it; and what the product does at second sixteen when a provider simply stops answering.
Every one of those is a boundary somebody has to draw and then defend in code. Building it alone meant drawing all of them — and then writing the privacy policy that promises them to a stranger.
Next case study
02 / 11