storebound/docs/watch.md

3.7 KiB

storebound watch

Store events for every app on this machine, for people and agents. Read-only on the stores.

storebound watch                      # loop, every 15 minutes
storebound watch --once               # one pass (cron, PM2 restarts)
storebound watch --dir ./my-app       # one folder instead of the machine's app list
storebound watch --notify os,ntfy     # choose the sinks (default: every configured one)
storebound watch --dry --json         # print the events that would go out; nothing is sent or saved
storebound watch test                 # send one test event to every configured sink
storebound events --since 24h --kind rejected --json

Apps come from the machine's app folder list (storebound apps folders, the same list as the desktop app: folders and store-only apps) or from --dir. Each app is read with the credentials its commands use (the project's .storebound/, its profile, or the store-only app's account).

Each pass reads release state per store (like release status), crash / ANR health (like health, at most once an hour) and new reviews (like reviews list), compares with ~/.storebound/watch-state.json and emits events. The first pass of an app only records the baseline: no event storm. A store that cannot be read in a pass keeps its last state and produces no event.

Events

kind when
rejected a version went from in review to rejected (App Store; link to App Review)
approved a version passed review and waits for release (App Store pending developer release)
live a version became live, or its rollout started
rollout_halted a Play staged rollout was halted, or an App Store phased release paused
crash_threshold Play crash or ANR rate (28 d) crossed the bad behavior threshold; a new App Store metric or diagnostic rated poor
review_low a new review of 2 stars or less
review_needs_reply a new review of 3-5 stars without a reply

Each event is one JSON object: { at, app, store, kind, version, from, to, detail, url }, where url is the absolute console link for the app. Events are appended to ~/.storebound/events.jsonl (rotated at 5 MB, one .1 file kept). STOREBOUND_HOME moves ~/.storebound. storebound events and the MCP tool events_recent read the file.

Sinks

All optional. Configure with environment variables or ~/.storebound/watch.json; values are never printed.

sink variables
ntfy STOREBOUND_NTFY_TOPIC (required), STOREBOUND_NTFY_URL (default https://ntfy.sh), STOREBOUND_NTFY_TOKEN (optional). Title, Tags and Click headers; Priority high for rejected and rollout_halted
Telegram STOREBOUND_TELEGRAM_TOKEN and STOREBOUND_TELEGRAM_CHAT
webhook STOREBOUND_WEBHOOK_URL (JSON POST of the event)
OS notification --notify os, or "notify": ["os"] in watch.json (Windows toast, macOS osascript, Linux notify-send; skipped quietly when unavailable)
{ "notify": ["os"], "ntfy": { "topic": "..." }, "telegram": { "token": "...", "chat": "..." }, "webhook": { "url": "..." } }

Network calls time out after 10 s and are retried once. A failing sink is logged (by name) and never stops the watch.

On a server with PM2

pm2 start "npx -y storebound watch --notify ntfy" --name storebound-watch
pm2 save

Put the credentials profiles (~/.storebound/credentials/) and the app folder list (storebound apps add <folder>) on that server, and the sink variables in the PM2 environment (STOREBOUND_NTFY_TOPIC=...). The loop polls every --every (5m at least, 15m default, +-10 % jitter), backs off after failed passes (up to 4 h), writes one heartbeat line per pass and stops cleanly on SIGINT / SIGTERM. With cron use --once.