← work / signaldesk

liveSolo: portal, backend and the operating procedures it supports

SignalDesk

The internal portal the platform is actually run from

// the problem

Operating Plus234Feed by hand does not scale: content, newsletters, publishing, API customers and pipeline health each needed a surface, and a person on duty needs them in one place. SignalDesk is that control plane.

// context

Built for the person on duty rather than for readers. It sits on three backends (its own API, the newsletter service and the public content API), all over the one Supabase database, so it adds operator surfaces without duplicating the platform.

// what I built

  1. A portal shell where each feature is a self-contained module with its own routes, API layer and lazy-loaded pages: Content, Newsletter, Intel, Analytics, Operations and Publish, registered in one place so a new module is a registry entry rather than a rewrite.
  2. Its own FastAPI backend, separate from the public API: 85 endpoints across 13 route modules, JWT auth through Supabase, and admin, manager and viewer roles on privileged routes.
  3. Newsletter operations with an approval gate (prepare, approve, then a cron sends), plus dry-run and test-send, subscriber counts by acquisition source, per-recipient delivery results, and GA4 UTM attribution showing which campaign drove sessions.
  4. An Operations view over automation health: success rate, average duration and last status per job, normalised across cron runs and scrapers, with a daily platform-health record and its trend.
  5. Health that refuses to look fine: a stale health run is reported as stale rather than rendered as healthy, because the failures worth catching all reported success at the time: scrapers returning 200 while inserting nothing, pageviews collapsing after a deploy, push going out at 0% CTR.
  6. An Intel module for the data API: usage, customers and API-key management.
  7. Service integrations behind the pages: AI-written drafts and captions, IndexNow submission, UTM tracking, X publishing, URL shortening and cache purging.

// key decisions

  1. Module registry with lazy-loaded routes

    Each module declares its base path, routes and backend in one registry file, and every page is lazy-loaded. Adding a module is a folder and a registry entry rather than a change to shell routing. The cost is one central file that has to stay current.

  2. The cron sends only what a person approved

    An issue is prepared from live data and approved in the portal; the Friday cron sends only approved items and expires unapproved drafts. A missed approval skips an issue rather than sending an unreviewed one. The send page runs a dry-run on load, and a real send needs a typed confirmation.

  3. A stale health record is shown as stale

    The health endpoint only reads what the 3-hourly monitoring job recorded and never re-runs checks. After 7 hours without a run, or on a query error, it returns stale rather than the last green result, because a stale green panel is worse than no panel.

  4. Reuse existing run tables for Operations

    The Operations view merges the existing cron_runs and scrape_runs tables into one normalised list instead of adding new logging infrastructure. It can only report what those jobs already record, which keeps it cheap to maintain.

// results

  1. 106 API endpoints across 16 route modules today.
  2. 6 lazy-loaded modules with 26 routes.
  3. 119 commits between January and October 2026.

// screens

Operations: every cron and scraper in one view, refreshed each minute. A run that succeeds while inserting nothing is visible here rather than buried.
Content: article operations with placement actions (hero, featured, breaking, newsletter), and the six modules the portal is assembled from down the left.
Newsletter: the issue built from live data and previewed exactly as it will land, before anyone approves it for sending.