Skip to content

Latest commit

 

History

1,091 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Orbit API

The backend for Orbit -- an AI-powered habit tracker. Provides a REST API for habit management, an AI agent (chat + MCP tools), gamification, goals, calendar sync, social/accountability, push notifications, and subscription billing. Built with Clean Architecture and CQRS.

Live: api.useorbit.org | App: app.useorbit.org | Landing: useorbit.org

Tech Stack

Layer Technology
Runtime .NET 10.0, C# 13
Database PostgreSQL via EF Core 10 (Npgsql)
CQRS MediatR 14
Validation FluentValidation 12
Auth JWT Bearer + email verification codes + Google OAuth (Supabase tokens)
AI OpenAI — gpt-4.1-mini (primary), gpt-5.4-nano (sub-tasks), via the OpenAI .NET SDK
Email Resend (verification codes, contacts)
Push Firebase Admin SDK (FCM, native Android) + WebPush (VAPID, browsers)
Payments Stripe (web) + Google Play Billing (native Android)
Storage Supabase object storage (uploads bucket)
Password/Token Hashing BCrypt
Observability Sentry
API Docs Scalar (dev only)
Testing xUnit + FluentAssertions
Containerization Docker (multi-stage build)

Features

  • Habit CRUD -- Create, update, delete, duplicate, reorder habits with sub-habit support
  • Smart Scheduling -- Server-side frequency calculations (daily, weekly, monthly, yearly, every N days) with day-of-week filtering and overdue detection
  • Completion Logging -- Toggle completion per day with optional notes, full log history
  • Metrics -- Current/longest streaks, weekly/monthly completion rates
  • AI Agent -- Multi-turn OpenAI-backed chat that creates habits, logs completions, analyzes progress, suggests breakdowns, assigns tags, and reschedules. Exposed to external clients as MCP tools with per-tool ownership/policy scoping and audit logging. Supports image upload
  • AI Assists -- Cached daily summaries, retrospectives, fact extraction, goal review, habit/tag/reschedule suggestions, proactive check-ins, slip alerts, and batched usage summaries
  • Gamification -- XP, levels, streaks, streak freezes, and achievements
  • Goals -- Goal tracking with deadlines and AI-assisted review
  • Calendar -- Google Calendar auto-sync of scheduled habits
  • Social & Accountability -- Friends, public profiles, accountability partners, challenges, and referrals
  • Checklist Templates -- Reusable habit/checklist templates
  • Tags -- Colored tags for habit organization
  • User Facts -- Personal context facts that enhance AI responses (soft-deleted)
  • Push Notifications -- Dual delivery: FCM for native Android, VAPID Web Push for browsers, plus background schedulers for reminders, goal deadlines, slip alerts, and check-ins
  • Email Verification -- Passwordless code-based login via Resend
  • Subscription Billing -- Stripe (web) and Google Play Billing (native) with webhook / RTDN processing; backend is the source of truth for entitlements
  • Waitlist -- Signed-token waitlist confirmation flow
  • Sync -- Batched pull/mutation sync endpoints for clients
  • Timezone Support -- All user-facing dates use timezone-aware "today" based on user profile
  • Pagination -- List endpoints paginated with PaginatedResponse<T>

Prerequisites

Getting Started

# Clone the repository
git clone https://github.com/thomasluizon/orbit-api.git
cd orbit-api

# Copy environment config
cp .env.example .env
# Edit .env / appsettings.Development.json with your database credentials and API keys

# Apply database migrations
dotnet ef database update --project src/Orbit.Infrastructure --startup-project src/Orbit.Api

# Run the API
dotnet run --project src/Orbit.Api

The API runs at http://localhost:5000. API docs available at /scalar in development.

Configuration

Settings are bound from appsettings.json, with secrets overridden in appsettings.Development.json locally and environment variables in production. Key sections:

