- Google OAuth2/PKCE flow via openid-client v6; session cookie (30-day) - Next.js middleware auth guard — redirects before any client render - Todoist OAuth2 connect/disconnect; REST v1 task fetch (today|overdue) - RandomPolicy recommender behind stable POST /recommend contract - Feedback endpoint (done/dismiss/snooze); marks task complete in Todoist - 30s in-memory task cache per user (~1ms recommend on cache hit) - Tip page: pure opacity fade-in (3.5s), fast fade-out (0.3s), no motion - "reading you…" loading text with breathe animation - PWA icons + manifest - Ports pinned: API=3078, web=3079; Caddy at o.alogins.net Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
oO
One tip. Right now. Feels like magic.
oO learns who you are from the apps you already use and surfaces one perfectly-timed suggestion — an advice or a todo — on a black page. No feed. No dashboard. One tip.
Why
Everyone has too many tasks, too many apps, too much noise. What people actually need is a single, well-chosen nudge at the right moment. oO is that nudge, powered by a recommendation engine that gets smarter the more of your life it sees.
Product principles
- One thing at a time. The UI is a black page with one tip. That's the product.
- We don't own your data, we understand it. Connect your apps; we read what we need, when we need it.
- Magic requires craft. Precision, timing, and restraint matter more than features.
- Private by default. Tokens are encrypted, models are per-user, deletion is one click.
Prototype scope (Phase 0)
Three pages. That's it.
| Page | What it does |
|---|---|
| Sign in | Google / Apple OAuth. No passwords. |
| Connect | A list of integrations. Tap "Todoist" → OAuth flow → token stored. |
| Tip | Black page. One tip. Tap to dismiss / done / snooze. |
Under the hood the "pick a tip" call already routes through a recommender service with a pluggable policy — so v0 is literally "random Todoist task" but every other version slots into the same contract.
Architecture at a glance
┌──────────┐ OAuth ┌────────────┐
│ Web / │──────────▶│ auth │
│ Mobile │ └─────┬──────┘
│ client │ │ JWT
│ │ REST/GraphQL ▼
│ │────────▶┌───────────────┐
└──────────┘ │ gateway │──┬──▶ profile
└───────┬───────┘ ├──▶ integrations ──▶ Todoist / Google / ...
│ └──▶ recommender ──▶ ml/serving (Python)
▼
┌───────────────┐
│ events │ ◀── integrations emit normalized events
│ (Kafka/NATS) │ ──▶ ml/pipelines (features, training)
└───────────────┘
More detail in docs/architecture/ and decisions in docs/adr/.
Monorepo layout
See CLAUDE.md for the full tree and conventions.
apps/ web, ios, android
services/ gateway, auth, profile, integrations, recommender, events, notifier
packages/ shared-types, sdk-js, ui
ml/ pipelines, features, registry, experiments, serving
infra/ docker, k8s, terraform, ci
docs/ architecture, adr, api
Roadmap
Phase 0 — Walking skeleton (M0)
Goal: a single user signs in with Google, connects Todoist, and sees one random Todoist task on a black page. Deletion works.
- Monorepo scaffold, CI skeleton, docker-compose dev env with
core/fullprofiles authon Auth.js with Google provider; OIDC-shaped boundary (ADR-0004)integrations/todoistOAuth2 flow + encrypted token vault + provider-side revocationrecommenderwithRandomPolicy; stablePOST /recommendcontractapps/web— three pages (sign-in, connect, tip); PWA manifest; offline reaction queue- ToS + Privacy Policy + consent capture on first sign-in
- Account-deletion endpoint: revokes providers, purges credentials, soft-deletes profile
- Metrics baseline: activation, first-tip reaction rate, dwell, retention (see
docs/architecture/metrics.md) - Deploy modular monolith +
ml/servingstub to a single VM via docker-compose + Caddy
Phase 1 — Real signal + in-the-moment delivery (M1)
Goal: tips are picked, not drawn from a hat — and they arrive at the right moment on the web.
- Event bus (NATS JetStream) with protobuf schemas (ADR-0005) + schema-registry CI gate
- Todoist event-driven sync (emit
signals.task.*) - Feature store skeleton + first five features (hour-of-day, overdue count, task age, priority, project)
ml/servingFastAPI scorer;RemotePolicywrapper in recommender- Global-then-personalize bandit: pooled LinUCB over shared features, per-user residual when data allows
- Shadow-deploy infra: every new policy logs what it would have picked; promotion requires reward-parity
- Feedback loop: reactions → rewards; delayed rewards for tasks completed in Todoist directly
- Web Push notifications (VAPID) so the "magic" shows up without opening the app
notifier(lite): web-push delivery, quiet-hours honoured, dedupe- Apple OAuth added (deferred from M0)
Phase 2 — Multi-source profile & trust (M2)
Goal: oO knows more than tasks, and users can see/control what we know.
- Integrations: Google Calendar, Apple Health (web import), generic webhook ingress
- Unified
Profilemodel (identity, preferences, contexts, consents) - Timing signals (Page Visibility, Idle Detection, coarse location) — opt-in, transparent
- Advice library + mixing policy (todo vs advice vs ambient)
- User-facing data dashboard: what's stored, what's computed, export, delete-by-category
- Cost/usage observability
Phase 3 — Native mobile (M3)
- iOS app (SwiftUI) with APNs push
- Android app (Compose) with FCM push
notifiergains APNs + FCM channels, per-device rate limits- Migrate auth from Auth.js to dedicated OIDC provider (trigger from ADR-0004)
- Decide-and-deliver scheduler: per-user "is this tip worth interrupting now?" threshold
Phase 4 — MLOps at scale (M4)
- Prefect/Airflow for batch feature materialization + retraining
- MLflow registry; shadow → A/B → launch pipeline as first-class
- Online experiments framework: deterministic assignment + bandit policies alongside fixed-split A/B
- Cross-user collaborative features (opt-in only); cohort slicing; fairness checks
- Drift monitoring (feature drift, prediction drift, reward drift); model cards per version
Phase 5 — Production hardening (M5)
- Audit logging, rotation of provider tokens + internal signing keys
- k3s on existing VM, then k8s + HPA once multi-node justified (no cliff)
- Multi-region failover, Postgres PITR, event-bus mirroring
- Public integration SDK; sandbox tenancy for third-party connectors
- Billing + subscription tiers
Contributing
This repo is split into independent modules; most tickets belong to exactly one. Pick an issue, check its milestone (= phase), read the service's README.md, ship.
Conventions and per-service guidance live in CLAUDE.md.
License
All rights reserved — 2026. Contact the owner for licensing inquiries. (We'll switch to an OSS license for non-sensitive packages once the public SDK lands in Phase 5.)