Search that never leaves the phone
Deciding how search works in an encrypted journal, and finding that the option I had feared for three months was already live in a feature I had shipped.
For three months I carried a note that said search in Duskglow was a hard fork. Either the model could read what it needed and the privacy design took a hit, or the design stayed intact and search stayed dumb. Today I sat down to make that call properly, and the first thing I found was that the note was wrong about where the line was. The second thing I found was that the best option was also the cheapest one.
The line I thought I had not crossed
Here is the constraint. Every saved journal entry is encrypted on the phone before it is stored, with a key our database does not hold. A search that understands meaning needs a model, and a model needs to read text. So my note said: if the phone unlocks entries and sends them to the model at search time, that reopens saved entries and weakens the privacy design in a way I did not want to pay for.
I read the live code before I read my own note again, which turned out to be the right order. The "Organize My Thoughts" button, which has existed since spring, does exactly the thing the note called a line. It unlocks one saved entry on the phone, sends it through the server to the model for a reflection, and stores nothing readable on the way. The privacy policy names this in plain words. A rule I wrote last week already covered it, under the heading of lines the app unlocks on the device and sends for that one request.
So the real question was whether a search-time flow stays inside the class I had already disclosed, or widens it. A narrower question, and a more honest one.
There was a smaller embarrassment underneath. Two of my own documents used the letters A and B for opposite options. In one, Architecture A meant "the phone unlocks and sends". In the other, A meant "the server ranks the readable summaries". I had been reasoning across both for weeks without noticing. Letters carry no meaning between documents. From today, options are named by what leaves the device, and nothing else.
Search on the phone, summary on a tap
Once the question was about degree, the options sorted themselves by one measure: how much text leaves the phone per search.
The history screen already unlocks every entry on the phone when it opens. That is how it renders them. So a search over that text is a filter on data already in memory, and it costs nothing. It reaches the user's own words, which the readable summaries leave names out of by design. It also reaches the reflection's theme tags, so a search for "family" finds an evening that only said "my parents came over". No model call, nothing sent, and the daily call budget untouched.
The summary-ranking search I had been running instead sent thirty-word summaries to the model on every search, cost a call each time, and missed any word the summary had not kept. I retired it today. The phone does the finding now.
For the brief "what did I find" summary that people will want, the shape is a tap, and the tap sends only the matched entries, for that one request, with the answer shown and never stored. I priced the alternatives against the live journal sizes and the model provider's current published rates. Sending the whole journal on every question costs a few cents today and climbs with every entry. A year of nightly writing puts it near twenty cents a question, and at a hundred people searching once a day that is close to five hundred dollars a month, still growing. Sending only the matches stays around three dollars a month at the same usage and does not grow with the journal's age. The privacy argument and the cost argument pointed the same way, which is worth noticing, because they usually do not.
Proton's mail search does the first half of this. It decrypts and indexes in the browser and never asks the server. Their assistant does the second half, sealed at rest and opened only for the request. I did not invent the shape. I checked that two products I trust had already chosen it.
A rule that was silent on degree
The class rule I mentioned was written for one-line flows. The intention a user set last night, recalled at the next greeting. One entry, reflected on request. It said nothing about how much. Under its letter, sending the whole journal every search would have been inside the class too, since every line was unlocked on the device and sent for one request.
That is the gap I closed today. Flows in that class are now per request and user scoped, and a flow that sends more than what the user is acting on, or a set the user cannot see, is outside the class and needs its own disclosure. A summary over the results the user is looking at stays inside. A question answered from the whole journal does not. Same mechanism, but the degree differs, and the rule now says so.
A quote keeps the tense of the night it was said
The other thread of the day was smaller and I almost missed it twice. The morning notification carries the intention a user set the night before, in their own words. This morning's headed it "Your intention for today", and under that heading the words "practice for thirty minutes tomorrow" read as the day after. Wrong by a day, because the heading had moved the words to this morning.
The fix was to frame the words as reported speech. "Last night you said", and then the words, untouched. The heading carries the time the words were said. The words keep their tense. That shipped this afternoon, and tomorrow morning is its first real run.
Then, while testing the new search this afternoon, I read the home screen and found the same heading on the same words. "Your intention for today", above a sentence ending in "tomorrow". Same bug, one surface over. I had fixed the notification and not gone looking for its sibling. The card now says "Last night you set an intention", the same frame in different words, because two surfaces that repeat an identical phrase start to read as a template.
Twice in two days, the lesson held. You do not edit the user's words to make them fit. You move the frame.
Mental Models
Read the live system before you price a principle. My note said a line had never been crossed. A feature I shipped in spring had crossed it, with disclosure. The real question was degree, and I could only see that from the code.
Flat beats cheap. A cost that stays constant as the product ages is worth more than a cost that starts lower and grows with every entry.
A rule needs a size. A disclosure rule written for one-line flows will admit the whole journal unless it says how much. Add the degree.
Frame the words, never rewrite them. A quote keeps the tense of the moment it was said. When the reading goes wrong, the frame is what moves.