Jerome GaoFull Stack Engineer
All projectsIndependent product

Independent product

Osprey Pulse

A live AI goal companion that turns one meaningful intention into an editable daily plan and lets users grow Ozzy, their pet companion, through daily progress.

Role
Independent Full Stack Engineer and product owner
Timeframe
2026 - Present
Status
Live
Osprey Pulse website showing an editable daily goal, progress, and Ozzy the pet companion
The live product now leads with AI planning, goal growth, and companion-led motivation rather than sports prediction and ratings.

I designed and shipped the product from its bilingual Next.js and Expo experiences through the ASP.NET Core API, structured AI planning, PostgreSQL model, automated tests, Android preview, and an OpenTofu- and Ansible-managed production path.

The product direction

Osprey Pulse began around sports data, prediction, and community ratings. That direction produced a technically interesting system, but it did not create a strong reason for people to return. I repositioned the product around a more useful promise: move one meaningful goal a little every day.

The current experience starts with a goal in the user's own words. AI helps turn it into a structured plan, but the user can review and edit every part. Today presents a small next step; completing tasks or checking in earns progress; Journey preserves the history; and Ozzy, the user's pet companion, grows with that effort. The pet is not decoration added after the workflow—it is the feedback surface for consistency.

The launch scope deliberately stays focused. One active goal is easier to understand than a life-management dashboard, and missed days do not punish or downgrade the companion. Social and richer sharing ideas exist in the product direction, but the live core is the goal, plan, daily progress, private history, and Ozzy loop.

From AI answer to durable plan

The AI boundary returns structured plan data rather than prose that the rest of the product has to interpret. Provider routes carry model, credential, request-per-minute, daily-usage, retry, and cooldown rules. A draft can ask for missing information before it proposes the plan, and confirmed data is saved only after the user reviews it.

AI failure is treated as a normal product state. Invalid JSON, rate limits, exhausted routes, or missing provider configuration can fall back to validated server templates. This keeps the first-use path available and prevents an external model from becoming the system of record.

Contracts, state, and history

The C# API publishes OpenAPI. The frontend monorepo generates TypeScript types, Zod schemas, and its SDK from that source instead of maintaining parallel handwritten DTOs. ASP.NET Core returns RFC 9457 Problem Details, so web and native clients can display field errors and recovery actions consistently.

The data model distinguishes an editable future from an immutable past. Plans can evolve, but a completed day keeps its task snapshot. XP changes are append-only, and task completion, check-in, and companion interactions use idempotency keys so a retry cannot silently award progress twice.

A production path designed to move

The public frontend runs on Vercel. API traffic reaches Cloudflare's edge, passes through a Cloudflare Tunnel to loopback-only Nginx on an Azure VM, and then reaches the Dockerised ASP.NET Core service. The VM exposes no public inbound application ports, and Azure PostgreSQL uses private network access.

The platform is reproducible. OpenTofu manages the Azure and Cloudflare resources, DNS, Key Vault, Tunnel, and R2 buckets. Ansible prepares the host and deploys the Compose stack. GitHub Actions builds amd64 images, publishes immutable SHA tags to GHCR, deploys through Cloudflare Access, and smoke-tests the public health endpoint. Docker health checks, auto-heal, log rotation, and Nginx retry rules keep the deliberately small host practical.

This shape also avoids coupling the clients to one compute provider. The API hostname stays stable, and Docker, Nginx, the Tunnel, Ansible, and the image registry can move together when the free-tier VM is no longer the right host.

What is live now

The marketing site and web experience are live at ospreypulse.com. The download page serves an Android preview APK from downloads.ospreypulse.com, backed by Cloudflare R2. The production API has a public health path, while the application data remains behind private infrastructure.

The live release keeps privacy-aware friend and sharing features outside the critical path. The shipped experience stays centred on the goal, daily progress, private history, and pet-companion loop.

Evidence

Full-stack evidence

A visible path from client experience to production operations.

  1. 01Product

    Goal-first web and mobile experience

    Next.js and Expo present one clear loop—describe a goal, review a plan, complete today's step, and grow the pet companion Ozzy—in English and Simplified Chinese.

  2. 02Contract

    Generated client contracts

    OpenAPI generates TypeScript types, Zod schemas, and the fetch SDK; RFC 9457 Problem Details gives every client a stable error vocabulary.

  3. 03API

    Clean Architecture .NET service

    ASP.NET Core Minimal APIs keep HTTP thin while application handlers and domain entities own goals, plan versions, daily snapshots, check-ins, XP, Journey, and companion rules.

  4. 04AI

    Structured planning with fallback

    Gemini routes return typed plan data under rate and usage controls; validated server templates remain available when a provider is unavailable or a response is invalid.

  5. 05Data & quality

    Durable progress and real PostgreSQL tests

    EF Core migrations persist goals and append-only progress. Idempotent mutations protect XP, while xUnit, Testcontainers, and Respawn cover the API against PostgreSQL.

  6. 06Delivery

    Infrastructure and release as code

    OpenTofu provisions Azure and Cloudflare resources, Ansible configures and deploys the VM stack, GitHub Actions publishes GHCR images, and R2 serves product media including the Android preview.

Architecture

System flow

The boundaries that kept product work, data, and external side effects understandable.

  1. 01

    Next.js web and Expo mobile

    Platform-specific interfaces share design tokens, localization, API infrastructure, query state, and generated contracts without forcing web UI onto native.

  2. 02

    Versioned REST contract

    A generated OpenAPI client carries authenticated requests through stable /api/v1 endpoints and turns typed errors into recoverable UI states.

  3. 03

    ASP.NET Core application and domain

    Identity, plan generation, daily tasks, reflections, Journey, XP, and Ozzy are separated into explicit handlers and durable domain rules.

  4. 04

    Gemini plus deterministic templates

    Provider routes create structured drafts when available; server-owned templates keep the onboarding path useful when AI is rate-limited or unavailable.

  5. 05

    Private PostgreSQL and object storage

    Azure PostgreSQL is reachable privately from the VM, while Cloudflare R2 provides S3-compatible storage for media, backups, and the downloadable Android build.

  6. 06

    Cloudflare edge to the Docker service

    Cloudflare terminates TLS and carries requests through a Tunnel to loopback-only Nginx, which routes to the long-running .NET container without public inbound VM ports.

Results

What shipped

  • Shipped a public bilingual marketing website, functional web product, and downloadable Android preview rather than stopping at a local prototype.
  • Reframed the product from sports prediction and rating into a clearer AI planning, daily progress, Journey, and companion-growth loop.
  • Proved a repeatable production workflow across tests, container images, infrastructure as code, automated deployment, health checks, and stable public domains.
Contact

Discuss this work

I can go deeper on the trade-offs, implementation boundaries, operational decisions, and engineering judgement behind this work.

Email Jerome