A mobile-first daily music ritual. Everyone on 365 discovers the same single song each day, chosen from songs submitted by the community. Not a Spotify replacement, social network, or recommendation engine.
flowchart LR
subgraph Mobile ["Expo (React Native + M3)"]
TodayScreen["Today — DAY N + Reveal"]
SubmitScreen["Submit — Catalog Search"]
HistoryScreen["History — 365 Archive"]
SettingsScreen["Settings & Policies"]
end
subgraph Core ["@365/core"]
Types["Shared Domain Types"]
MusicProvider["MusicProvider Interface"]
SpotifyAdapter["Spotify API Adapter"]
end
subgraph Backend ["Supabase (PostgreSQL)"]
DB["Tables + RLS Policies"]
RPC["RPC Functions"]
PgCron["pg_cron — 7 PM IST Release"]
end
subgraph CI ["GitHub Actions"]
CronJob["Daily Cron Failsafe"]
CLI["triggerRelease.ts"]
end
subgraph Admin ["Admin Dashboard (Next.js)"]
Desk["Curation & Moderation"]
end
Mobile --> Core
Admin --> Core
Mobile --> Backend
Admin --> Backend
CI --> Backend
- Hero Artwork Dominance — Large album art with generous corner radius, elevated tonal shadow.
- Dynamic Palette —
M3ColorSchemederives its tonal palette from the album artwork's dominant color. - Editorial Typography —
Frauncesserif for display headings,Interfor body. - Zero AI Slop — No purple-blue gradients, glowing orbs, glassmorphism, card clutter, or emoji icons.
365/
├── packages/
│ └── core/ # Shared domain types, MusicProvider, dedup rules
├── apps/
│ ├── mobile/ # Expo React Native app (Expo Router + Reanimated)
│ │ └── android/ # Native Android build (Gradle, signing)
│ ├── admin/ # Next.js web dashboard for curation & moderation
│ └── backend/ # Release trigger script & package scripts
│ └── supabase/ # Schema SQL (mirror)
├── supabase/
│ └── schema.sql # Full Supabase schema — tables, RLS, RPC, seed data, pg_cron
├── .github/
│ └── workflows/
│ └── daily-release.yml # GitHub Actions daily cron failsafe
├── .env.example # Environment variable template
└── package.json # Monorepo root (npm workspaces)
| Layer | Service | Cost |
|---|---|---|
| Database | Supabase Free Tier (PostgreSQL + RLS + RPC) | $0 |
| Daily Release | pg_cron inside Supabase + GitHub Actions failsafe |
$0 |
| Auth | Supabase Auth (Anonymous + Google) | $0 |
| Music Catalog | Spotify Web API (Client Credentials) | $0 |
| CI/CD | GitHub Actions (2,000 min/mo free) | $0 |
| Hosting | N/A — mobile-only, no web hosting needed | $0 |
Total: $0/month. No credit card required.
- Node.js ≥ 20 · npm ≥ 10
- A free Supabase project
git clone https://github.com/ReturnKartikey/365.git
cd 365
npm install- Create a project at supabase.com/dashboard.
- Open the SQL Editor and paste the entire contents of
supabase/schema.sql. - Click Run — you should see "Success. No rows returned."
This creates all tables, RLS policies, RPC functions, seed data, and the pg_cron scheduled job.
cp .env.example .envFill in your Supabase credentials:
SUPABASE_URL=https://<your-project-ref>.supabase.co
SUPABASE_ANON_KEY=<your-anon-key>
SUPABASE_SERVICE_ROLE_KEY=<your-service-role-key>Also copy to apps/mobile/.env for the mobile app.
For live catalog search, add your Spotify credentials to .env:
SPOTIFY_CLIENT_ID=<your-client-id>
SPOTIFY_CLIENT_SECRET=<your-client-secret>Without Spotify credentials,
@365/core's built-in fallback catalog activates with curated offline tracks.
npm run start --workspace=@365/mobile- Press
a→ Android device / emulator - Press
w→ Web browser - Press
i→ iOS Simulator
npm run dev --workspace=@365/admincd apps/mobile/android
./gradlew.bat assembleReleaseOutput: apps/mobile/android/app/build/outputs/apk/release/app-release.apk
The core ritual: every day at 7:00 PM IST, one song is published for the entire community.
-
pg_cron(Primary) — A PostgreSQL cron job inside Supabase callsexecute_daily_release()at 13:30 UTC daily. Zero infrastructure needed. -
GitHub Actions (Failsafe) —
.github/workflows/daily-release.ymlruns at 13:35 UTC as a backup. RequiresSUPABASE_URLandSUPABASE_SERVICE_ROLE_KEYas repository secrets. -
CLI (Manual) — For testing or emergency releases:
npm run release --workspace=@365/backend
The execute_daily_release() RPC function:
- Checks if today's song is already published (idempotency guard).
- If a song is pre-scheduled for today → publishes it.
- If not → picks the oldest submission from the queue.
- If the queue is empty → falls back to the
editorial_fallback_pool. - Updates
daily_songs, marks the submission asselected, and increments the submitter's selection count.
Six tables with Row-Level Security:
| Table | Purpose |
|---|---|
users |
User profiles, submission/selection counts, ban status |
daily_songs |
The 365 archive — one row per day |
submissions |
Community song queue |
config |
App-wide settings (current day number, release time) |
reports |
User-submitted reports on songs/users |
editorial_fallback_pool |
Curated backup songs when the queue is empty |
Three RPC functions:
submit_song— Validates, deduplicates, and inserts a submission. Enforces one active submission per user.execute_daily_release— The daily release logic with full idempotency.report_item— Handles user reports with rate limiting.
- One active submission per user — enforced at database level.
- Song deduplication — prevents the same song from being queued twice.
- Guest gate — anonymous users can listen but must sign in to submit.
- Idempotent — calling
execute_daily_release()multiple times on the same day is safe. - Graceful fallback chain — pre-scheduled → oldest queue entry → editorial fallback pool.
- No dead days — the system always has a song to publish.
npm run build # Build all workspaces
npm run typecheck # TypeScript checks across all workspaces
npm run release --workspace=@365/backend # Trigger daily release manually
npm run start --workspace=@365/mobile # Start Expo dev server
npm run dev --workspace=@365/admin # Start admin dashboardMIT. Built for the daily ritual of discovery.