DailyRoundup

Operations

Health Monitoring

The server exposes a GET /health endpoint that returns 200 OK when the service is running. Check it from the server or any machine with network access:

curl https://<your-server>/health

For account-scoped stats (number of synced lists and registered devices), use the authenticated status endpoint:

curl -H "Authorization: Bearer <auth-token>" https://<your-server>/dailyroundup/status

The healthcheck.py script in roundup-server/ performs a more thorough post-deployment check: it polls GET /health until the service responds, validates APNs JWT signing, and verifies Trello API connectivity for all configured accounts. It is run automatically by the Deploy code to sync server GitHub Actions workflow after each deployment.

Alerting

GitHub Actions sends a notification when the Deploy code to sync server or Upload to App Store Connect workflow fails. No automated alerting is configured for runtime server errors; monitor the service by watching logs (see Server Logs) or polling GET /health.

Usage Metrics

When DD_AGENT_HOST is set, the server emits a focused set of low-cardinality DogStatsD metrics over UDP to a local Datadog Agent so feature/endpoint usage, latency, and outbound calls are visible in Datadog. Counters are incremented by 1 per event; the request-latency metric is a histogram sample per request. When DD_AGENT_HOST is unset the metrics client is a complete no-op and opens no socket, so development, tests, and CI are unaffected. Set DD_DOGSTATSD_PORT to override the default UDP port (8125). Both variables are documented in the Environment Variables table.

Metric Type Tags Emitted when
dailyroundup.requests counter endpoint (matched view name, e.g. api.sync, or unknown), method, status_class (2xx/3xx/4xx/5xx) Every HTTP request completes (Flask after_request hook)
dailyroundup.request.duration histogram (ms) endpoint (matched view name, e.g. api.sync, or unknown) Every HTTP request completes; the value is request wall-clock latency in milliseconds (Flask before_request/after_request hooks)
dailyroundup.trello.calls counter operation (logical Trello call, e.g. GET_cards_:id), status (success/rate_limited/timeout/auth_error/not_found/server_error/error) Each outbound Trello API call finishes
dailyroundup.apns.notifications counter type (conflict/duplicate/list_removed/card_reassigned/sync_push), status (sent/failed/permanent_error/skipped) Each APNs push attempt (per device), or once as skipped when APNs is not configured
dailyroundup.geocoding counter direction (forward/reverse), status (success/failure/timeout) Each Nominatim geocode during location sync

The endpoint and operation tag values are deliberately templatized (object IDs are collapsed to :id) to keep tag cardinality a small finite set. No per-user, per-card, or per-token tags are emitted.

dailyroundup.request.duration is sent as a DogStatsD histogram, so Datadog automatically derives the dailyroundup.request.duration.avg, .95percentile, .count, and .max sub-metrics (among others). Chart .avg and .95percentile for typical and tail web-call latency, and use .count to confirm request volume.

The host deployment sets DD_AGENT_HOST=127.0.0.1 and DD_DOGSTATSD_PORT=8125 automatically via the Deploy code to sync server workflow, which writes them (as non-secret values) into roundup-server/.env. The systemd service loads that file, so a Datadog Agent listening for DogStatsD on 127.0.0.1:8125 receives the metrics after the next deploy and restart. If the Agent is not yet installed, the sends fail silently (logged at WARNING) and do not affect request handling.

Common Issues

App shows “Authentication failed” after entering server URL

The auth token in Settings is invalid or missing. Tap Connect to Server… and enter your bootstrap token to create a new account. If you already have a permanent token, expand “I already have a token” and paste it directly.

Trello changes are not appearing in Reminders

  1. Verify the server is reachable: pull down on the task list to trigger a sync and check the sync status at the top.
  2. Confirm a Webhook Secret is configured in Settings → Trello Configuration. The server rejects all webhook events from accounts without one.
  3. Check the server logs for 403 responses or no trello_webhook_secret configured warnings.

Webhook events returning 403

The Webhook Secret in the iOS app does not match the value Trello is signing requests with. Update the Webhook Secret in Settings → Trello Configuration to match, then save.

APNs push notifications not delivering

Check that all four APNS_* environment variables are set in .env and the server has been restarted. The server logs a DEBUG message on startup if any are missing. Confirm APNS_USE_SANDBOX matches the build type (set to 1 for development builds, omit for App Store/TestFlight).

Sync stopped after moving to a new device

iCloud Keychain syncs the auth token automatically, but may take a few minutes. If the token does not appear, use “I already have a token” in Settings to paste it from your previous device.

Sync drift or a job stuck after a deploy or restart

A systemctl restart kills the gunicorn worker without awaiting detached background-job threads, so a sync/merge/import job can be cut off mid-run. The system self-heals (issue #393): the deploy workflow runs drain.py first to let in-flight sync jobs finish (bounded by DAILYROUNDUP_DRAIN_TIMEOUT), startup recovery in create_app flips any job still running at boot to error so clients stop polling and re-push, and the scheduler’s startup deep reconcile re-sends whatever was left partial. To confirm recovery ran after a restart, look for Startup recovery: marked N interrupted background job(s) as error and scheduler: deep reconcile (startup) in the journal. If drift persists, the daily deep reconcile imports untracked Trello cards, re-creates missing Reminders, and corrects completion mismatches with Trello as source of truth; it can be hastened by restarting the service. See Sync Internals — Self-healing and restart recovery.

Server Logs

The server logs to stdout/stderr, captured by systemd’s journal. Logs are written in this format:

2026-02-24T14:32:01 INFO [dailyroundup.webhook] Recorded 'create' change for Trello card abc123

Fields: YYYY-MM-DDTHH:MM:SS LEVEL [logger.name] message

Reading logs requires SSH access to the server and the journalctl tool (included with systemd). Replace <username> with the system user running the service:

# Stream live logs
journalctl -u dailyroundup@<username> -f

# Last 100 lines
journalctl -u dailyroundup@<username> -n 100

# Logs from the past hour
journalctl -u dailyroundup@<username> --since "1 hour ago"

# Errors only
journalctl -u dailyroundup@<username> -p err

# Save today's logs to a file
journalctl -u dailyroundup@<username> --since today > ~/dailyroundup-$(date +%F).log