Architecture
NIE-SLA divides collection, probing, storage, display, and third-party extensions into trust zones. Understanding the boundaries keeps "VPS online", "Cloudflare reachable", and "external latency" from being conflated. New to the project? Start with the overview.
Components
| Component | Runs on | Responsibility |
|---|---|---|
| Rust Agent | monitored VPS | second-level sampling, TCP/HTTP probes, batched uploads |
| Latency Agent | independent Linux node | probes public TCP targets from other networks |
| Worker | Cloudflare edge | routing, auth, scheduling, aggregation, alerts, public API |
| Durable Objects | Cloudflare | regional probe coordination, per-agent telemetry buffering |
| D1 | Cloudflare | config, state, aggregation, events, indexes |
| R2 | Cloudflare | high-frequency history, snapshots, archives, theme packages |
| Worker Static Assets | Cloudflare | public status page, admin UI, same-origin API |
Production is one Worker serving API and Static Assets from the same origin; no separate Pages project. The public one-click repository is a desensitized release source; keep the Worker, Frontend, and Agent source boundaries intact and never overwrite production sources from archives or the public copy.
Data paths
Rust Agent --scoped token--> Worker --> D1 / R2 --> Public status
|
Cloudflare probe ------------------+
|
Latency Agent --scoped token-------+Sampling, upload, ping, and update checks run on separate schedulers. A VPS can sample every second while the Worker only receives batches; a slow network does not block local sampling.
Four latency and online states
| Source | Initiated by | Answers |
|---|---|---|
| Cloudflare checks | Worker / Durable Object | can Cloudflare edge reach the target, at what latency |
| Agent online state | monitored VPS | is the metrics agent still reporting |
| Agent network probes | monitored VPS | network quality from the host to a TCP/HTTP target |
| External latency | independent nodes | latency from extra regions/providers to public TCP targets |
Clients must keep source labels. An online Agent does not imply successful Cloudflare checks, and an open port does not imply the metrics agent is running.
Storage strategy
Raw per-second metrics are not written to D1:
- Current state and indexes live in D1.
- High-frequency history goes to R2 first.
- SLA history is aggregated into fixed buckets in D1.
- Traffic accumulates in the current period row; each agent seals one daily ledger row per day, and reset-day changes recompute from the daily ledger.
- Status endpoints use short caches.
- Cleanup runs on schedule, not per request.
This keeps the public state fresh while bounding Worker requests, D1 writes, and R2 operations.
Raw loss data and optional timeseries export
R2 keeps the raw probe points agents report. Regular latency curves are downsampled per public API caps; include_loss=1 returns all loss events losslessly as compact runs, so long failures do not need per-second objects in the browser.
External timeseries export is off by default. With TIMESERIES_EXPORT_URL set, the Worker exports batches from R2 per agent and completed hour; TIMESERIES_EXPORT_FORMAT is victoriametrics (default) or influx, with optional Bearer credentials via TIMESERIES_EXPORT_TOKEN. The URL must be HTTPS without embedded credentials. These values come only from Worker Secrets or deployment environment; they never enter the frontend, Agent commands, public API, backups, or source.
Export uses a bounded retry marker. On remote timeout, throttling, or unavailability, R2 archiving and Agent ACK stay successful; after the retry cap only that export attempt is dropped, never the R2 data. With no export configured, no external requests are added and the 100-VPS storage path is unchanged.
Interfaces and trust boundaries
| Interface | Auth | Can do |
|---|---|---|
/api/v1/* | none | read desensitized public state and history |
/api/agent/* | node scoped token | the matching Rust agent reports and reads policy |
/api/latency-agent/* | node scoped token | latency node fetches targets, reports, updates |
| Admin API | short session, optional TOTP | modify config, upload themes, read admin data |
Canvas themes and alternate frontends may only use the public v1 API. Never pass admin sessions, passwords, TOTP, the Agent master token, or node scoped tokens to a theme; the plugin runtime is not open.
Passwords and GitHub OAuth tickets exist only for login. After login the admin UI uses the short-lived x-admin-session; passwords must not be stored as API tokens or proxied by alternate frontends. Agents and latency nodes use per-node scoped tokens that stop working when the node is disabled.
NodeQuality image boundary
NQ Network and Return Route images are rendered and uploaded by the Worker after an authenticated task completes. The upload chain is a fixed S3 channel with an empty folder; the frontend, Agent, and admin API cannot read or override the upload URL, token, channel, or folder.
- Upload URL, token, and optional channel name are Worker Secrets only.
- Only tasks created by an admin, claimed by the matching Agent credential, and completed successfully trigger uploads.
- There is no browser setting, test upload, or arbitrary file upload endpoint.
- Public NQ JSON returns same-origin image proxies only; the real upstream URL is resolved inside the Worker.
- Normal backups exclude old image-host metadata; sensitive backups are password-encrypted.
- Self-hosted deployments call the fixed official public broker with no image-host credentials; shared credentials exist only in the official Worker Secrets and never enter source, build output, tutorials, or broker requests.
The broker accepts bounded network/route text, re-renders SVG server-side, and rate-limits by source and globally via D1. This gives self-hosters zero-config NQ image backups without turning the shared image host into an arbitrary upload API. Non-official instances cannot override the fixed public chain with their own secrets.
All four NQ tabs keep the original ANSI content. Network and Return Route use the historical segmented renderer when the original text exists; broker images only fill in for old data without text. Mobile uses a fixed compact monospace size, the modal scrolls vertically only, wide network segments scroll horizontally inside their content area, and return-route hops use a responsive grid. The public page and admin UI share the same rules.
Current state vs long-term SLA
The Worker Cron runs every minute as a lightweight scheduler; healthy targets are probed for history every 15 minutes by default, while failed targets enter an approximately 2-minute recovery queue. Long-term SLA and daily grids still use 5-minute buckets. The next_probe_at index reads only due candidates instead of scanning every target. With FAST_STATUS_ENABLED off, active probe state falls back to bucket granularity.
In economic mode, the Agent still samples locally every second but uploads a batch every 15 minutes by default. Task claiming is every 10 minutes and ordinary update-policy checks are daily. Metrics can therefore be up to 15 minutes old, while failure recovery remains short and manual/forced updates bypass the ordinary update interval.
"Latest state on the public page" and "one new daily grid cell" do not need the same frequency. Integrations should read checked_at, updated_at, and source fields rather than inferring liveness from array lengths.
Theme runtime
CSS themes load only verified stylesheets. Canvas themes run in a sandbox="allow-scripts" iframe without same-origin access; CSP blocks direct networking, forms, and top-level navigation, and data arrives through the restricted message protocol. Plugin upload APIs and plugin runtimes are not open. See Theme System and Security and Release.