Music publishing rights intelligence platform for A&R and sync licensing teams, live at publisting.net. Search songwriter credits, publishing splits, PRO affiliations (ASCAP / BMI / SESAC), streaming performance, and catalog valuations across Spotify, Apple Music, Tidal, MusicBrainz, and a dozen other sources.
- Frontend: React 18 + TypeScript (strict) + Vite + shadcn/ui — hosted on Vercel
(SPA rewrites, caching, and security headers in
vercel.json) - Backend: Supabase (PostgreSQL + Row-Level Security + ~140 Edge Functions)
- Testing: Vitest + Testing Library
- Domain: publisting.net
npm install
cp .env.example .env # fill in your Supabase project values
npm run dev # dev server on :8080| Command | What it does |
|---|---|
npm run build |
Stage-string check, then production build to dist/ |
npm run lint |
ESLint + stage-string check |
npm run typecheck |
tsc -b (strict mode) |
npm test |
Vitest suite (one-shot); npm run test:watch for watch mode |
npm run preview |
Serve the production build locally |
Only public values live in .env — they are embedded in the browser bundle:
VITE_SUPABASE_URL=https://<project-ref>.supabase.co
VITE_SUPABASE_PUBLISHABLE_KEY=<anon-key>
In production both are set in Vercel (Project → Settings → Environment Variables);
.env is not committed. Server-side secrets never go in .env — see below.
The crawl4ai/ directory runs a self-hosted Crawl4AI
server that the crawl4ai-crawler Supabase edge function uses to crawl DSP song
credits, PRO/rights-org affiliations and splits (BMI, ASCAP, The MLC, HFA,
SoundExchange), streaming/playlist stats, chart placements, radio airplay, and
official social links. See crawl4ai/README.md for setup.
src/
pages/ Route components (lazy-loaded; admin routes gated by AdminGate)
components/ Feature components + shadcn/ui primitives in components/ui
hooks/ Data hooks (auth, teams, watchlists, lookups, …)
lib/ Domain logic: parsers, normalizers, scoring, api/ fetch wrappers
contexts/ App-wide providers (team, system status, catalog import)
integrations/ Supabase client + generated DB types
test/ Vitest suites for critical business logic
supabase/
functions/ Deno edge functions (one directory per lookup/integration)
migrations/ SQL migrations (source of truth for schema + RLS)
- Frontend: Push to
main→ Vercel auto-deploys to publisting.net - Edge Functions:
supabase functions deploy --all --project-ref <ref> - Migrations:
supabase db push --project-ref <ref> - Auth: In the Supabase dashboard set Site URL to
https://publisting.net, allow-listhttps://publisting.net/**, and enable the Google/Apple OAuth providers.
| Secret | Purpose |
|---|---|
GOOGLE_AI_API_KEY |
Google AI Studio (Gemini 2.5 Flash, text-embedding-004) |
SPOTIFY_CLIENT_ID / SPOTIFY_CLIENT_SECRET |
Spotify API |
DISCOGS_TOKEN |
Discogs API |
GENIUS_TOKEN |
Genius API |
FIRECRAWL_API_KEY |
Web scraping (optional — pro-lookup falls back to the AI lookup) |
CRAWL4AI_URL |
Self-hosted crawl4ai server URL — powers the crawl4ai-crawler pipeline (BMI, ASCAP, The MLC, HFA, SoundExchange, DSP credits, charts, radio, social) |
CRAWL4AI_API_TOKEN |
Bearer token for the crawl4ai server (only if JWT auth is enabled) |
CRAWL4AI_LLM_PROVIDER |
LiteLLM provider for extraction (optional; default openai/gpt-4o-mini) |
CRAWL4AI_PROXY_URL |
Route the crawler's headless browser through a residential/rotating proxy, e.g. http://user:pass@gw.dataimpulse.com:823 (optional; or set CRAWL4AI_PROXY_SERVER/_USERNAME/_PASSWORD separately) |
CRAWL4AI_SOCIAL_PROXY_URL |
Dedicated proxy for social crawls (Instagram/TikTok/etc.), which are IP-sensitive and often need a separate residential pool. Falls back to CRAWL4AI_PROXY_URL when unset (or set CRAWL4AI_SOCIAL_PROXY_SERVER/_USERNAME/_PASSWORD). |
CHARTMETRIC_REFRESH_TOKEN |
Chartmetric data |
HUNTER_API_KEY |
Contact discovery |
YOUTUBE_API_KEY |
YouTube data |
SOUNDCHARTS_API_KEY |
Soundcharts |
ALLOWED_ORIGIN |
CORS origin (https://publisting.net) — also locks the internal lookup/data functions' CORS to the app origin |
INTERNAL_FUNCTION_SECRET |
Optional. When set, gates the internal-only credit/enrichment functions (spotify-credits-lookup, genius-lookup, apple-credits-lookup, hunter-lookup) so only other edge functions (which send the matching x-internal-secret header) can call them. Unset = gate disabled (safe default). Set any long random string to lock these paid endpoints down. |
BUDGET_<PROVIDER> |
Optional per-provider daily call ceiling override (e.g. BUDGET_HUNTER=1000, BUDGET_GEMINI=5000). Defaults are built in; set to tune the global daily spend cap without a redeploy. |
- Load the app — landing page renders, no console errors.
- Sign up / sign in (email + Google OAuth), confirm the session persists on refresh.
- Run a song lookup from the home page; credits and sources populate.
- Add an artist to a watchlist; check it appears on
/watchlist. - Export credits to CSV/XLSX/PDF from a lookup result (exercises the lazy-loaded export chunks).
- As an admin user, open
/admin/healthfor provider status.