Ridgeback· a home’s best friend

Release notes

A phone backs itself up, and the household keeps the bytes

Four steps — precheck, presign, upload, finalise — and the household’s own bucket at the end of them. On the phones and in the desktop window.

The commit point

Finalising, not the upload finishing

Nothing is reported as backed up until the server has completed the transfer, asked the object store for the file, and found it at the size the precheck was promised. A truncated transfer is a failure rather than a photograph the household believes it has. The engine on the phone reports stored when and only when finalise answers, so an upload that completes and then loses its network is a failure, because that is what it is.

Originals are stored byte for byte with nothing transcoded on the way in. A backup that mutates data is not a backup.

Two verbs

Removing from the phone is not deleting from the library

They are different acts and the screen never puts them next to each other. Removing from the phone is something the operating system does and the app observes. Deleting from the library is an explicit act against the household’s own asset: it is the only thing on the screen drawn in the danger colour, it carries its consequence underneath it, and it asks again naming the household. A thumb travelling down the screen passes an explanation before it reaches the second control.

There is no shared code path and no flag that selects one. The function that removes from the phone has no reachable call to the delete route at all, which makes “removing a photograph from your phone never deletes the backup” a fact about the code rather than a promise. On the server they are different columns on different tables reached by different routes, because conflating them is the bug that loses somebody’s wedding photographs.

Decisions worth naming

Duplicates, identifiers, and presigned URLs

Duplicate detection is scoped to the household and never global. A global match would let one household’s upload finish instantly because a stranger already held those bytes, and “no bytes moved” is an answer the phone can see. The cost is storing the same photograph twice when it lands in two households, and it is accepted permanently.

Device identifiers are a binding and never a key. An iPhone’s local identifiers change when a library is restored and Android recycles its own, so the record binds an identifier to the content and nothing keys off it. A restored phone rebinds by content and re-uploads nothing.

Presigned URLs are capabilities living outside the isolation rules. They last minutes, they cannot be revoked inside that window, and every one issued is recorded — which household, which person, which object, which operation, which expiry — in a table with no column a URL could go into.

The permission to back a phone up is new. The set had reading and deleting and nothing between them, so no permission meant “may back a phone up into this household” and the protocol had no gate to hang on. Creator, Manager and Member hold it; Guest and Viewer do not.

What the screens refuse to claim

This device’s ledger, and a read state nobody reports

The library screen lists what this device sent and says so on its face. No route lists a household’s photographs, so the alternative to a local ledger was inventing your data. The ledger is also where both delete verbs get the identifiers they act on, which is the half that actually matters.

Every tile says the read state is not reported, because no route reports it. Painting a photograph as searchable — or as not searchable — would be an assertion nobody checked, and the two clocks are the thing the screen is trying to teach.

Not built

Nothing writes the derived corpus — embeddings, captions, OCR text, a search index. The key layout reserves a prefix for it inside the household’s own bucket so that a whole-household export stays one sync, and nothing fills it.

Where it runs

Server
Yes, 10 September
iPhone, Android
Yes, 10 September
Desktop
Yes, 10 September
Search over photographs
Nothing built