feat: add Cloudflare tunnel hosting

This commit is contained in:
Jet 2026-05-28 14:50:07 -07:00
parent e6c1b82679
commit 23e087ae4b
No known key found for this signature in database
15 changed files with 839 additions and 30 deletions

View file

@ -66,7 +66,7 @@ The production DigitalOcean host in this repo enables the cache, Discord, and RS
- `noisebell.extremist.software`
- `discord.noisebell.extremist.software`
- `rss.noisebell.extremist.software`
- `rss-noisebell.extremist.software`
After installation, authenticate Tailscale interactively on the host with:

View file

@ -18,7 +18,7 @@ If the Pi stops responding to polls (configurable threshold, default 3 misses),
`since` is the Pi-reported time when the current state began. `last_checked` is when the cache most recently attempted a poll.
The public Caddy vhost returns `404` for `/metrics`; Prometheus scrapes the cache directly on localhost. Metrics include the configured Pi target, poll interval, offline threshold, last poll result, last HTTP status, last poll duration, last poll attempt/success/failure timestamps, and failure counters split into HTTP, timeout, connect, request-other, and parse failures.
The public Caddy vhost returns `404` for `/metrics` and `/webhook`; Prometheus scrapes the cache directly on localhost, and the Pi posts webhooks over Tailscale. Metrics include the configured Pi target, poll interval, offline threshold, last poll result, last HTTP status, last poll duration, last poll attempt/success/failure timestamps, and failure counters split into HTTP, timeout, connect, request-other, and parse failures.
Regular timer-driven poll data should be debugged from Prometheus and Grafana, not by scanning logs. The cache logs sparse events instead: state changes applied from the Pi, offline/online transitions, first or changed poll failures in a failure streak, stale events, auth/rate-limit rejections, outbound webhook deliveries, retries, and final failures. Successful unchanged polls, badge/image/status reads, and metrics scrapes are intentionally quiet at `INFO`.

View file

@ -4,6 +4,7 @@ pkg:
let
cfg = config.services.noisebell-cache;
bin = "${pkg}/bin/noisebell-cache";
caddyHost = if cfg.httpOnly then "http://${cfg.domain}" else cfg.domain;
in
{
options.services.noisebell-cache = {
@ -14,6 +15,12 @@ in
description = "Domain for the Caddy virtual host.";
};
httpOnly = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Use an explicit HTTP-only Caddy virtual host, for example behind Cloudflare Tunnel.";
};
piAddress = lib.mkOption {
type = lib.types.str;
description = "Address of the Pi (e.g. http://noisebell:80).";
@ -87,9 +94,10 @@ in
};
users.groups.noisebell-cache = { };
services.caddy.virtualHosts.${cfg.domain}.extraConfig = ''
services.caddy.virtualHosts.${caddyHost}.extraConfig = ''
redir / https://git.extremist.software/jet/noisebell 302
respond /metrics 404
respond /webhook 404
reverse_proxy localhost:${toString cfg.port}
'';

View file

@ -4,6 +4,7 @@ pkg:
let
cfg = config.services.noisebell-rss;
bin = "${pkg}/bin/noisebell-rss";
caddyHost = if cfg.httpOnly then "http://${cfg.domain}" else cfg.domain;
in
{
options.services.noisebell-rss = {
@ -14,6 +15,12 @@ in
description = "Domain for the Caddy virtual host.";
};
httpOnly = lib.mkOption {
type = lib.types.bool;
default = false;
description = "Use an explicit HTTP-only Caddy virtual host, for example behind Cloudflare Tunnel.";
};
port = lib.mkOption {
type = lib.types.port;
default = 3002;
@ -37,7 +44,7 @@ in
};
users.groups.noisebell-rss = { };
services.caddy.virtualHosts.${cfg.domain}.extraConfig = ''
services.caddy.virtualHosts.${caddyHost}.extraConfig = ''
reverse_proxy localhost:${toString cfg.port}
'';