Doveborn: neither letter opens until both are sealed

A weekly letter between two people. You cannot read theirs until you have written yours, and the prompt model never sees a word either of you wrote.

Client
Nuits
Year
2026
Status
Waitlist
Role
Solo founder, Product design, Engineering
Stack
TanStack Start, TanStack Query, React 19, PWA
Doveborn: neither letter opens until both are sealed

Context

Doveborn is one letter a week between two people. That is the whole product.

Both of you write. Neither letter unlocks until both are sealed. If your partner has not written theirs, you are not looking at anything, no matter how many times you open the app. When the second one seals, both appear at once.

It is on a waitlist.

The problem

I have the same conversation with people I care about in three different apps and none of it accumulates into anything. Messaging is optimised for immediacy, which means it is optimised against reflection. The thing you would have said properly given twenty minutes gets said badly in twenty seconds, and then it scrolls away.

The specific mechanic I was chasing came from watching how people behave when they can see the other person’s message first. They match it. Length, tone, depth, effort. Someone writes three lines and you write three lines back, and the exchange settles at the floor rather than the ceiling. Read receipts make it worse, because now the silence is legible and both of you are performing for it.

Remove the ability to read first and the anchoring disappears. You write what you have, not what you are matching.

My role

Solo, as with everything at Nuits. Product, interaction design, engineering, copy, the privacy model.

The privacy model is the part I would point at. It is not a policy document I wrote afterwards to describe a system. It is a constraint I designed the system around, and it removed features I wanted.

Approach

The seal is the product, so the interface exists mostly to make sealing feel deliberate. Writing is a full-screen, unadorned page with no formatting toolbar and no character count. There is one action at the end and it is irreversible. That irreversibility is doing real work: an editable letter is a draft, and a draft never gets finished.

The waiting state was the other design problem. Once you have sealed and your partner has not, there is nothing to show and a strong temptation to fill the space with encouragement, progress bars or nudges. All of those turn waiting into pressure, and pressure applied to a weekly letter produces a bad weekly letter. The waiting screen is close to empty on purpose.

There are prompts, optional, for the weeks when you open the page and have nothing. They are the only place a model touches the product.

Building it

TanStack Start with TanStack Query and React 19, shipped as a PWA so it installs and sits on a home screen where a weekly ritual actually survives. Query handles the sync between sealed state and unlock state, which is more delicate than it sounds: two clients can seal within the same second, and both need to transition from waiting to revealed without either one flickering through a half state.

The privacy design is the load-bearing decision. The prompt model reads metadata and only metadata. It knows how many weeks the pair has been going, roughly how long the letters tend to be, how long it has been since the last exchange, and which broad themes the pair has previously selected for themselves. It never receives letter bodies. Not a sentence, not an excerpt, not a summary.

That constraint cost me the feature I most wanted to build. A prompt generator that has read your last six letters can ask a genuinely good question. A prompt generator working from counts and dates asks a decent generic one. I shipped the decent generic one, because the alternative means the sentence “your letters are private” is false in a way the person reading it would not expect, and I am not willing to write that sentence and then quietly qualify it.

The rest follows from the same rule. Letters are stored, because you need to reread them, but nothing in the pipeline analyses them. No sentiment scoring, no topic extraction, no search index built over the bodies, no engagement model. There is no feed to optimise, so there is nothing to optimise it with.

Failure modes are mostly about asymmetry. One person seals every week and the other stops, and the app has to handle that without turning into a guilt machine. Right now it simply keeps the sealed letter waiting and says nothing. That is not a solved problem; it is a deliberate refusal to solve it badly.

Outcome

Waitlist at doveborn.com. That is the honest status.

The mechanic has held up in testing with the small number of pairs using it. Nobody has asked for the ability to peek, which I half expected to be the first request.

What I’d do differently

I spent too long on the letter editor and not enough on the reveal. The reveal is the emotional payload of the entire product and it currently just shows two letters. It should be the most designed screen in the app and it is the least.

I would also have built the pair-invite flow before the writing experience. Everything in Doveborn requires two people, and I built the single-player half first, which meant discovering the awkward parts of the invitation late.