A Public Online Diary Anyone Can Read, Without Publishing the Rest
Publish a single entry with a permanent link anyone can read, no account required, while every other page you have written stays private.
Product updates, service announcements, and engineering insights from Storyie
Publish a single entry with a permanent link anyone can read, no account required, while every other page you have written stays private.
Storyie saves what you write on your phone the instant you write it, offline or not, then syncs quietly once a connection shows up. Nothing you write is ever waiting on a signal bar.
Post short moments as they happen and Storyie compiles them into one diary automatically. No blank page at 11pm, no trying to remember what actually happened.
No Dropbox, no Drive, no export-and-import dance. Storyie syncs your diary between iPhone, Android, and the web on one account, so switching phones is just a sign-in.
Checking only plan name or only status made the same user Pro in one feature and Free in another. isProEntitled(plan, status) is the fix.
Soft delete on the server is only half the story. The sync pull contract must carry tombstones as typed delete changes — not omit the row and hope clients notice.
A notification tap is three different races depending on app state. Storyie stopped guessing with setTimeout and waits for Expo Router readiness instead.
In-memory retry timers forget their schedule when the OS kills the app. Storyie stores the backoff deadline in SQLite so the next launch picks up exactly where the last attempt left off.
Auth is expensive. We put a pluggable sliding-window rate limiter ahead of session lookup so brute-force never pays for a DB round-trip — and the Expo client uses the same budget.
One auth user is not enough. Storyie needs a public profile and a private profile on every real signup. We put that invariant in a Better Auth hook — and made every other path fail closed instead of inventing a row.
An offline edit is a promise the app has to keep after a force-quit. Storyie persists every pending mutation as a SQLite row with a status state machine, exponential-backoff retry, and an ownership guard against stale server data.
The canonical Lexical-to-Markdown path needs a live editor and a registered node for every type. Our export runs in a Lambda, so it walks the JSON tree directly instead.
A hard DELETE in a local-first app is a bug waiting for the next sync. Here is the tombstone column and the two guards that keep a deleted row deleted.
Our free plan allows 31 diaries a month. When "a month" was UTC in one place and local time in another, the reset and the dedup key disagreed at midnight on the 1st. Here is the fix.
A guest writes diaries, then signs in with Google. Better Auth deletes the anonymous user right after the link resolves — so one hook has one chance to move the data. We made it atomic, retryable, and safe when both sides already exist.
LWW is not "give up and take the newest timestamp." Storyie forgives one second of clock drift, breaks ties toward the server, guards uncommitted local edits at the SQLite layer, and always tells you when your version was replaced.
Web login broke with state_security_mismatch while mobile kept working. The fix was one plugin, in one position: nextCookies() had to be last.
Migration scripts exit 0 when they do not crash, not when your data arrived intact. We wrote a separate, read-only verification step to actually prove it before switching traffic to Turso.
Debounce is a data-loss window. Storyie writes to on-device SQLite synchronously on every keystroke and defers the network save — then uses refs, not state, to keep the race honest.
A 44-post import turned an O(N) parse into an O(N²) build and blew past the 60s SSG timeout. The fix hinges on caching the parse Promise, not its result.
Our FK cascades look correct in the schema and do nothing at runtime. We delete a user across 25 tables explicitly, inside one transaction, by hand.
A provider swap touches every page that reads the current user. We contained the blast radius to a single file — a CompatUser shim — instead of a 56-file rewrite. The trade-off: the shim tells one deliberate lie.
Per-route auth is a forgetting machine. We rebuilt Storyie’s Hono API so a new route is locked unless you explicitly open it — and a regression test keeps it that way.
We published 35 technical articles in about six weeks while building Storyie. Here are the concrete patterns — backed by likes and bookmark data — for what actually gets read.
We run Claude inside GitHub Actions to draft X posts weekly, store them in Supabase as drafts, review them ourselves, then let a second workflow publish on schedule. Here is how the whole thing fits together.
We replaced SaaS RUM tools with a self-hosted pipeline built on useReportWebVitals, Supabase, and a daily Lambda Cron. Cost: essentially zero. Alerting: two-day consecutive breach triggers Slack.
Color definitions were scattered across Tailwind, React Native, Lexical, and email templates — each using a slightly different shade of ochre. We fixed it with a two-layer palette → token design inside a single internal package.
Shared packages in Node, web in jsdom, mobile under jest-expo, E2E via Playwright on a self-hosted runner — here is how we organized four test layers across the Storyie monorepo and what we learned the hard way.
Repeated price changes, per-environment duplication, and manual SQL migrations convinced us to declare Stripe resources in Terraform. Here's the full setup.
Long-lived IAM keys in GitHub Secrets are a liability. We eliminated them with GitHub OIDC and Terraform — here's the exact setup we run in production.
We run RLS policies on every table in Storyie. After designing and operating 27 of them, the patterns collapsed into seven reusable shapes — here is each one with its intent and the pitfalls we hit.
Relying only on WHERE clauses for tenant isolation is an accident waiting to happen. Here is how we moved that guarantee down to the database with Supabase RLS.
Supabase Auth looks simple until you need it working correctly across Server Components, Server Actions, Middleware, and a React Native app simultaneously. Here is what we learned.
Plan limits that live in a pure-function package mean one change propagates to web, mobile, and tests simultaneously — no copy-paste, no drift.
Storyie is a cross-platform diary app where every entry is private by default, and going public is one tap. Here's what it does and how it's built.
One sst.config.ts, two stages, no WAF bill — how we wired SST v3 and CloudFront to keep production safe while still being able to test Stripe webhooks and OAuth flows.
Six months of running Storyie on SST v3 produced a list of things the docs don't cover — Pulumi Output resolution in transforms, stage-scoped env loading, Cron offsets to avoid connection pool contention, and more.
We run Storyie on AWS instead of Vercel — not out of stubbornness, but because SST gives us fine-grained CloudFront control, cheap Lambda cron jobs, and multi-tenant wildcard domains that Vercel can't match without a painful price tag.
How we keep eight scheduled Lambda jobs running cheaply and reliably inside our SST v3 monorepo, from load-spreading tricks to an AI-powered monthly report.
The scariest part of solo development isn't shipping — it's the invisible tax of keeping things running. Here's how we designed Storyie to operate itself.
Storyie runs in production on roughly $25/month. Here is exactly how we structured the infrastructure to get there, and why each service made the cut.
The honest answer to "does your solo project need an admin dashboard?" is: probably later than you think, but definitely earlier than you expect.
Push notifications are easy to fire once. Getting timezone handling, deduplication, and token lifecycle right — without a queue worker — is the real work.
Permission design is easy to under-engineer and easy to over-engineer. Here is how we landed on RBAC plus a Feature Gate — and why we deliberately left ABAC on the shelf.
We wired an AI agent to the App Store Connect CLI so that keyword research, 10-locale metadata drafts, and monthly ASO reviews run mostly unattended — with a dry-run gate before anything lands.
We gave an AI agent a permanent desk in our repo. Here is what seven cron jobs later actually looks like in practice.
Adding multi-tenancy to a small app sounds excessive — until you wire up Stripe. Here is what we built, why it clicked, and the one race condition we only found in production.
Three decision axes — cross-app usage, platform-agnosticism, and change/impact balance — that tell us whether code belongs in a shared package or stays in the app.
We run 11 GitHub Actions workflows across a Next.js + Expo pnpm monorepo without Turborepo or Nx — here is the full design and the lessons from operating it.
We added llms.txt and Markdown route handlers to Storyie so AI assistants can understand the app without parsing HTML — dynamically generated, ISR-cached, and privacy-safe by default.
Lexical has no built-in image node, so we built one and split it per platform. That split quietly became three implementations and two classes fighting over the type name "image". This is how we merged them back into one.
Same architecture, different implementations: how we built one image upload pipeline for Next.js and Expo using Cloudflare R2 and client-generated presigned URLs.
Supporting 10 languages in a cross-platform monorepo means four different i18n strategies — one per layer. Here is how we wired them together without losing our minds.
SDK 55 ships React 19.2 and React Native 0.83.2 — and three breaking changes that require real attention. Here's what hit us in the Storyie monorepo and how we handled it.
Push notifications go silent the moment a user deletes the app. We built a layered email system on SST Cron jobs and Resend so we can reach users wherever they are — without sending anything they didn't ask for.
We put every Drizzle schema and RLS policy into a shared monorepo package. Web uses it with the ORM directly; mobile uses the generated types via the same import. One source of truth, zero copy-paste.
After a year of shipping Storyie, our migration file count hit 56. Here is what we learned about running Drizzle ORM in production with Supabase — two-stage CI deployment, when to write custom SQL, and how we think about rollbacks.
Diary apps have a peculiar SEO problem: most content is private by design. Here's the four-layer filtering stack we built to surface only the public, quality entries users actually want discovered.
Sharing UI components across Next.js and Expo sounds appealing, but the ROI is almost never there. Here is the three-layer sharing model we settled on after a year of dual-platform development.
Two payment providers, one subscriptions table. How we designed shared webhook logic and idempotency so that a Stripe purchase on web and a RevenueCat purchase on iOS both resolve to the same Pro check.
One month, 13+ AI-generated PRs, nearly 100% merge rate. Here is what actually changes when a GitHub Actions workflow is writing most of your code.
Write an issue, attach a label, and let Claude handle the plan-then-implement loop — with a required human checkpoint between the two.
Supabase Auth, RLS, and RBAC each solve a distinct problem. Here is how we wired all three together in Storyie so the database enforces security even when application code forgets to ask.
One endpoint, six diary patterns, ten languages — and a validation + fallback layer that keeps the feature working even when the AI API is down.
How we wired SST Cron, Lambda, and the Anthropic API to generate a monthly diary analysis for every Pro user — and the design decisions around input limits, structured output, and idempotency that made it production-safe.
Empty timelines drive users away. We solved the cold-start problem with AI bot users that post real diary entries every day — here is how the whole system is built and what we learned running it.
Expo's "use dom" lets you reuse Lexical code on mobile almost as-is — but the boundary between DOM and native still needs a deliberate bridge.
yourname.storyie.com is a Pro feature: a memorable address for your public diaries and notes, live the moment you upgrade, no DNS or hosting involved. Here's what shows up there, what it costs, and what it doesn't do.
Real-world case studies showing how Speckit transforms feature requests into production code through structured AI collaboration.
Five practical ways to use Storyie: travel journal, parenting log, learning diary, gratitude practice, and creative writing space — with example entries for each.
Share one diary entry with a link, keep the rest private, and change your mind whenever you want. Here is how the public link, your profile page, and visibility actually work.
A deep dive into implementing subdomain-based multi-tenancy with Next.js 16 middleware, SST wildcard domain configuration, CloudFront CDN, and subscription-based access control.
Database schemas are the foundation of any application, yet they're often the source of the most frustrating bugs: runtime errors from typos in column names, incorrect types passed to queries, and subtle data inconsistencies that slip through testing. At Storyie, we eliminated these issues by...
Building a Lexical editor that works identically on web and mobile is one of the most challenging aspects of cross-platform development. At Storyie, diary entries created on our Next.js web app must render perfectly in our React Native mobile app, and vice versa. Through a two-package architecture...
Mobile app development has come a long way from writing separate codebases for iOS and Android. At Storyie, we chose Expo SDK 53 and React Native 0.79 to build a production-ready mobile app that shares code with our Next.js web application while delivering a native mobile experience. In this post,...
A comprehensive guide to deploying Next.js 16 on AWS using SST, featuring App Router patterns, Server Components strategies, and ISR caching techniques from our production application.
A deep dive into our monorepo architecture using pnpm workspaces, TypeScript project references, and cross-platform code sharing strategies.
We're excited to introduce Storyie, your personal story publishing platform!