--- title: OrgState API emoji: 📊 colorFrom: blue colorTo: indigo sdk: docker app_port: 8080 suggested_hardware: cpu-basic pinned: false --- # OrgState API on Hugging Face Spaces This Space runs the OrgState drift-detection API. Two deployment modes: * **Free production (recommended)** — point the Space at an external Postgres (Neon free tier or Supabase free tier). The DB persists through every Space restart. Use this for any deployment that has customers, even pilots. * **Demo-only** — leave the default `ORGSTATE_DB_PATH=/data/orgstate. sqlite3`. Data is wiped on every Space restart since HF's free CPU tier has no persistent storage. Use only for "click around the UI" demos with no real users. Both modes use the same Dockerfile + Space configuration. The choice is one env var. ## Endpoints - `GET /health` — liveness probe - `GET /health/ready` — readiness probe - `GET /metrics` — Prometheus - `GET /docs` — Swagger UI with paste-ready examples - `POST /tenants/{tid}/...` — see Swagger UI ## Free production setup (HF + Neon) 1. **Neon Postgres** — sign in to [neon.tech](https://neon.tech), create a new project ("orgstate"). Copy the connection string from the project dashboard. It looks like: ``` postgresql://neondb_owner:xxx@ep-yyy-pooler.region.aws.neon.tech/neondb?sslmode=require ``` Neon's free tier (0.5GB storage shared across projects, no card) is plenty for the first 10k tenant rows + 1M audit rows. Storage is shared across all projects in the account — if you're tight, switch to Supabase ([supabase.com](https://supabase.com)) which has a separate quota (500MB per account). 2. **HF Space variables** (Settings → Variables and secrets): * `ORGSTATE_DB_PATH` = the Neon connection string above * `ORGSTATE_ADMIN_KEY` = `openssl rand -hex 16` output * `ORGSTATE_CORS_ORIGINS` = your dashboard origin (e.g. `https://orgstate.1bigfam.com`) * `ORGSTATE_HSTS_ENABLED` = `false` initially (HF's TLS terminator is fine, but enable only AFTER you've verified your dashboard loads cleanly — HSTS pins for `max-age=1 year` by default and a misconfig will brick browsers for the duration) 3. **Push the repo to your Space**: ```bash git remote add hf https://huggingface.co/spaces//orgstate-api git push hf master ``` First build takes ~5 min (pip install heavy line is psycopg's binary wheel + cryptography). Subsequent rebuilds are fast. 4. **Verify** at `https://-orgstate-api.hf.space/docs`. You should see the Swagger UI listing every `/v1/...` and `/scim/v2/...` endpoint. 5. **Bootstrap the first tenant + API key**: ```bash # against the live URL, using the admin key you set above curl -X POST https://<...>.hf.space/v1/tenants \ -H "Authorization: Bearer $ORGSTATE_ADMIN_KEY" \ -H "Content-Type: application/json" \ -d '{"tenant_id":"acme","name":"ACME Inc"}' # mint a per-tenant API key (shown ONCE): curl -X POST https://<...>.hf.space/v1/tenants/acme/keys \ -H "Authorization: Bearer $ORGSTATE_ADMIN_KEY" \ -H "Content-Type: application/json" \ -d '{"name":"admin"}' ``` Paste the returned `raw` value into the dashboard's "Sign in" form. 6. **Drop the bootstrap admin key** from HF env once a DB-backed admin key exists (the platform falls back to DB-backed admins; the env key is only needed for the first POST `/tenants`). ## Demo-only setup (HF + SQLite ephemeral) Skip the Neon step. Set only `ORGSTATE_ADMIN_KEY` and (optionally) `ORGSTATE_CORS_ORIGINS`. The DB lives at `/data/orgstate.sqlite3` and gets wiped on every restart. Demo data must be re-seeded; see the `gtm/` directory for sample fixtures. ## Limits (both modes) - **HF auto-sleeps after 48h of no traffic.** The first request after sleep takes 30-60s to wake the container. Subsequent requests are normal. The dashboard handles this gracefully (loading state on 502) but cron jobs hitting the API will see occasional failures during cold start. - **No scheduler process.** HF Spaces runs a single container; the background ingestion scheduler isn't started. Manually trigger runs via the dashboard or `POST /tenants/{tid}/observations/run`. - **Rate limits apply.** Defaults are 600 rpm per credential, 60 rpm per IP (see Stage 79). Tighten via `ORGSTATE_RATE_LIMIT_PER_KEY`. - **No HSTS by default** — flip `ORGSTATE_HSTS_ENABLED=true` only AFTER the dashboard works end-to-end on HTTPS. HSTS pins for a year and a misconfig is hard to roll back. ## When to graduate off HF HF free is fine through your first 10-20 customers OR a few thousand SCIM users. The signals to graduate: * Cold-start latency hurts (real customers complain about the 30s wake delay). * You exceed Neon's free 0.5GB quota — bump to Neon's $19/mo tier OR migrate to Supabase pro / self-hosted Postgres. * You need a scheduler process — pick Fly (`deploy/fly.toml`) or Render (`deploy/render.yaml`) which support multi-process apps. ## Replacing this README In your Space repo root, copy this file as `README.md` and adjust the frontmatter (title/emoji/colors). The frontmatter drives Spaces' UI; the body is what users see when they visit the Space page.