Private journal · Greek & English · self-hosted

My Story

A private, self-hosted journal. Speak or write in Greek or English; it keeps every word in plain files on your own machine, puts your life in order, and names what happened.

Download for Windows ·Install on anything else ·Source

What it does

Yours, on your machine

Entries are plain markdown files in a folder you own. No account, no company, no server but yours.

Speak Greek or English

Record and it transcribes, word for word or tidied. The original stays beside the English.

Put in order

Each entry carries when it happened, not when you typed it. A rough range is enough.

Names what happened

Gaslighting, DARVO, scapegoat, fawn, gray rock and more, each with a plain definition and your own sentence as proof.

Chapters you can check

Entries grouped into episodes and drafted into chapters. Every sentence cites the entry it came from.

Any AI, or none

Free cloud tiers, your own keys, or models on your own hardware. Local-only is enforced, not preferred.

Showcase

Dev

My Story is one Node process: an Express server (server.ts, bundled to dist/server.cjs by esbuild) serving a React 19 + Vite client (src/App.tsx). No database. Entries are markdown files with front matter, written atomically (temp file + rename). Trash is a folder; nothing is ever deleted.

Run it

git clone https://github.com/Stravelakis/mystory.git
cd mystory
npm install
npm run dev          # http://localhost:38726
npm run desktop      # the Electron window
npm run build-exe    # NSIS installer + portable build in release/

Port 38726 was picked with the standards’ pick-port.mjs (5 digits, 10000–49151, no pattern, no clash). Override with PORT; bind with HOST (default is dual-stack, because localhost resolves to ::1 on Windows).

Architecture

src/App.tsx  ──fetch /api──►  server.ts  (routes only)
                                 │
       ┌──────────┬──────────────┼──────────────┬─────────────┐
    vault.ts   providers.ts   vocabulary.ts   episodes.ts   translate.ts
    (disk)     (every model)  (35 terms)      (chapters)    (DeepL / model)
                  │
               live.ts  (Gemini Live over WebSocket)
Module Owns
providers.ts every model call: routing, fallbacks, token budgets, error explanations
live.ts Gemini Live sessions: text jobs through speech, PCM16 conversion via ffmpeg
vocabulary.ts the terms, the tagging prompt, and normalise(), which drops any label whose quote is not in the entry
vault.ts entries, audio, .trash/, timeline order by when it happened
episodes.ts groups dated entries by gap, drafts chapters, strips invented citations
chapters.ts long chapters in parts: splits by an estimated token budget (EPISODE_PART_TOKENS, default 12000), saves each part to vault/chapters/ as it lands, resumes from the first missing part
env.ts / auth.ts .env allow-list and writes; scrypt passcode and signed sessions
maintenance.ts Settings → Advanced: Repair and Update (GitHub Releases API, SemVer)

The model router

Six jobs: transcribe, when, indicators, companion, title, synthesis. Each has three slots, MODEL_<JOB>_1/_2/_3 = provider::model. A failure (quota, retired model, empty answer, or an answer that fails the job’s validate check) steps to the next slot. The error that surfaces leads with the first failure, in plain English.

Provider kinds: openai (any /v1 endpoint: Groq, Mistral, Cerebras, NVIDIA, OpenRouter, OmniRoute, local runtimes), gemini (Google SDK), gemini-live (bidiGenerateContent). A local model is just a provider with local: true.

Routing: cloud-first, local-first, local-only. Local-only is enforced twice: the chain excludes cloud providers, and middleware returns 409 on the Google routes.

Consent gate. Without CLOUD_CONSENT=yes, cloud providers (DeepL included) are filtered out of every chain. With no local provider left, the call fails with a message pointing at the checkbox.

Gemini Live notes (checked 25 Sep 2026)

  • gemini-3.8-live-extended-thinking has no TEXT output. The app asks for AUDIO with outputAudioTranscription, a “silent function” system instruction, and waits 1.5 s after turnComplete. An empty answer is retried once.
  • gemini-3.5-transcribe-live returns inputTranscription; a 1008 close after the transcript is success.
  • All Live audio is sent as 16 kHz mono PCM16, converted with ffmpeg.

Data

---
id: 20260816-142530-a3f
title: …
created: …                   # never rewritten
when: "the summer before I left school"
when_start: "2011"
when_end: "2011"
when_confidence: inferred    # stated | anchored | inferred | unknown
found: [{"id":"gaslighting","evidence":"…exact quote…"}]
verbatim: "…"                # the untidied transcript, if tidied
---

Configuration

