Storify is a Swedish AI journaling app for turning short notes, guided answers, or an interview-style chat into polished diary entries. Users can write through several flows, choose a narrative voice, save entries to a private journal, export/share selected entries, and optionally publish anonymized entries to the community.
Live site: https://mystorify.se
- AI diary generation with Anthropic Claude, including streaming responses and fallback model support.
- Four writing modes: AI interview, step-by-step wizard, quick entry, and free-writing editor.
- Swedish-first UI and generated content, with some voices intentionally using other styles or English.
- Private account-based journal backed by Supabase Auth and Postgres.
- Profile, theme, accent, font, avatar, and account settings.
- Entry editing, retone generation, title generation, PDF/image-style sharing utilities, and public share links.
- Community page for voluntarily shared diary entries.
- Blog posts from local Markdown files in
src/posts. - Badge/gamification system for writing milestones and feature usage.
| Area | Technology |
|---|---|
| App | SvelteKit 2, Svelte 5, TypeScript, Vite |
| Runtime | Node adapter with server.js |
| AI | Anthropic SDK |
| Database/Auth | Supabase |
| Rate limiting | Upstash Redis |
| Resend | |
| Maps/geocoding | Google Maps / Google Cloud credentials |
| Rich text | Tiptap |
| PDF/export | jsPDF, html2canvas |
| Tests | Vitest, Testing Library, svelte-check |
| Hosting | Render web service |
Use npm; this repo includes package-lock.json.
npm install
cp .env.example .env
npm run devThe dev server runs at the URL printed by Vite, usually http://localhost:5173.
Render deploys with Node 20, and that is the safest local version to use as well.
Copy .env.example to .env and fill in the services you need for the feature you are testing.
| Variable | Purpose |
|---|---|
ANTHROPIC_API_KEY |
Required for AI generation and chat. |
CHAT_MODEL, CHAT_MAX_TOKENS |
Chat/interview model configuration. |
GENERATE_PRIMARY_MODEL, GENERATE_FALLBACK_MODEL, GENERATE_MAX_TOKENS |
Diary generation model configuration. |
PUBLIC_SUPABASE_URL, PUBLIC_SUPABASE_ANON_KEY |
Supabase client/auth configuration. |
SUPABASE_SERVICE_ROLE_KEY |
Server-side jobs and privileged API work. |
RESEND_API_KEY |
Email sending. |
UPSTASH_REDIS_REST_URL, UPSTASH_REDIS_REST_TOKEN |
Server-side rate limiting. |
GOOGLE_MAPS_API_KEY, GOOGLE_CLOUD_PROJECT_ID, GOOGLE_APPLICATION_CREDENTIALS_JSON |
Places/geocoding support. |
CLEANUP_SECRET |
Protected cleanup endpoint access. |
SUPABASE_AUTH_EXTERNAL_GOOGLE_CLIENT_SECRET |
Google auth integration secret when enabled in Supabase. |
VITE_API_BASE_URL |
Legacy/optional API base setting; current app calls local API paths. |
The database schema lives in supabase/migrations. Apply the migrations in order to a Supabase project before using authenticated flows.
Core tables/features include:
profilesfor account profile, preferences, and badge backfill state.entriesfor private saved diary entries and public share links.community_entriesfor community-shared copies.user_badgesand related badge migration data.
Supabase Row Level Security policies are part of the migrations.
npm run dev # start local dev server
npm run build # production build
npm run preview # preview the production build
npm run check # svelte-check + SvelteKit sync
npm run test # run Vitest in watch mode
npm run test:run # run Vitest once
npm run test:coverage # run Vitest with coveragescripts/backfill-titles.mjsbackfills entry titles.
storify/
├── src/
│ ├── routes/
│ │ ├── +page.svelte # landing/dashboard
│ │ ├── interview/ # AI interview flow
│ │ ├── wizard/ # guided multi-step flow
│ │ ├── quick/ # short entry flow
│ │ ├── editor/ # free-writing editor
│ │ ├── journal/ # private saved entries
│ │ ├── community/ # public shared entries
│ │ ├── profile/ # profile, settings, account pages
│ │ ├── blog/ # Markdown-backed blog
│ │ ├── shared/[shareId]/ # public entry links
│ │ └── api/ # generation, chat, share, badges, email, geocode
│ ├── lib/
│ │ ├── assets/ # fonts, icons, custom emoji SVGs
│ │ ├── components/ # reusable Svelte components
│ │ ├── data/ # tones, prompts, badges, copy, samples
│ │ ├── gamification/ # badge evaluation and awarding
│ │ ├── stores/ # Svelte stores for auth, wizard, UI prefs
│ │ ├── supabase/ # browser/server Supabase clients
│ │ ├── utils/ # date, sharing, PDF/image, title helpers
│ │ └── validation/ # input validation, sanitizing, rate limits
│ ├── posts/ # blog Markdown
│ └── service-worker.ts # service worker support
├── docs/tones/ # human-readable tone documentation
├── scripts/ # maintenance scripts
├── static/ # favicons, manifests, OG image, robots/sitemap
├── supabase/migrations/ # database schema and RLS migrations
├── render.yaml # Render web service
├── server.js # Node production server wrapper
└── package.json
Voice definitions are split between:
src/lib/data/tones.tsfor UI metadata.src/lib/data/tonePrompts/for generation prompts.src/lib/data/voiceSamples.tsandsrc/lib/data/voiceGallery.tsfor previews and examples.docs/tones/for longer tone documentation.
When adding a new voice, update all relevant files and add tests where prompt or generation helper behavior changes.
render.yaml defines storify, the Node web service running node server.js.
The production build uses @sveltejs/adapter-node. server.js adds cache headers for long-lived static assets and short-lived metadata files before handing requests to SvelteKit.
Storify stores account data, saved entries, preferences, badges, and community links in Supabase for logged-in users. Entries are private by default. Public sharing and community publishing are explicit user actions.
AI generation sends the provided diary input and profile context needed for the selected flow to Anthropic. Optional location-related features are used for weather/geocoding behavior when enabled.
See the in-app /privacy, /terms, and /cookies pages for user-facing policy text.
Proprietary software. All rights reserved.