Skip to the waitlist

A quiet inbox for everything you run.

CI jobs, cron, systemd units and your own services report to one HTTPS endpoint. Posts that share a key update a single card and keep its timeline, so a deploy is one card on your Mac and your iPhone, not five messages in a chat channel.

You’re on the list. Your signup just arrived in the demo as a card.

One email on the day it ships. Nothing else.

If it can run a shell command, it can post.

  • GitHub Actions
  • GitLab CI
  • Jenkins
  • Buildkite
  • CircleCI
  • Woodpecker
  • cron
  • systemd timers
  • launchd
  • Ansible
  • Salt
  • Terraform
  • Pulumi
  • Nomad
  • Kubernetes CronJobs
  • Docker healthchecks
  • Kamal
  • Fly.io deploys
  • Capistrano
  • Makefiles
  • npm scripts
  • mix tasks
  • rake tasks
  • restic
  • borg
  • rsync
  • pg_dump
  • certbot
  • ZFS scrubs
  • smartd
  • NUT / UPS
  • Home Assistant
  • Proxmox hooks
  • TrueNAS
  • Uptime Kuma
  • Node-RED
  • n8n
  • your own apps
All events 12 events
Today Monday 26 October

A working replica of the Mac app. Pick a topic, open an event.

One endpoint. No SDK.

If it can run curl, it can post. A title is the only required field. Add a key if the event will change, and a topic to file it under.

POST /api/events bash
curl -sS --max-time 5 https://farolim.dev/api/events \
  -H "Authorization: Bearer $FAROLIM_TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{
    "topic":  "acme/deploys",
    "key":    "deploy:web:4f2c1a9",
    "status": "running",
    "title":  "Deploying web 4f2c1a9"
  }' || true
201 Created {"id": "01K1…", "seq": 4471, "op": "created", "revision": 1}
Deploying web 4f2c1a9 acme/deploys

No spam. One key, one card.

A deploy reports when it starts, while it checks itself and when it finishes. Post every step under the same key and farolim revises one card and adds the step to its timeline, instead of piling up messages. A retry that changes nothing is counted, not repeated.

POST /api/events deploy:web:4f2c1a9
  1. Deploying web 4f2c1a9 { status: "running" }→ created
  2. Health check passed { status: "running" }→ updated
  3. Deployed web 4f2c1a9 { status: "success" }→ updated
  4. Deployed web 4f2c1a9 { status: "success" }→ deduplicated

Silent until it matters.

Each post says how loudly it should arrive. Routine updates revise the card without a banner and without moving it, so a deploy that checks itself every thirty seconds stays where you left it. And a job that promised to report back and went quiet says so on its own.

How it arrives notify
  • "notify": "silent" Revised in place. No banner, no reordering. quiet
  • "notify": "normal" Moves to the top and shows a notification. banner
  • "notify": "high" The same, with a sound. banner + sound
  • "expects_update_within": 600 A promise. Miss it and the card turns stale. stale

Post once. It lands everywhere.

The server stores the post once, and every app shows the same card. The Mac app hears it over a WebSocket. The iPhone is pushed by the server itself, not relayed through your Mac, so the banner arrives with the laptop shut. What a producer marked silent never buzzes the phone. A web app for any browser is in progress.

POST /api/events bash
curl -sS --max-time 5 \  https://farolim.dev/api/events \  -H "Authorization: Bearer $FAROLIM_TOKEN" \  -H 'Content-Type: application/json' \  -d '{    "topic":  "acme/deploys",    "status": "failure",    "notify": "high",    "title":  "Deploy failed web 4f2c1a9",    "body":   "exit 1 at migrate"  }' || true
ready sending… 201 Created {"op": "created", "revision": 1}
Mac app WebSocket
Web app WebSocket in progress
iPhone APNs

Built to stay out of the way.

  • Never blocks a deploy

    Every documented request carries a timeout and swallows its own failure, so a slow or unreachable server can’t fail the job that posted.

    --max-time 5 … || true
  • Rejections are kept

    An authenticated post that fails validation is stored with its raw body and shows up in the app, so a typo costs visibility, not data.

    422 invalid_topic_path
  • Separate read and write keys

    Producer tokens can post but can’t read. The Mac app’s token reads but can’t write. A build runner never sees your stream.

    src_live_… / cli_live_…
  • History is never rewritten

    A deploy that failed at 19:54 failed. A later success is a new fact, not a correction. Nothing is marked resolved on your behalf.

    status: failure

Bring the webhooks you already have.

Not everything that reports is a script you wrote. A service that sends its own JSON is mapped onto a card, field by field. A tool that only knows how to post to Discord, Slack, ntfy or Gotify posts here with nothing but a new URL.

  • Any webhook, mapped to a card.

    Point a service’s webhook at farolim, Shopify’s new orders for one, and choose which of its fields become the title, the status and the body. Nothing about the sender has to change.

    title ← "Order \(.name)"
  • Speaks Discord, Slack, ntfy and Gotify.

    Uptime Kuma, Grafana, Proxmox, Watchtower and most of what you self-host can already post to one of these. Paste a farolim URL where theirs was and leave the rest; the message arrives as a card. Mattermost targets work too, because they send Slack’s shape.

    discord · slack · ntfy · gotify

Keep a light on everything you run.

farolim is in development for macOS and iPhone. Leave an email and you’ll get one message, on release day.

You’re on the list. Your signup just arrived in the demo as a card.

One email on the day it ships. Nothing else.