Kuga
Back

OwnStory

A live product that turns photos, videos, and voice notes into a private story page people unlock with one QR scan — for gifts, weddings, memorials, birthdays. Real users, real traffic, no app required. I led the 3-month pivot that shipped it as three production surfaces.

May 3, 2026

Problem

OwnStory began as a digital memorial platform — a niche product with steady but limited demand. The founder saw a larger opportunity: pivot into a **private digital story platform** for life moments — birthdays, weddings, graduations, travel, new babies, memorials — positioned explicitly against social media (private by default, owned by the user, designed for gifting rather than posting). Each story gets a QR code that attaches to a physical gift, so the digital and physical sides of a keepsake live together. The engineering problem was: ship that pivot, build out the supporting AI infrastructure for scale, and keep the original memorial / tribute products alive for existing customers — without a migration window.

Constraints

I joined as the founding engineer, working directly with the founder/CEO, with no BA or PM layer between an idea and shipped code. Three months from pivot decision to production launch for the new platform. Existing memorial and tribute products had to stay alive throughout — no scheduled downtime, no "we're rebuilding, check back." AI infrastructure had to be cost-aware from day one because budget ceilings were real and an unbounded LLM bill was a company-level risk.

What I built

Three product surfaces shipped to production: - **ownstory.com** — the new private story platform for any life moment, with QR-linked physical gifts and private invite-only viewing - **eternstory.com** — memorial-focused, multi-instance deployment for the existing memorial demand - **eternaltribute.com** — the original tribute product, kept alive for existing paying customers All three run on **one multi-tenant codebase** with per-tenant custom domain routing, tenant-aware admin tooling, and atomic onboarding so a new tenant going live is a configuration change, not a deploy. On the product surface I shipped a featured-stories editor, admin user filters, scan-tracking for QR-linked stories, collaborative storytelling (multiple contributors per story), and the tenant homepage configuration system that lets each domain present a fully custom landing page from the same engine. *(I also contribute to a separate AI platform the same team is building — that work has its own case study.)*

Key decision

**One multi-tenant codebase, three product surfaces.** The alternative was three apps — separate codebases for ownstory, eternstory, and eternaltribute — and that path is the one most teams default to under pivot pressure because each app feels like a separable concern. We didn't take it. The cost of the chosen path was real engineering investment up front: per-tenant custom domain routing, tenant-aware data, atomic onboarding, a config layer that lets each surface diverge in branding and feature flags without diverging in code. The payoff was that *shipping a new product surface became a tenant configuration*, not a separate build. When the founder wanted to spin a memorial-only deployment for a specific market, that didn't queue behind a "build a new app" project — it was a tenant record with a domain and a config. As the founding engineer shipping a pivot under 3 months, that decision was load-bearing. Three codebases would have been three deploys, three on-call surfaces, three sets of dependencies to keep current. With one, the surface area scales with tenants, not with apps.

Outcome

Three surfaces live in production on one codebase. Memorial product continued to serve existing customers across the pivot — no migration window. Atomic QR-linked onboarding works across surfaces, so a new tenant goes live by scanning a QR, not by waiting on engineering. Adding a new product variant — a memorial-only deployment for a specific market, a tribute surface with a different brand — is a tenant configuration plus a domain, not a separate build.

Hindsight

Three months for a pivot of this scope was genuinely tight, and the thing I'm most glad we got right was building the multi-tenant configuration layer up front rather than treating each surface as a one-off. Once the config layer existed, shipping a new product variant stopped being an engineering project — and that's the property that made three surfaces sustainable for a founding engineer. The thing I'd reconsider is being more aggressive about feature flags earlier. Solo and small-team builds tend to ship without flags because flags feel like infrastructure when features feel urgent. But the moment a regression hits a single tenant — and it always does — having flags means the rollback is scoped to the broken thing, not a full-deploy revert across surfaces. I'd treat them as table stakes next time.

Architecture notes

Two pieces shape how this product surface holds together.

Per-tenant custom domains, atomic onboarding

Each surface — ownstory.com, eternstory.com, eternaltribute.com — resolves to the same Next.js application. Middleware reads the request host, resolves it to a tenant record, and attaches tenant configuration to the request context so downstream code (UI, API, jobs) can read tenant-aware data and branding without per-domain code paths.

Onboarding is atomic in the literal sense: a tenant going live happens inside a single transaction that creates the tenant record, provisions storage, registers the domain, and emits a QR code that links the physical product (memorial plaques, tribute pages) to the digital surface. Either every step succeeds or none of them do. There is no half-onboarded state for support to clean up later.

The tenant configuration layer

Each tenant has a config record describing branding, feature flags, content rules, and surface-specific copy. UI components, API handlers, and job workers all read the config from request context — the same React component renders ownstory's "create a story" affordance and eternstory's "create a memorial" affordance from the same code path, because the surface-specific copy and feature gates live in tenant config, not in component code.

That layer is what makes shipping a new product variant a config change. Without it, every variant becomes a fork of the codebase, and forks compound badly.

Why the multi-tenant bet held

As the founding engineer, three apps would have been three deploys, three on-call surfaces, three sets of dependencies to keep current. Engineering surface area would have grown with product surface area, and at solo capacity that's the failure mode that kills a pivot before it ships.

One codebase plus per-tenant configuration meant the surface area scales with tenants instead of apps — and tenant onboarding is a config record, not a build. That property is the difference between shipping a pivot in three months and not shipping it at all.

Tech stack

Next.js (App Router)TypeScripttRPCDrizzle ORMPostgreSQLTurbo monorepoVercel