Every variable is listed in .env.example. The desktop app keeps .env and the vault in %APPDATA% (MYSTORY_ENV_PATH, VAULT_DIR), and uninstalling leaves them in place.

Quality gates

npm run lint (tsc), npm test (vitest, no network) and npm run build run in CI on every push and PR, along with a gitleaks scan of the whole history. gitleaks also runs in the pre-commit hook (git config core.hooksPath githooks).

English

My Story is a journal that lives on your own computer. You talk or type, and it keeps what you said in ordinary text files in a folder you choose. Nobody hosts it for you and there is nothing to sign up for.

What it does

  • Writes down what you say. Press Record and speak in Greek or English. You can keep your exact words, or a tidied version with the “um”s taken out. The exact words are always kept in the file either way.
  • Translates, without replacing. Greek entries get an English version beside them. The original is never changed.
  • Puts things in order. You can say when something happened in your own words (“the year my brother left”). It works out a rough date range, so entries line up in the order they happened, not the order you wrote them.
  • Names what happened. As you write, it spots patterns such as gaslighting, DARVO, scapegoating or the golden child, and responses such as fawning or gray rock. Each one comes with a short explanation and the exact sentence of yours it was found in. If it can’t point to your words, it doesn’t show the label.
  • Helps you write the book. It groups entries into episodes by time and drafts each one as a chapter. Every sentence is marked with the entry it came from, so you can check it. A long chapter is written in parts and each part is saved as soon as it’s done, so if something fails you only lose the part it was working on.

Where your words go

This is the part to understand before you start.

  • If you use cloud AI (Google Gemini, Groq, Mistral and others), what you wrote or said is sent to that company to be processed. On free tiers, that company may use it to improve its products. The app won’t use any cloud service until you tick “I understand where my words go” in Settings.
  • If you use local AI (a model running on your own computer), nothing leaves the machine. Set routing to Local only and the app refuses cloud services even when keys are saved.

Getting it

  • Windows: download the installer from the latest release and run it. It installs like any app and has a normal uninstaller. Your entries and settings stay behind when you uninstall.
  • Anything else: install Node.js 20, then follow INSTALL.md. It also runs in Docker, or on a small home server that you open from your phone over Tailscale.

Keeping it safe

Set a passcode first (Settings → The lock). Your entries are plain files, so you can back them up by copying the folder. Settings → Advanced has Repair, which checks the app’s own files, and Update, which installs the newest release. Neither one touches your entries.

This is a journal, not a therapist. It describes behaviours and gives them their common names. It doesn’t diagnose anyone. If you are in crisis, please reach out to a person.

ELI5

Imagine a diary that listens.

You press the big button and talk. The diary writes down every word, in Greek or English. It keeps the diary in a drawer in your house (your computer), not in somebody else’s house.

Then it helps in three ways:

  1. It puts the pages in order. You say “this happened the summer we moved”, and it puts that page with the other pages from that summer.
  2. It tells you what things are called. Sometimes people do a thing that has a name, like saying “that never happened” when it did. That one is called gaslighting. The diary shows you the name, what it means, and the sentence where you said it.
  3. It helps you write a book. It puts pages from the same time together and turns them into a chapter. Every sentence points back to the page it came from, so nothing gets made up.

One rule to know: to understand your voice, the diary can ask a helper. The helper can be a big company far away (fast, free, but it hears your words) or a helper that lives inside your own computer (nobody else hears anything). You choose. It won’t ask the far-away helper until you tick the box that says you understand.

Put a lock on it first. Settings → The lock.

Walkthrough: your first entry

  1. Install. Run the Windows installer from the latest release, or npm install && npm run dev anywhere Node 20 runs. It opens at http://localhost:38726.
  2. Settings → The lock. Set a passcode. Until you do, anyone who can reach the port can read every entry.
  3. Settings → Where your words go. Leave it unticked to keep everything on this machine (you need a local model for the AI jobs). Tick it to allow cloud providers, knowing your words go to them.
  4. Settings → Provider keys. Paste a free Google Gemini key (or Groq, Mistral, OpenRouter, or a local endpoint). Press Test, then Load models, then Save all.
  5. Vault → Record. Speak in Greek or English. Stop, and the words appear. They are already saved.
  6. When did this happen? Type it in your own words, or press Work it out for me. "The summer before I left school" is a good answer.
  7. Indicators fill in as you write: each named pattern with its definition and the sentence it came from. A label it cannot quote you for is dropped.
  8. Synthesis → Find my episodes → Draft this episode. A chapter, where each sentence names the entry it rests on. Copy it into your book or blog.