The Doozer Brainlive from the knowledge bundle · v2.2.697 · 2026-10-29 10:59 ETall pages · one page

The Wuz, a memory with the place attached (data model and rules)

decisions/the-wuz.md · Decision · wuz, doozer-app, check-ins, memories, logfile, receipts, privacy, consent, data-model, plan

status: live (A2 server, v2.2.671); receipts and findable moments still plan · written by claude-opus-5-5 on 2026-10-25 · stale after 2027-04-01

What a Wuz is next to a check-in and the trail, how a check-in becomes one, the tables (wuzzes, items, companions who agreed, receipts, findable moments), the four privacy levels, the mutual "who else was here", remembering where you have been on the device only, and export and delete.

The Wuz

"Doozer knows the world. Logfile knows your trail through it. Wuz lets you go back."
(doozer.world)

Decided (Jon, Oct 25 2026): Logfile and Wuz are named features inside one app called Doozer, not separate brands. Inside the app the trail is called Logfile and a kept memory is a Wuz. wuz.ai becomes the share link for a memory (the link level below); logfile.me points into the app. Remembering where you have been arrives in A2, saved places first; the full on-device visit log waits until people ask for it.

Live now (v2.2.671, Oct 25 2026)

The A2 server half is live in includes/wuz.php, served by API v1: wuzzes, wuz_items, wuz_people and place_photos.wuz_id / client_id self-heal (wuzReady()); keep from a check-in or after the fact; photos through placePhotoStore() (the same re-encode and private Spaces path the web's check-in photo now uses); invites, accept, decline, leave; on this day; and the share page at https://wuz.ai/<uid> (cover at /<uid>/cover, invite page at /i/<token>; wuz.ai already pointed at this server, so no DNS or Plesk work). Receipts, findable moments, export and account delete are not built.

Where the build chose, plainly:

Three things, kept apart on purpose:

what it is where it lives today
Check-in "I'm here": a person present at a place, now. Feeds "here now" and the counts a business sees. check_ins (live)
The trail (Logfile) Everything a person did, newest first: check-ins, kept memories, receipts. Private. the One sidebar's Trail (live, check-ins only)
Wuz A memory assembled from evidence the person chose (a photo, words, a receipt, the people who agreed), with the place and the time attached. new

A check-in is presence. A Wuz is what you decided to keep. Keeping them apart is what lets "here now" stay honest: a memory added tonight about this morning must never make a cafe look busy now.

How a check-in becomes a Wuz

  1. Arrive. "I'm here" on The One (or the app's arrival prompt, if the person turned it on).
  2. The app checks, on the phone, whether it is within 150 m of the place's pin and sends only near: true|false. Raw coordinates are not sent. The check-in shows on the trail at once (offline too: it waits in the outbox with its client_id).

  3. Keep. A quiet sheet offers: add a photo, a few words, a receipt, who you are with. The
  4. first thing added creates the Wuz (source: checkin, anchored to that check-in). A bare check-in stays a trail entry and is never nagged about.

  5. Relive. The timeline and the map draw Wuzzes and check-ins together. "On this day"
  6. reads Wuzzes by month and day, and is a notification only if the person turned it on.

  7. After the fact. A Wuz can also begin from a photo the person picks from their library
  8. (the phone reads its date and place locally; the server receives only the chosen place and date, and the photo is re-encoded without metadata), from a receipt, from a game result, or from nothing but words ("Grandpa's 80th, at the Elks"). These never create a check-in.

One moment, one set of words: when a Wuz is made from a check-in that already has a note, the note moves to the Wuz (and the check-in's note is cleared) so there are never two versions.

The tables

MariaDB 10.6, self-healing CREATE TABLE IF NOT EXISTS like checkInsTableReady(). JSON written with JSON_EXTRACT(?, '$').


wuzzes (
  id BIGINT UNSIGNED PK, uid CHAR(26) UNIQUE,      -- ULID: share links, offline creation
  user_id BIGINT UNSIGNED NOT NULL,                 -- the keeper
  business_id INT NULL,                             -- the place; NULL when it is not on Doozer
  place_label VARCHAR(160) NULL,                    -- then the person's own name for it
  lat DECIMAL(10,7) NULL, lng DECIMAL(10,7) NULL,   -- the PLACE's pin, never a trace of the person
  tz VARCHAR(40) NOT NULL,                          -- the place's zone
  happened_at DATETIME NOT NULL, ended_at DATETIME NULL,   -- UTC
  title VARCHAR(120) NULL, note TEXT NULL,          -- the person's words (2,000 chars)
  privacy ENUM('private','companions','link','public') NOT NULL DEFAULT 'private',
  source ENUM('checkin','photo','receipt','game','remembered','manual') NOT NULL,
  checkin_id BIGINT UNSIGNED NULL UNIQUE,           -- the anchor: one Wuz per check-in
  cover_item_id BIGINT UNSIGNED NULL,
  client_id CHAR(36) NULL, UNIQUE KEY (user_id, client_id),
  created_at, updated_at
)
wuz_items (                                         -- everything in a memory, each owned by who added it
  id, wuz_id, user_id, kind ENUM('photo','receipt','game','link'), ref_id BIGINT UNSIGNED,
  sort SMALLINT, created_at
)                                                   -- photo -> place_photos.id (gains wuz_id), receipt -> receipts.id
wuz_people (                                        -- companions who agreed
  id, wuz_id, invited_by BIGINT UNSIGNED, user_id BIGINT UNSIGNED NULL,
  invite_hash CHAR(64) NULL,                        -- sha256 of a one-time invite token
  state ENUM('invited','accepted','declined','left') NOT NULL,
  shown_as ENUM('name','first_name','hidden') NOT NULL DEFAULT 'first_name',  -- the companion chooses
  invited_at, answered_at, expires_at, UNIQUE KEY (wuz_id, user_id)
)
receipts (                                          -- Logfile
  id, uid CHAR(26), user_id, business_id INT NULL, wuz_id NULL,
  private_key VARCHAR(255) NULL,                    -- the image, private on Spaces, re-encoded
  merchant VARCHAR(160) NULL, total_cents INT NULL, currency CHAR(3) NULL,
  purchased_at DATETIME NULL, tz VARCHAR(40) NULL,
  read_by ENUM('person','device','ai') NOT NULL, place_match ENUM('none','suggested','confirmed'),
  created_at
)
moment_findable (                                   -- "let others who were here find this moment"
  id, user_id, wuz_id, business_id INT NOT NULL,
  window_start DATETIME, window_end DATETIME,       -- rounded to 30 minutes
  created_at, expires_at                            -- 14 days after window_end
)
moment_pairs (
  id, a_id, b_id,                                   -- two moment_findable rows
  a_answer ENUM('pending','yes','no'), b_answer ENUM('pending','yes','no'),
  created_at, connected_at NULL
)

Columns added to what exists: check_ins.client_id (+ unique with user_id), check_ins.near (TINYINT NULL), place_photos.wuz_id, lb_follows.lists (see API v1). Delete is a real delete, rows and files, not a flag.

Privacy, four levels

level who sees it notes
private (default) the keeper Everything starts here.
companions the keeper and companions who accepted Inviting someone raises a private Wuz to this level, and says so.
link anyone with its unguessable link A share card; noindex, listed nowhere, revocable (a new uid kills the old link).
public the place's page too ("Made here") Only when every companion who appears agrees. Photos still pass review, exactly as check-in photos do today (pending then live).

Receipts never go above private, whatever the Wuz's level: others see at most that the memory has a receipt, and usually not even that.

Companions who agreed

Receipts (Logfile)

Live since v2.2.678 (includes/wuz-receipts.php, table receipts, images under ugc/r/ on Spaces). Each person sees only their own, in any memory; a receipt is also a wuz_items row (kind receipt) so it moves with its owner when they leave or the keeper deletes. The AI read asks the vision model for merchant, date and time, total and currency only, strips card numbers and card-network words from what comes back, and logs the cost to ai_generation_history with no content and no person. Not built: receipts without a memory, and place matching (place_match stays none).

A receipt proves a place and a time to its owner and to no one else. It is read on the phone first (Apple Vision and Android ML Kit text recognition, free and private); reading it with an AI model on the server happens only when the person taps to do it, with a plain line saying the image goes to our AI provider to read the store, date and total. The fields can be kept while the image is deleted.

It is never shown to companions or the public, never sent to the business, never compared with anyone else's receipts, and never used to suggest or reveal that someone else was there.

"Who else was here" (mutual, or nothing)

The loop's step 5. A memory platform that surprises people with who saw them is the dark pattern we promised never to build, so:

  1. On a Wuz anchored to a real check-in that was near the place (not a remembered visit or
  2. words alone, so nobody can claim a place to go fishing), the keeper may turn on "Let others who were here find this moment". Off by default.

  3. The server pairs findable moments at the same place whose 30-minute windows overlap.
  4. Nobody learns anything unless both people turned it on.

  5. Each then sees only: "Someone else who was at Luc's around 3 PM on Saturday also chose to
  6. be findable. Say hello?" No name, no photo, no count beyond "someone".

  7. Both say yes: each sees the other as they chose to appear, and either can invite the other
  8. to the Wuz (the companion model above). No open messaging in A2; that is its own system with its own safety work.

  9. Either says no, or nobody answers in 14 days: nothing is revealed, the pair closes, and no
  10. one is told a "no" happened.

  11. Guardrails: adults only (stated at first use), a weekly limit on findable moments, block
  12. and report, never paired with someone you blocked.

No Bluetooth or Wi-Fi scanning, no faces, no background-of-the-photo, no receipts. Only two people who each said yes about the same moment.

Remembering where you have been (on the device only)

Off until the person turns it on, explained in one sheet, paused or erased in one tap.

Raw locations never leave the phone at either level. Background location needs the iOS "Always" permission and Google Play's background-location declaration (with a video); both reviews expect exactly this kind of plain, opt-in explanation.

What a business sees

Counts: check-ins in the last 30 days, people here now, Wuzzes made public there ("Made here"), photos shared and approved. Never names, never receipts, never a trail. A person becomes known to a business only by contacting it.

Offline and sync

The phone holds the trail, Wuzzes, items and an outbox in SQLite. Writes carry a client_id and the server is idempotent on it. Pull is by updated_since. The person is almost always the only writer, so: last writer wins per field (title, note, privacy), items are a union, delete wins.

Export and delete

Live since v2.2.674: the server pages it through GET me/export and the app builds the zip; a Wuz exports only the person's own words and photos, and DELETE me deletes the memories they keep, leaving each companion their own photos as their own memory.

One tap each. The export is a zip a person can open without Doozer: one folder per Wuz (photos, receipts, a JSON file) and a plain HTML index. Delete removes a Wuz, its items and their files at once (a few seconds of undo on the phone, nothing kept after). Deleting the account removes all of it (API v1, DELETE me).

Where it starts

Live on Oct 25 2026: 34 check-ins by 19 people, no notes, no photos. There is nothing to migrate, so the model can be right from the first row.

Related: The Doozer App, The One Page, API v1, No Dark Patterns, Implicit Verification.

← The Value Model (how Doozer creates and captures value)The writer model is chosen on accuracy, not price →