Nümi architecture
This document describes the high-level architecture of the Nümi application: layout of packages, request flow, and main subsystems.
Tech stack
- Language: Go (primary backend)
- Frontend: Server-rendered HTML (
html/template), vanilla JS, CSS. Clerk for auth (JWT + browser). - Database: MongoDB (Atlas). Drivers:
go.mongodb.org/mongo-driver/v2. - Auth: Clerk (JWT verification via
clerk-sdk-go). Webhooks for user lifecycle (create/update/delete). - Email: SMTP via
gomail.v2; HTML templates ininternal/service/email/. - Config: Environment variables;
.envviagodotenv. Env-prefixed Clerk keys (e.g.DEV_*,PROD_*).
Top-level layout
main.go: Entry point. Loads config, connects MongoDB, builds repositories and services, registers HTTP routes, starts background notification loop.internal/: All application code.api/: JSON API handlers (journeys, contact, auth, test-emails). Used by the frontend. Settings API is wired viahandlers.SettingsHandlerforGET/POST/PUT /api/settings(user from context).handlers/: HTTP handlers for HTML pages and browser flows (dashboard, journey, superadmin, docs, settings).config/: Configuration structs and loading (Clerk, DB, SMTP, superadmin, notification defaults).db/: MongoDB connection and repositories (settings, journeys, email notifications).model/: Domain and DTO types (journeys, levels, settings, emails, quotes, etc.).service/: Business logic (email sending, notification scheduling, PDF report generation).middleware/: Auth (JWT, browser token), security headers, caching.logger/: Structured logging abstraction.
internal/templates/: Go HTML templates (dashboard, journey, index, privacy, tos, contact, superadmin, docs, offline).static/: Static assets (JS, CSS, data files, PWA manifest/service worker).
DNR-style flow (Data – Network – Render)
- Data:
modeltypes anddbrepositories. No HTTP or templates here. - Network:
apiandhandlersdeal with HTTP (JSON or HTML). They call services and repositories. - Render: Server-side rendering via
html/template; client-side updates via fetch to/api/*.
So: data layer (db + model) → network layer (api + handlers) → render (templates + static JS). Business rules live in service and are used by api/handlers.
Request flow
HTML pages (browser)
- Request hits a route in
main.go(e.g./,/dashboard,/journey/,/superadmin). - Middleware may run (e.g.
SecurityHeadersMiddleware;BrowserAuthMiddlewarefor superadmin). - Handler in
main.goorhandlers/loads data (config, quote, etc.), parses a template, executes it, writes HTML. - Some handlers do not call repositories (e.g. dashboard only passes config/quote); others do (e.g. superadmin).
JSON API (authenticated)
- Request to
/api/*(e.g./api/journeys,/api/settings). AuthMiddlewareverifies Clerk Bearer token and setsuserID(and optionallyuserEmail) in context.- Handler in
api/orhandlers/reads context, path, and body; calls repositories and/or services; returns JSON. - Settings: The wired route is
GET/POST/PUT /api/settingshandled byhandlers.SettingsHandler, which uses the authenticated user ID from context (no path segment).
Webhooks
- Clerk:
POST /api/webhooks/clerk. Signature verification, then create/update/delete user and settings (and optional welcome email). Useshandlers.ClerkWebhookHandlerand repos.
Dependency flow (from main.go)
- Config → from env, validated at startup.
- DB →
db.InitMongoDB()sets package-levelDatabase(and collections); repositories are constructed withdb.Database. - Repositories →
SettingsRepository,JourneyRepository,EmailNotificationRepository. - Services →
EmailService(reads env for SMTP);NotificationService(repos + email service + config for checkpoint time/rate/spam control). - Handlers → API handler gets repos + email service + config; Settings handler gets settings repo; Superadmin gets settings + journey repos + config; Webhook handler gets settings repo + email service + email notification repo.
- Background →
NotificationService.Start(ctx)runs in a goroutine; ticker triggerscheckForMissingCheckins(journeys → settings → filters → email).
Key subsystems
Journeys and levels
- Model:
Journey(metadata,Levels[]),Level(dates, weights, mood, medication, side effects). Status enums for journey and level. - Persistence:
JourneyRepository(CRUD, GetUserJourneys, GetAllActiveJourneys, etc.). Collectionjourneys. - API: List/create at
/api/journeys; get/update/delete at/api/journeys/{id}; level update atPATCH /api/journeys/{id}/levels/{levelNumber}; email report atPOST /api/journeys/{id}/email-report; active journey and random title endpoints.
Settings and users
- Model:
Settings(Clerk user ID as_id, preferences, notification and check-in day, timezone). - Persistence:
SettingsRepository; collectionsettings. Created/updated by Clerk webhook and by settings API. - Auth: User identity from Clerk JWT; superadmin determined by config (user ID), not exposed to client.
Notifications and email
- NotificationService: Ticker-driven. Fetches active journeys, groups by user, loads settings, applies filters (day, time window, missing check-ins, spam control), then sends check-in reminder emails and records them.
- EmailService: Sends via SMTP; uses
internal/service/email/for HTML generation (welcome, badge, check-in reminder, first check-in, level completed, goal reached, contact, report). - EmailNotificationRepository: Records sent emails (user, type, time) for rate limiting and auditing; collection
notifications.
Security and middleware
- AuthMiddleware: Validates
Authorization: Bearer <token>with Clerk JWT; setsuserIDin context; returns 401 if invalid. - BrowserAuthMiddleware: Accepts token from header, query, or cookie; used for superadmin.
- SecurityHeadersMiddleware: Adds security-related headers (e.g. CSP). Applied to page routes.
- CacheControlMiddleware: Different cache policies for static assets vs API vs HTML.
Configuration (env)
- App:
ENV,LOG_LEVEL, version. - MongoDB:
MONGODB_*(username, password, database). - Clerk:
CLERK_WEBHOOK_SIGNING_SECRET; env-prefixed*_CLERK_FRONTEND_KEY,*_CLERK_FRONTEND_URL,*_CLERK_SECRET_KEY. - SMTP:
SMTP_*,CONTACT_EMAIL_TO,SMTP_FROM_*. - Notifications:
DEFAULT_NOTIFICATION_CHECKPOINT_TIME,DEFAULT_NOTIFICATION_CHECKPOINT_RATE,DEFAULT_NOTIFICATION_SPAM_CONTROL. - Superadmin:
SUPERADMIN_USER_ID,SUPERADMIN_EMAIL.
Related documentation
- README.md: Setup, env, features, changelog.
- docs/codebase.md: Code review, logging/TODO/error-handling notes.
- docs/email-system.md, docs/checkin-reminder-service-analysis.md, docs/check-in-reminder-hypotheses.md: Email and reminder behavior.
- docs/notification-checkpoints-review.md: Where and when notifications are sent.
- docs/apis.md: API reference.
- docs/architecture.md: This document.