Self-host in 15 minutes

You will stand up your own YawningFace Block cloud: a Supabase database for storage, an Auth0 tenant for sign-in, and this repo deployed to Vercel. Everything fits in the free tiers. You need a GitHub account and a terminal with curl.

Heads-up: there is no dashboard UI in v1. The desktop app and the HTTP API are the interface - step 5 shows how to edit your config with curl.

1Create the Supabase project

Go to supabase.com and create a new project (any region, free tier is fine). Once it is ready:

  1. Open SQL Editor, paste the contents of supabase/migrations/0001_init.sql from this repo, and run it. It creates the profiles, devices, configs and events tables and enables RLS with no policies (only the server, using the service-role key, can touch the data).
  2. In Project Settings → API, copy the Project URL (this is SUPABASE_URL) and the service_role key (this is SUPABASE_SERVICE_ROLE_KEY). Treat the service-role key like a root password - it never leaves the server.

2Create the Auth0 tenant

Sign up at auth0.com (free tier) and create a tenant. You need two things: an API (so tokens carry the right audience) and a Native application (so the desktop app can sign in with a device code).

a) Create the API. In Applications → APIs → Create API:

  • Name: anything, e.g. YawningFace Block.
  • Identifier: a URI you control, e.g. https://block.example.com/api. This exact string is your AUTH0_AUDIENCE.
  • In the API's Settings, enable Allow Offline Access (clients need refresh tokens so you do not have to sign in every day).

b) Create the Native application. In Applications → Applications → Create Application, pick Native. Then in its Settings → Advanced Settings → Grant Types, make sure Device Code and Refresh Token are enabled, and save.

c) Note your values. From the tenant/application settings, write down the Domain (e.g. your-tenant.eu.auth0.com - this is AUTH0_DOMAIN, no https://) and the Native application's Client ID (the desktop app asks for it at sign-in).

3Deploy this repo to Vercel

Fork or push this repository to your GitHub account, then import it at vercel.com/new. Vercel detects Next.js automatically. Before deploying, set the environment variables (Project → Settings → Environment Variables):

AUTH0_DOMAIN=your-tenant.eu.auth0.com
AUTH0_AUDIENCE=https://block.example.com/api
SUPABASE_URL=https://YOUR-PROJECT.supabase.co
SUPABASE_SERVICE_ROLE_KEY=eyJ...   # service_role key from step 1
NEXT_PUBLIC_SITE_URL=https://your-deployment.vercel.app

Deploy, then verify the API is alive:

curl https://your-deployment.vercel.app/api/health
# → {"ok":true}

4Install the desktop app and sign in

Download the app for Mac or Windows from the latest release. On first launch, point it at your cloud: enter your deployment URL, your Auth0 domain, the Native app's Client ID, and the API audience from step 2.

The app then shows a short device codeand opens your browser to Auth0's activation page. Confirm the code, sign in (or sign up), and the app receives its tokens - this is the Device Authorization Flow, the same thing your TV apps use. From then on it syncs your config from GET /api/v1/config and reports events back automatically.

5Edit your config via the API

Your blocklists live in one JSON document (the full schema is in docs/schema.md). To edit it, you need an access token. The easiest way to get one is to copy it from the desktop app's settings; any token obtained from your Auth0 tenant with the right audience works.

Read your current config:

export TOKEN="eyJ..."   # your access token
export BASE="https://your-deployment.vercel.app"

curl -s "$BASE/api/v1/config" -H "Authorization: Bearer $TOKEN"

Update it - for example, block Twitter and LinkedIn on weekday mornings everywhere:

curl -s -X PUT "$BASE/api/v1/config" \
  -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
  "config": {
    "version": 1,
    "blocklists": [
      {
        "id": "morning-focus",
        "name": "Morning Focus",
        "metadata": {
          "enabled": true,
          "severity": "block",
          "devices": ["desktop", "mobile", "tablet"],
          "timeZone": "Europe/Madrid",
          "timePeriods": [
            { "startTime": "09:00", "endTime": "13:00", "schedule": ["mon","tue","wed","thu","fri"] }
          ]
        },
        "targets": { "websites": ["twitter.com", "linkedin.com"], "apps": ["Discord", "Steam"] },
        "exceptions": []
      }
    ]
  }
}'

Every signed-in device picks the new config up on its next sync. That's it - your attention now has a bouncer on every door.