Data flow
What happens when a user deploys an app — end-to-end traces.
This page traces what happens when a user deploys an app.
How to read the diagrams
Each sequence diagram is a chronological story: each arrow is one network call from left to right. Solid arrows are synchronous (the sender waits); dashed arrows are fire-and-forget. The participant names match the code — HUB is internal/api/ws.go's Hub, SCH is internal/scheduler, W is the internal/worker agent, and so on.
1. Deploy
2. Live container metrics
Snapshots live in process memory and expire after 45 s; misses return live=false. The dashboard receives live stats via WebSocket (/api/ws/usage for aggregate, /api/apps/:id/ws/stats for single-app), replacing the former 10-second HTTP polling. The stats store supports SubscribeStats(appID) which returns a channel + cancel function for push-based updates.
3. Runtime logs
Each line is sanitised (stripANSI + null byte removal) and capped to 8 KiB before reaching the bus. Logs are persisted to the runtime_logs table so they survive control-plane restarts — on WebSocket reconnect, the client receives historical lines from the database before switching to live streaming.
4. Worker health
If a worker drops, the scheduler:
- Marks it
offline. - Stops all
runningapps assigned to it (status cascade). - Pending jobs queued for that worker remain
assigneduntilrecoverStuckJobs(15-min timeout) flips them tofailed. Failures are not retried automatically — the job staysfailedand surfaces to the user, who can redeploy (the in-processrequeueFailedJobsloop andmax_retries/retry_countcolumns were removed in migration 0004 so a worker outage no longer multiplies into N silent re-runs once it returns).
5. Push events (auto-deploy)
Each provider has its own header & secret style:
| Provider | Header | Algorithm |
|---|---|---|
| GitHub | X-Hub-Signature-256 | HMAC-SHA256 of body |
| Codeberg | X-Gitea-Signature | HMAC-SHA256 of body |
| GitLab | X-Gitlab-Token | shared-token equality |
| Bitbucket | X-Hub-Signature (sha256=) | HMAC-SHA256 of body |
6. Alert evaluation (per-app rules)
The poller walks every enabled rule across every app on each tick. A rule fires only after the metric stays over threshold for sustain_minutes, and resolves only after the same window elapses below the threshold — both edges debounced. Event-driven kinds (offline, crashloop, deployment_failed) skip the threshold check and fire/resolve directly off the underlying state change.
7. Usage time-series
Recorder writes one row per (app_id, 5-min bucket) per minute (last write wins for the same bucket). A daily pruner deletes rows older than 90 days. The endpoint never reads raw samples beyond that window.
8. Custom-domain routing
Two Traefik HTTP-provider endpoints serve routing config from internal/api/traefik.go:
- Worker Traefik (
/v1/traefik/config?worker_id=N) — serves zone-subdomain routes (e.g.Host(\myapp.de.uday.me`)) pointing tolocalhost:PORT`. Authenticated by a static bearer token. Polled every 2 s. - Proxy Traefik (
/v1/traefik/proxy-config) — serves custom-domain routes for verified domains, pointing tohttp://WORKER_WG_IP:PORTwith TLS via Let's Encrypt. UsesListVerifiedDomainsByAppIDsfor a single batch query. Polled every 5 s.
Both endpoints support ETag/If-None-Match — SHA-256 hash of the response body, returning 304 Not Modified when the config hasn't changed. buildHostRule joins verified hostnames with ||, and safeRouterName keeps router/service names ASCII-safe.