Skip to content

The Bridge

The Bridge gives the quick-capture inbox a way in from anywhere — a phone on a dog walk, another machine — instead of only a terminal on one box. It pulls captures from a self-hosted channel into inbox.db, where /triage routes them onward, with no daemon and no background process: the session-start hook drains whatever accumulated since it last looked, and stops.

The transport is pluggable. ntfy ships today; the channel interface is written so a new transport (MQTT, a Telegram bot) is a subclass plus a registry line, not a rewrite of the plumbing.

Off by default

The Bridge is disabled until you switch it on, deliberately: enabling it allows outbound network access, which isn't always permitted (a work machine under a corporate policy). While disabled it makes zero network calls — the drain short-circuits before any request. Turn it on only where that access is allowed, per machine.

Enable and configure it from the home hub — System ▸ ↗ Configure Bridge. That leaf is the only way into the editor; there is no mait-code bridge command. The form collects the channel's settings and offers a Test connection button that probes them before you save.

The gate and the channel choice are also plain settings, if you'd rather script them or edit settings.toml directly:

mait-code settings set bridge enabled
mait-code settings set bridge-type ntfy

The channel's server, topics and token stay in the Bridge screen — they aren't registry settings.

Setting up ntfy

The Bridge expects a private ntfy topic. ntfy self-hosts as a single container, so the home server is the natural place for it:

docker run -d --name ntfy -p 80:80 \
  binwiederhier/ntfy serve

Pick a private, unguessable capture topic (it's the address captures are published to and drained from — treat it like a secret), and, on a protected server, mint an access token. Then in the Bridge editor:

Field Value
Server URL Base URL of your ntfy server, e.g. https://ntfy.example.org
Capture topic The private topic captures are drained from, e.g. mait-capture-7f3a9c
Notify topic The topic your phone subscribes to for outbound reminders (leave blank for inbound-only)
Access token A bearer token for a protected topic (leave blank if open)

Set Status to enabled, Test connection to confirm the server is reachable and the token works, then Save.

A 403 here isn't always the token. Cloudflare-fronted servers reject requests carrying urllib's default user-agent, so the Bridge sends a real one — if you still see a 403, suspect a proxy or WAF in front of ntfy before you go regenerating credentials.

Capturing

Anything published to the capture topic becomes an inbox item on the next drain. Publish however suits the moment:

# from any machine
curl -d "ring the vet about Cody's booster" https://ntfy.example.org/mait-capture-7f3a9c

On a phone, the ntfy app, an Android HTTP Shortcut or an Apple Shortcut turns the share sheet into the same authenticated POST — capture from anywhere, triage at your desk.

Draining

Draining happens two ways, both idempotent (a per-machine watermark tracks what each machine has already seen):

  • Automatically, at session start — the hook drains before it builds the brief, so fresh captures show up in the inbox count straight away.
  • Manually, any time: mc-tool-inbox drain.

Captured items land in the inbox exactly as if typed there, and /triage routes them to the board or memory as usual.

Reminders on your phone

With a notify topic set, the Bridge closes the loop the other way too: when a reminder falls due, it's published to that topic and lands on your phone's lock screen. Each notification carries a "Done" button — tapping it posts a control message back to the capture topic, and the next drain dismisses the reminder for you. No terminal required, from either direction.

The control message is a plain string, mait-ctl:dismiss:<id>. Any inbound message starting mait-ctl: is intercepted as a command rather than filed as a capture — worth knowing, since captures can arrive from any device that can publish to the topic.

Publishing rides the same reactive triggers as draining — the session-start hook and mc-tool-reminders check — and each reminder is sent once (a notified_at stamp stops a still-overdue reminder from re-notifying every session). Subscribe your phone to the notify topic in the ntfy app; on a protected server the "Done" button carries the access token so the dismissal authenticates.

Health

mait-code doctor reports the Bridge: disabled when off (the safe default), and a warning — never a failure — when it's enabled but a required field is blank, so a half-configured Bridge degrades to a no-op rather than a broken session.

The channel config lives in bridge.json under the data dir; the drain watermark lives beside it in bridge-state.json and is never synced between machines.