Section Purpose
ConnectionStrings:DefaultConnection / SessionConnection PostgreSQL (direct + session pooler)
AI:ApiKey / AI:Model / AI:SubTaskModel / AI:BaseUrl OpenAI credentials and models (BaseUrl defaults to https://api.openai.com/v1)
Jwt:SecretKey / Issuer / Audience JWT signing and claims
Supabase:Url / AnonKey / SecretKey / Bucket Supabase auth tokens + object storage
Resend:ApiKey / FromEmail Transactional email
Vapid:PublicKey / PrivateKey / Subject Web Push (VAPID)
Encryption:Key At-rest field encryption
Google:ClientId / ClientSecret Google OAuth + Calendar
Stripe:SecretKey / WebhookSecret / price IDs Stripe billing
Cors:AllowedOrigins / LandingOrigins Allowed frontend origins
Sentry:Dsn / Environment Error monitoring

Architecture

Clean Architecture with four layers:

src/
  Orbit.Api/              # Presentation layer
    Controllers/          #   26 controllers (see below)
    Extensions/           #   PayGate-aware IActionResult helpers
    Middleware/           #   Security headers, unhandled/validation exception handlers
    Program.cs            #   DI configuration, middleware pipeline

  Orbit.Application/      # Application layer (CQRS)
    Common/               #   PaginatedResponse<T>, ErrorMessages, AppConstants, PayGateService, ...
    Habits/               #   Commands / Queries / Validators / Services (HabitScheduleService)
    Chat/                 #   AI chat orchestration
    Goals/ Gamification/  #   Goals, XP/streaks/achievements
    Calendar/             #   Google Calendar sync
    Accountability/ Social/ Challenges/ Referrals/   # Social graph
    ChecklistTemplates/ Tags/ UserFacts/ Uploads/    # Content
    Auth/ Profile/ ApiKeys/ Subscriptions/ Support/ Notifications/ Waitlist/
    Behaviors/            #   MediatR pipeline (ValidationBehavior, ...)

  Orbit.Domain/           # Domain layer (zero dependencies)
    Entities/ Enums/ Interfaces/ Common/ Models/     # Entities, Result<T>, repository/service contracts

  Orbit.Infrastructure/   # Infrastructure layer
    Persistence/          #   OrbitDbContext, GenericRepository<T>, UnitOfWork
    Services/             #   AI (AiIntentService, AiSummaryService, OpenAiBatchPollerService, ...),
                          #   Agent/MCP (AgentCatalogService, AgentOperationExecutor, AgentPolicyEvaluator, ...),
                          #   billing (StripeBillingService, GooglePlayBillingService),
                          #   push + schedulers (PushNotificationService, ReminderSchedulerService, ...),
                          #   auth (JwtTokenService, GoogleTokenService), UserDateService, EncryptionService
    Migrations/           #   EF Core migrations

tests/
  Orbit.Domain.Tests/         # xUnit + FluentAssertions
  Orbit.Application.Tests/    # xUnit + FluentAssertions
  Orbit.Infrastructure.Tests/ # xUnit + FluentAssertions

Controllers

Accountability, Achievements, Ai, ApiKeys, Auth, Calendar, Challenges, Chat, ChecklistTemplates, Config, Friends, Gamification, Goals, Habits, Notification, OAuth, Profile, PublicProfile, Referral, Subscription, Support, Sync, Tags, Uploads, UserFacts, Waitlist.

Key Patterns

  • Result<T> -- Handlers return Result<T> instead of throwing for expected failures
  • CQRS -- Command/query separation via MediatR, one folder per feature
  • Factory methods -- Entities created via Entity.Create() static methods
  • Generic repository + Unit of Work -- Abstracted data access
  • PayGate -- Subscription-gated features via PayGateService and Result propagation helpers
  • Validation pipeline -- ValidationBehavior<TRequest, TResponse> in the MediatR pipeline
  • Cache invalidation -- AI summary cache cleared on any habit mutation

Push Notifications

Dual delivery via PushNotificationService:

  • FCM -- Firebase Admin SDK for native Android. Subscriptions with p256dh == "fcm" route through Firebase
  • Web Push -- VAPID-based for browsers, via Lib.Net.Http.WebPush
  • Schedulers -- Background services (ReminderSchedulerService, GoalDeadlineNotificationService, SlipAlertSchedulerService, ProactiveCheckinSchedulerService) send push + create in-app notifications; a SentReminder record prevents duplicates

Testing

# Run all unit tests
dotnet test

Tests use xUnit with FluentAssertions. Unit tests only — there is no integration or E2E suite.

Deployment

Component Service
Hosting Render (Docker, manual API releases)
Database Supabase PostgreSQL (session pooler)
Domain api.useorbit.org
Push Firebase project orbit-11d4a (FCM)
Email Resend
Payments Stripe + Google Play Billing
Monitoring Sentry

Run release.yml from main with an environment of production or staging and a branch to release. Production accepts only main; staging deploys the selected branch head. Both jobs use the Render API to wait for the requested deploy and verify the live commit. Staging also verifies the commit reported by the public health endpoint. Set RENDER_API_KEY as an environment secret in each of the production and staging GitHub environments, and set the repository variables RENDER_PRODUCTION_SERVICE_ID and RENDER_STAGING_SERVICE_ID. Restrict the production, staging, and render-operations GitHub environments to deployment branch main. The staging reseed and Postgres access reconciliation workflows use the render-operations environment secret RENDER_API_KEY for database and service management. Remove the repository secret RENDER_API_KEY after both release paths and the operational workflows succeed.

Turnstile remains disabled after deployment. Create a Turnstile widget for useorbit.org in the Cloudflare dashboard and set BotProtection__SecretKey in the Render orbit-api environment. Keep BotProtection__Enabled unset or false until all three conditions hold:

  1. The deployed web sign-in sends turnstileToken for both send-code and verify-code. Check production requests and the deployed web version. Check APP_VERSION in every deployed environment of the Orbit web project in Vercel before raising the version floor. An unset APP_VERSION fails open; a set value must be at least the proposed floor.
  2. The Android build sends turnstileToken for both calls and is live on the Google Play track. Check the Play Console release and active version distribution. Only then raise the production AppConfigs.MinSupportedVersion row to that build using a guarded update, and read the row back to verify the exact value. Allow up to 30 minutes for the per-process cache to apply the floor.
  3. The deployed landing waitlist sends turnstileToken in the /api/waitlist body. The source at orbit-landing-page/src/scripts/waitlist.ts already does this on main; check the deployed form request and confirm PUBLIC_TURNSTILE_SITE_KEY is set for the landing deployment.

Then set BotProtection__Enabled=true in the Render orbit-api environment. Verify token-bearing web and Android sign-in and the landing waitlist succeed, while tokenless requests to all five protected routes return 400. Monitor HTTP 426 traffic and client reports for 24 hours after the version-floor raise. Existing clients without a token continue to work while the switch is false. The production smoke account bypasses this gate only when both SMOKE_TEST_EMAIL and SMOKE_TEST_CODE are configured, so knowledge of that address permits sends only to Orbit's own mailbox.

Docker

# Build and run with Docker Compose
docker compose up -d --build

The Dockerfile uses a multi-stage build (SDK for build, ASP.NET runtime for production) and runs as a non-root user.

Related Repositories

Repo Description
orbit-ui-mobile Turborepo frontend — apps/web (Next.js 16) + apps/mobile (Expo, Android) + packages/shared
orbit-landing-page Marketing landing page

License

Private project.

About

.NET 10 REST API for Orbit, an AI-powered habit tracker. Clean Architecture + CQRS, PostgreSQL, Gemini AI chat, push notifications, Stripe billing.

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages