Inspiration
We're more connected than ever and, at least it feels this way, people are getting lonelier. Social feeds run on persistent identity and performance: every post is a name, a face, someone to follow. I wondered what connection looks like when all of that is taken away and the most personal thing people keep, a diary, seemed like the right place to start.
A diary isn't necessarily a secret, but it's not something people actively share either. I wanted to give that fading habit a warm place to be read not a stage and let people know each other's lives without being connected in any way. The hackathon added a second question worth answering: how far and how fast can one person build with an AI agent?
What it does
Diary of a Stranger is a small pool of strangers' latest diary pages. Open the site and you're immediately reading someone's newest entry no landing page, no login, no name anywhere. Seven mood chips (melancholy, hopeful, tired, late night, family, work, small joys) steer the draw; if nothing matches tonight, you still get a random stranger plus a dim whisper saying so.
Hearts attach to whole sentences, not entries one per person per sentence and comments cap at 140 characters, shown with time only. Reading is open; reacting needs a login, and every wrong login looks the same. Publishing from Obsidian sends only your newest entry, and each publish replaces the last — one row per account, enforced by the database itself. Your page shows your entry exactly as strangers see it: hearts on the lines that landed, comments beneath as "a stranger, 2 hours ago." A ban list checks entries and comments alike, and a report removes content from the pool instantly.
How we built it
Two plain programs, one repo, one command to run. The pool is Node.js 22 + Express 5 + better-sqlite3 a single SQLite file holding every account's current entry, per-sentence heart counts, comments, reports and sessions. Pages are fully server-rendered: hearts, comments, reports and logins are plain form POSTs with zero client-side JavaScript, passwords hash with Node's built-in scrypt, and one hand-rolled stylesheet carries the nostalgic-warm look. No frontend framework.
The Obsidian side is a TypeScript plugin (esbuild → one main.js, official sample base) with four settings pool address, diary folder, username, password and a single command: find the newest diary note, preview it with the chips pre-checked from your vault's tags, send via Obsidian's requestUrl.
I built it as eight slices, each verified with curl and by hand in the browser, with an agent working directly in the files backed by an 18-check contract test that spawns a throwaway pool and replays the plugin's exact HTTP payloads.
Challenges we ran into
The pool was smooth; the plugin's first real publishes were not.
One stopped at "no markdown files in Diary" note discovery only scanned direct children of one exactly-typed folder, and real vaults aren't shaped that way. Rebuilt: recursive subfolders, / meaning the whole vault, normalized paths, and Notices that say what the scan actually sees.
Another died with no error at all: a sandboxed renderer's cross-origin POST to plain http://localhost can be blocked before anything catchable the missing failure Notice was itself the clue. Switching to requestUrl, serving CORS preflights on the API, and wrapping the send in a catch fixed it.
A third left three console lines, then silence: the preview modal was constructed but never opened. And my favorite came later a refactor broke an import while the typechecker stayed green, and the contract test failed loudly within seconds.
Accomplishments that we're proud of
- The anonymity promise is checked, not hoped for — a sweep across every public page finds zero username leaks.
- "Only your newest entry is public" is enforced by database structure and verified at that level.
- A real publish from my actual Obsidian vault the one check an agent can't run landed in the pool and showed on
/mine. - Moderation verified end to end: report → instant removal from draws → review page with content and time.
- 18 contract checks green; one command to start; five seeded strangers covering all seven chips, alive on first run.
- Publish-on-save went from a one-sentence request in my kick-the-tires feedback to a shipped, opt-in setting.
What we learned
- Test the interface the way its consumer actually uses it function-level tests would have stayed green through the quiet breakage the contract test caught.
- Enforce rules with structure one row per account beats a rule code has to remember.
- Silent failures are the real adversary the absence of expected feedback is the bug report.
- Subtraction ships no client JS, no email, no frontend framework removed whole classes of debugging.
- Building with an agent slices with mechanical checks kept me in control, and the product decisions stayed mine: the chip set, login as a permission door, moderation's shape, the warm aesthetic. Next time I'd run the real-world check much earlier a contract test can't see your sandbox, folder layout, or Obsidian, and both meaty bugs lived exactly there.
What's next for Diary of a Stranger
- A hosted pool and with it, real moderation behind the stub: queues, appeals, trust signals.
- OAuth into diary apps that offer it, so the plugin isn't the only way in.
- Password recovery today a lost password means a new identity: in spirit, but not kind.
- Random picks that stay random but get better at landing on someone unlike you diversity as a feature, never recommendations.
Built With
- better-sqlite3
- css3
- express.js
- git
- github
- html5
- javascript
- node.js
- obsidian
- opencode
- sqlite
- typescript
Log in or sign up for Devpost to join the conversation.