Appearance
Bandwidth metering, throttle & cutoff
Bandwidth on Imaginook is pooled per account — a monthly cap (25 GB by default) shared across all of a user's sites — and metered at the edge, since cache hits never reach the origin. This document covers how counts get in, how usage is graded into a state, and what each state does.
The ledger
Edge metering produces per-host byte counts per window. Those land in the bandwidth_samples table, keyed uniquely on (host, period_key) so re-ingesting the same window is idempotent. An account's bandwidth_used is the sum of its hosts' samples for the current period; BandwidthMeter::recompute() rolls it up.
Ingest (edge → origin)
The edge pushes counts in as JSON and the origin ingests them out-of-band — there is no public HTTP ingest endpoint (nothing browser-reachable can write usage):
php artisan imaginook:ingest-bandwidth --file=/path/to/edge-counts.json
# or stream on stdin:
cat edge-counts.json | php artisan imaginook:ingest-bandwidthEach element is {"host": "alice.imaginook.com", "period": "2026-07-04T14", "bytes": 12345}. Malformed rows (missing host/period) are skipped so one bad line never aborts a batch. Wire this to a Cloudflare GraphQL Analytics pull (or nginx log rollup) on a short cron; the upsert makes overlapping windows safe.
States
BandwidthMeter::stateFor(used, cap) grades usage against the account's cap — users.bandwidth_bytes, a 25 GB default set by the column/factory, overridable per row:
| State | Range | Effect |
|---|---|---|
ok | < warn% of pool | served normally |
warning | warn% … 100% | served normally; owner warned once |
throttled | 100% … pool × cutoff_multiplier | served but rate-limited at the edge (courtesy slow-down) |
cutoff | ≥ pool × cutoff_multiplier | taken offline until the monthly reset (runaway ceiling) |
Thresholds live in config/imaginook.php under bandwidth (warn_percent = 80, cutoff_multiplier = 5). A non-positive cap disables gating entirely (an account is never throttled).
Enforcement (HostMap)
The derived state is denormalised onto users.bandwidth_status and enforced through the nginx map files, mirroring how suspensions and noindex work:
throttledhosts are written to the throttle map (_nginx/throttle_map.conf,map $host $imaginook_throttle). The wildcard server block applies a courtesylimit_req/ "over budget" page to matches. Sites stay online.cutoffaccounts have their hosts dropped from the served host map — the same mechanism a suspension uses, so nginx returns 404 until the reset.
BandwidthMeter applies this immediately on a state change (best-effort); imaginook:nginx-sync (HostMap::rebuild()) is the reconciling backstop and is itself bandwidth-aware, so the maps are correct even if a live signal is missed. The DB is always the source of truth.
Notifications
Each upward step into warning / throttled / cutoff notifies the owner once (debounced by the stored status, so a busy account isn't spammed):
- in-app always, via
Notificationtypebandwidth.{state}; - email via the queued
BandwidthThresholdmailable, gated byimaginook.bandwidth.notify. The email carries only the account's own figures and reset date — never any other user's data.
Monthly reset
imaginook:reset-bandwidth runs daily and, for any account past its period_reset_at, zeroes bandwidth_used, clears its ledger rows, rolls the period forward a month, resets bandwidth_status to ok, and restores any throttled/cut-off hosts to the served map. Only due accounts are touched, so it is safe to run every day.