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

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.
Full-stack evidence
A visible path from client experience to production operations.
- 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.
- 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.
- 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.
- 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.
- 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.
- 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.
System flow
The boundaries that kept product work, data, and external side effects understandable.
- 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.
- 02
Versioned REST contract
A generated OpenAPI client carries authenticated requests through stable /api/v1 endpoints and turns typed errors into recoverable UI states.
- 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.
- 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.
- 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.
- 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.
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.
Discuss this work
I can go deeper on the trade-offs, implementation boundaries, operational decisions, and engineering judgement behind this work.