Build Journey

KINDAback to the verdict

0%0 of 18 items done

Saved on this device only. Tick prerequisites first, then work the phases in order · do not start one until the checks above it pass.

A local journal whose hard problems are making plain files the real storage while keeping search fast, handling photo timestamps and coordinates honestly, and getting everything out in a form that reimports without loss.

estimated effort multi-daythe files for this build are in the project pack

RuntimeTauri 2 with React and TypeScriptStorageMarkdown files with front matter, media beside them, written atomicallyIndexSQLite with FTS5, rebuildable from the folder in one commandTimeLocal wall time plus the zone identifier, both stored, never one aloneMediaCopied into the folder, originals never moved or modified

Before step 1

Everything below is assumed from the first step. Tick each one when you actually have it, not when you plan to.

  1. installfree

    Why Builds the interface.

    Get it nodejs.org, or nvm install 22. open ↗

    Verify node --version prints v22 or higher

  2. installfree

    Why Builds the desktop shell and the file handling that must not corrupt an entry.

    Get it rustup.rs, then the Tauri prerequisites for your platform. open ↗

    Verify cargo tauri info reports no missing dependencies

  3. installfree

    Why Search is the feature that makes a long journal usable, and FTS5 is not present in every build.

    Get it Most distributions include it; check rather than assume. open ↗

    Verify A create virtual table using fts5 statement succeeds

  4. have readyfree

    Why The zone-unknown capture time, the orientation flag and the embedded coordinates are all only testable with real files.

    Get it A few pictures from a phone, plus one with location switched off and one rotated.

    Verify Each behaves as its phase requires

  5. decidefree

    Why It decides the FTS5 tokenizer, and changing that later means reindexing after the person has already stopped trusting search.

    Get it Write down every script you expect to write in, including the occasional one.

    Verify The tokenizer choice in the README names them

  6. have readypennies per month, or free on a spare disk

    Why A local-first journal with no offsite copy is one disk failure from nothing.

    Get it Any object storage or removable disk with a key you hold.

    Verify A backup written and restored into an empty install

Data model

Create these before the first phase that stores anything. Changing a table later is the expensive kind of change.

- On disk: entries/2026/2026-09-11-0743-<id>.md with YAML front matter, and media/2026/09/<id>-<n>.<ext>
- entries(id, path, written_at_local, written_at_utc, timezone, latitude, longitude, place_label, body_sha256, indexed_at)
- media(id, entry_id, path, mime, bytes, sha256, taken_at_local, taken_at_zone_known, latitude, longitude, width, height)
- tags(entry_id, tag) and links(from_entry, to_entry) · both derived from the file, both rebuildable
- revisions(entry_id, ordinal, body_sha256, path, at) · previous versions kept as files under revisions/
- entries_fts(entry_id, body) · an FTS5 table whose tokenizer is a recorded decision, not a default nobody looked at
- Every table above can be dropped and rebuilt from the folder, and the rebuild is a test rather than a claim.

Environment variables

These go in a .env file the app reads at startup. The pack's .env.example is this table as a file · copy it, never commit the filled-in version.

VariableNeededExampleWhere the value comes from
JOURNAL_DIRrequired~/Documents/JournalThe folder that is the journal. Everything else is rebuildable from it.
INDEX_PATHrequired~/.local/share/journal/index.dbThe SQLite index. Safe to delete, because the rebuild is tested.
FTS_TOKENIZERrequiredunicode61Recorded as a decision. Changing it reindexes and touches no entry file.
REVISION_KEEPrequired50How many previous bodies to keep per entry, as files under revisions/.
STRIP_LOCATION_ON_EXPORTrequiredtrueThe default for anything leaving the machine. Overridden per action, never globally by accident.
BACKUP_TARGEToptionals3://my-bucket/journalEncrypted backup destination, if you configure one.
BACKUP_PASSPHRASEsecretoptionala long passphrase, redactedEncrypts the backup. Lose it and the backup is noise.

The build, in order

  1. Files first, index second

    Entries live as Markdown on disk and the database can be deleted without losing anything.

    1. The order is the whole guarantee; reversing it makes the folder a copy.

      Files src-tauri/src/storage.rssrc/entry/save.ts

    2. A truncated entry file is the one loss this design exists to prevent.

      Files src-tauri/src/storage.rstests/atomic-write.test.ts

    3. An index nobody has rebuilt is an index that cannot be rebuilt.

      Files src/index/rebuild.tstests/rebuild.test.ts

    done when · tick each as it passes
    watch out
    • Storing entries in the database and exporting files on demand looks identical for a year and then diverges the first time an export fails halfway or a schema migration goes wrong, and the folder people trusted turns out to have been a stale copy. Write the file first, index afterwards, and test the rebuild so the dependency only runs one way.
  2. Search that works in the language you write in

    Full-text search returns the right entries for the scripts and short words the person actually uses.

    1. The default is right for English prose and wrong for anything written without spaces.

      Files src/index/fts.tsdocs/search.md

    2. Silently returning nothing for a two-character search reads as a broken app.

      Files src/search/query.tstests/short-query.test.ts

    3. One script in the fixtures means one script that works.

      Files tests/search-scripts.test.ts

    done when · tick each as it passes
    watch out
    • Accepting the default tokenizer and testing with English prose ships a journal where a Japanese or Chinese entry is unfindable, and switching later means reindexing everything after the person has already decided search does not work. Choose the tokenizer against the actual languages in the journal, and put a two-character search and a non-Latin search in the suite on day one.
  3. Photographs, timestamps and places

    Imported media keeps its own truth and the entry keeps a time that means something.

    1. Verified by comparing checksums before and after import.

      Files src/media/import.tstests/media-untouched.test.ts

    2. The usual metadata field does not, and guessing makes a holiday land on the wrong days.

      Files src/media/exif.ts

    3. The orientation flag is information, not a defect to be corrected in the file.

      Files src/media/render.tsx

    done when · tick each as it passes
    watch out
    • Treating a photograph's capture time as if it were in the machine's current zone puts every picture from a holiday on the wrong day for the rest of the journal's life, and displaying the image without honouring its orientation flag shows half a trip sideways. Record whether the time carried a zone, ask when it did not, and rotate at render time only.
  4. Revisions and a timeline worth trusting

    Edits are recoverable and the calendar view never misplaces an entry.

    1. A revision inside the database contradicts the entire storage rule.

      Files src/entry/revisions.ts

    2. Backdating is honest only when both are kept.

      Files src/entry/dates.ts

    3. Converting to the current zone misfiles every entry written abroad at night.

      Files src/views/timeline.tsxtests/timeline-zones.test.ts

    done when · tick each as it passes
    watch out
    • Grouping the timeline by converting each entry's stored instant into the current machine zone moves entries written late at night abroad onto the previous or next day, so a month of travel is silently misfiled. Group by the local date recorded with the entry, and keep the instant only for ordering.
what this build does not replace
after v1, if you want it

Need the files? The project pack on the verdict page hands your agent the whole brief · more notes & knowledge.