Commits
MCP server that controls the everyday Firefox through a WebExtension and
a native messaging host instead of Marionette/WebDriver, so
navigator.webdriver stays false and Cloudflare/reCAPTCHA stop flagging
normal browsing. No browser restart is needed to attach, tabs are
addressed by id on demand.
barnacle installs the server and links the native messaging host
manifest into /Library/Application Support/Mozilla/NativeMessagingHosts.
The extension is signed as unlisted on AMO via browser-mcp-sign.
atuin 18.23.0 fails this e2e test in unsandboxed darwin builds: the test
shell reads nix-darwin's /etc/bashrc, which resets PATH so the atuin
under test is not used. Fixed in nixpkgs by NixOS/nixpkgs#574509
(NOSYSBASHRC in preCheck); drop this package once our nixpkgs pin
includes it. Linux keeps the unmodified nixpkgs derivation.
ntfy.sh's free tier counts every UnifiedPush message against the
subscriber: 250 messages/day shared by all UnifiedPush apps on the phone.
Once used up it answers 429, phonetpm can no longer wake the phone and
every SSH login and deploy fails. Run our own ntfy on neoprism instead,
anonymous like ntfy.sh so app servers publish without accounts.
Rate limits: ntfy 2.29 defaults stay, except for an exemption.
- visitor-message-daily-limit is 0 (unlimited), only the request limiter
applies.
- visitor-subscriber-rate-limiting is off (ntfy.sh turns it on), so
publishes to up* topics are charged to the publisher, not to the phone.
The phone only spends requests on its subscription connections (30
allowed, the app uses one per server).
- The request limit is 60 burst, then one per 5s, per publisher IP.
Synapse on neoprism sends every Matrix push of the phone from one
address; encrypted and bridged rooms notify per message, so bridge
catch-up can exceed that. visitor-request-limit-exempt-hosts exempts
neoprism's own addresses. phonetpm daemons publish one wake per
connection and stay far below the limit.
- behind-proxy: ntfy takes the right-most X-Forwarded-For address, the
one nginx appends, so visitors are not all 127.0.0.1 and a client
cannot spoof its address.
The module enables attachments by default (attachment-cache-dir), set it
empty to keep them off. nginx settings follow ntfy's example config
(buffering off, 3m timeouts against the 45s keepalive, websockets).
No backup: the state dir (/var/lib/private/ntfy-sh) holds the message
cache (12h, disposable) and an auth db without users.
SystemdUnitFailed matched any line containing "Failed with result", so a
VictoriaLogs warning echoing a rejected query fired it for
victorialogs.service. Anchor a regexp on _msg to systemd's
"<unit>: Failed with result '<result>'." form.
Since d9ed167 networkd installed the route to the container itself.
networkd adds it as soon as ve-riot gets carrier, before the container
start script runs `ip route add 10.233.1.2 dev ve-riot`, so the script
fails with "RTNETLINK answers: File exists" and container@riot ends up
in a restart loop. Before the reboot riot had never restarted since
d9ed167, so nobody noticed.
The script's route and address come from nixpkgs' nixos-containers
module and can't be turned off without patching nixpkgs (localAddress
also sets the container side), so the networkd route has to go. Now
ve-riot is Unmanaged=yes instead, and the start script owns its
address and route. Checked against systemd 261:
- An unmanaged link gets no configuration from networkd, so nothing is
in place before the script's `ip route add` and it succeeds on every
start.
- networkd only drops foreign routes for the link it is configuring
(link_drop_unmanaged_config, CONFIGURING state). When link_drop_routes
marks foreign routes it skips routes on other interfaces, "Otherwise,
routes assigned to unmanaged interfaces will be dropped". So a networkd
restart or reload no longer touches the script's route. That was the
bug d9ed167 fixed.
KeepConfiguration=static could not keep the script's route anyway: it
only protects foreign routes with protocol static, and `ip route add`
uses protocol boot. LinkLocalAddressing=no and KeepConfiguration=static
mean nothing on an unmanaged link, so they are gone, along with the
commented-out IPForward/IPMasquerade lines. Forwarding and NAT stay in
nftables and the sysctl. No other .network file on neoprism matches
ve-riot (99-ethernet-default-dhcp needs Kind=!*), but Unmanaged=yes
also keeps a later catch-all file from taking over ve-riot.
neoprism's NVMe disks report critical_warning 4 and a failing SMART
status only because percentage_used is above 100% (rated endurance
exceeded). Hetzner confirmed with the manufacturer that bit 0x04 is set
for that reason and that the drives are fine as long as available_spare
is above available_spare_threshold, which NvmeSpareLow watches. smartctl
derives smart_status from the same bit, so both alerts stayed red on
worn but healthy drives.
NvmeCriticalWarning now fires only on critical_warning bits other than
0x04 (bitmap_and with 251). SmartFailed skips devices that
smartctl_exporter reports with protocol="NVMe" and keeps covering
SATA/SCSI disks.
smartctl_exporter runs on every non-container machine, bound to
localhost and scraped by Vector into the host-tagged metrics path.
Alerts on a failed SMART status, NVMe critical warnings, new NVMe media
errors and NVMe spare below its threshold.
Memory PSI comes from /proc/pressure/memory via an exec source, as a
counter of stalled seconds, so the alert takes an exact 10m rate instead
of the kernel's 5m avg300 EWMA. Plus an alert on any OOM kill.
MemoryPressure threshold 10%: some task stalling on memory a tenth of
the time over 10 minutes is sustained reclaim/swap thrash, while short
spikes such as big builds average out.
autoUpgrade pins the upgrade to the revision the forward-only guard resolved and writes it to /var/lib/superconfig-revision after a successful upgrade, like deploy does. Vector reads that file every 30s and exports nixos_system_info{host,revision,revision_short}; the node dashboard shows host -> revision_short.
The flake's git revision (-dirty with uncommitted changes, unknown for non-git sources) is copied to the target with the source and written by deploy-switch once the switch succeeded. Read by the monitoring agent; it is in no derivation, so a commit doesn't change every machine's closure.
kartei carries both cards (krebs/kartei#92), so the superconfig-only host
set is empty; read hosts, own addresses and tinc host files from kartei
alone.
Query field values for host and unit (unit narrowed by the selected hosts), with multi-select and All = *, and filter the panel with in().
debugLevel 0 still maps to Info, so reconnect, MTU and path-revalidation messages flood journald. RUST_LOG has to be target-scoped: -d 0 sets the global directive and replaces a bare RUST_LOG=warn.
HTTP service rendering wallpapers on demand for a location, variant,
overlay and size. Renders stay fresh for 10 minutes; ones that were
picked up are re-rendered in the background when they go stale.
realwallpaper gains --cache-dir so locations share one data cache, and
its cache cleanup now tracks last use instead of fetch time.
Replaces pkgs.realwallpaper (xplanet, dead NASA NEO sources) with a
Python renderer: oblique cylindrical map centred on home with a zoom
lens (or plain/mercator), multi-resolution WMS imagery (Sentinel-2
cloudless, Blue Marble, Black Marble lights), live EUMETSAT clouds with
synthesised detail, VIIRS snow/fire/chlorophyll, sea ice and the sky.
The krebs.realwallpaper module, its nginx config and the mors
fetchWallpaper config are dropped; the tool runs standalone for now.
pkgs.phonetpm: 0.4.1 -> 0.4.2
Branches, PRs and auto-merge now use the UPDATE_PKGS_TOKEN repo secret
instead of GITHUB_TOKEN, so the normal pull_request ci.yml run (with the
farm build) shows up on each PR and GitHub merges it with a merge commit
once the ruleset is met. The serial verify job (rebase, dispatch ci.yml,
wait, merge) and the final ci dispatch on master are gone: the auto-merge
counts as the token's, so the push to master triggers ci.yml itself.
UPDATE_PKGS_TOKEN: fine-grained PAT (or GitHub App token) for this repo
with Contents: read/write, Pull requests: read/write and Workflows:
read/write (force-pushing an update branch rebased over master commits that
touched .github/workflows is refused without it). The repo needs "Allow
auto-merge" enabled. The workflow fails early if the secret is missing.
Grafana 13 no longer ships the prometheus datasource as a core plugin, so the provisioned VictoriaMetrics datasource was unusable. Switch it and the dashboards to victoriametrics-metrics-datasource.
Every NixOS machine runs Vector (host metrics, internal metrics, journal)
and writes to VictoriaMetrics/VictoriaLogs on both styx and coaxmetal,
each sink with a 512 MiB disk buffer. Both servers run vmalert (metrics and
LogsQL instances), a two-node Alertmanager cluster posting to Matrix via
matrix-alertmanager-receiver, Gatus (results scraped into VictoriaMetrics,
alerted on by vmalert) and Grafana with provisioned datasources and
dashboards. All ports are open on retiolum only.
Replaces telegraf on neoprism, including the borgbackup status file.
The client gives up after connect-timeout on a store it has never reached.
At farm start envoy has no builder yet (none joined, or not yet dialed and
health checked through its tunnel), answers with grpc-status 14, and a few
machine jobs failed on their first QueryMissing. Probe a health check through
the balancer until a builder answers it.
hermes-agent runs with ProtectSystem=strict and cannot write the
group-writable routes dir. crit now asks crit-routes.socket (group crit)
for a route; a root service picks the id, writes the route, regenerates
the nginx map and reloads nginx.
Encrypting to an age1gpg recipient asked gpg-agent (READKEY) for the
public key, which fails on hosts that only have the public key imported
and no secret key or smartcard stub. Export the key by keygrip with gpg
instead in that case and build the same key as the agent path, so
stanzas are unchanged. Decryption still goes through gpg-agent.
ci.yml calls farm.yml (now reusable) instead of the eval-only nixos-*
checks, which are gone from flake.nix. The farm job is only the
coordinator (scheduler + envoy, which now requires the farm client
cert, reachable over iroh). Each machine job joins as a builder,
evaluates its machine, builds the toplevel through the farm and leaves
when done; its outputs are in niks3 by then.
ci runs for master pushes, PRs and dispatches; a newer run for the same
ref cancels the older one, farms of different refs queue FIFO
(queue: max). update-pkgs verifies PRs with ci.yml alone.
The crit wrapper writes a fresh random 10-char id per daemon port into
/var/lib/crit-routes; a path unit turns that into an nginx map on
coaxmetal:7720 (retiolum only). neoprism proxies *.review.lassul.us there
with a wildcard cert obtained via DNS-01 (RFC2136) against a delegated
_acme-challenge.review.lassul.us zone on knot, so ids never hit CT logs.
MCP server that controls the everyday Firefox through a WebExtension and
a native messaging host instead of Marionette/WebDriver, so
navigator.webdriver stays false and Cloudflare/reCAPTCHA stop flagging
normal browsing. No browser restart is needed to attach, tabs are
addressed by id on demand.
barnacle installs the server and links the native messaging host
manifest into /Library/Application Support/Mozilla/NativeMessagingHosts.
The extension is signed as unlisted on AMO via browser-mcp-sign.
atuin 18.23.0 fails this e2e test in unsandboxed darwin builds: the test
shell reads nix-darwin's /etc/bashrc, which resets PATH so the atuin
under test is not used. Fixed in nixpkgs by NixOS/nixpkgs#574509
(NOSYSBASHRC in preCheck); drop this package once our nixpkgs pin
includes it. Linux keeps the unmodified nixpkgs derivation.
ntfy.sh's free tier counts every UnifiedPush message against the
subscriber: 250 messages/day shared by all UnifiedPush apps on the phone.
Once used up it answers 429, phonetpm can no longer wake the phone and
every SSH login and deploy fails. Run our own ntfy on neoprism instead,
anonymous like ntfy.sh so app servers publish without accounts.
Rate limits: ntfy 2.29 defaults stay, except for an exemption.
- visitor-message-daily-limit is 0 (unlimited), only the request limiter
applies.
- visitor-subscriber-rate-limiting is off (ntfy.sh turns it on), so
publishes to up* topics are charged to the publisher, not to the phone.
The phone only spends requests on its subscription connections (30
allowed, the app uses one per server).
- The request limit is 60 burst, then one per 5s, per publisher IP.
Synapse on neoprism sends every Matrix push of the phone from one
address; encrypted and bridged rooms notify per message, so bridge
catch-up can exceed that. visitor-request-limit-exempt-hosts exempts
neoprism's own addresses. phonetpm daemons publish one wake per
connection and stay far below the limit.
- behind-proxy: ntfy takes the right-most X-Forwarded-For address, the
one nginx appends, so visitors are not all 127.0.0.1 and a client
cannot spoof its address.
The module enables attachments by default (attachment-cache-dir), set it
empty to keep them off. nginx settings follow ntfy's example config
(buffering off, 3m timeouts against the 45s keepalive, websockets).
No backup: the state dir (/var/lib/private/ntfy-sh) holds the message
cache (12h, disposable) and an auth db without users.
Since d9ed167 networkd installed the route to the container itself.
networkd adds it as soon as ve-riot gets carrier, before the container
start script runs `ip route add 10.233.1.2 dev ve-riot`, so the script
fails with "RTNETLINK answers: File exists" and container@riot ends up
in a restart loop. Before the reboot riot had never restarted since
d9ed167, so nobody noticed.
The script's route and address come from nixpkgs' nixos-containers
module and can't be turned off without patching nixpkgs (localAddress
also sets the container side), so the networkd route has to go. Now
ve-riot is Unmanaged=yes instead, and the start script owns its
address and route. Checked against systemd 261:
- An unmanaged link gets no configuration from networkd, so nothing is
in place before the script's `ip route add` and it succeeds on every
start.
- networkd only drops foreign routes for the link it is configuring
(link_drop_unmanaged_config, CONFIGURING state). When link_drop_routes
marks foreign routes it skips routes on other interfaces, "Otherwise,
routes assigned to unmanaged interfaces will be dropped". So a networkd
restart or reload no longer touches the script's route. That was the
bug d9ed167 fixed.
KeepConfiguration=static could not keep the script's route anyway: it
only protects foreign routes with protocol static, and `ip route add`
uses protocol boot. LinkLocalAddressing=no and KeepConfiguration=static
mean nothing on an unmanaged link, so they are gone, along with the
commented-out IPForward/IPMasquerade lines. Forwarding and NAT stay in
nftables and the sysctl. No other .network file on neoprism matches
ve-riot (99-ethernet-default-dhcp needs Kind=!*), but Unmanaged=yes
also keeps a later catch-all file from taking over ve-riot.
neoprism's NVMe disks report critical_warning 4 and a failing SMART
status only because percentage_used is above 100% (rated endurance
exceeded). Hetzner confirmed with the manufacturer that bit 0x04 is set
for that reason and that the drives are fine as long as available_spare
is above available_spare_threshold, which NvmeSpareLow watches. smartctl
derives smart_status from the same bit, so both alerts stayed red on
worn but healthy drives.
NvmeCriticalWarning now fires only on critical_warning bits other than
0x04 (bitmap_and with 251). SmartFailed skips devices that
smartctl_exporter reports with protocol="NVMe" and keeps covering
SATA/SCSI disks.
smartctl_exporter runs on every non-container machine, bound to
localhost and scraped by Vector into the host-tagged metrics path.
Alerts on a failed SMART status, NVMe critical warnings, new NVMe media
errors and NVMe spare below its threshold.
Memory PSI comes from /proc/pressure/memory via an exec source, as a
counter of stalled seconds, so the alert takes an exact 10m rate instead
of the kernel's 5m avg300 EWMA. Plus an alert on any OOM kill.
MemoryPressure threshold 10%: some task stalling on memory a tenth of
the time over 10 minutes is sustained reclaim/swap thrash, while short
spikes such as big builds average out.
autoUpgrade pins the upgrade to the revision the forward-only guard resolved and writes it to /var/lib/superconfig-revision after a successful upgrade, like deploy does. Vector reads that file every 30s and exports nixos_system_info{host,revision,revision_short}; the node dashboard shows host -> revision_short.
HTTP service rendering wallpapers on demand for a location, variant,
overlay and size. Renders stay fresh for 10 minutes; ones that were
picked up are re-rendered in the background when they go stale.
realwallpaper gains --cache-dir so locations share one data cache, and
its cache cleanup now tracks last use instead of fetch time.
Replaces pkgs.realwallpaper (xplanet, dead NASA NEO sources) with a
Python renderer: oblique cylindrical map centred on home with a zoom
lens (or plain/mercator), multi-resolution WMS imagery (Sentinel-2
cloudless, Blue Marble, Black Marble lights), live EUMETSAT clouds with
synthesised detail, VIIRS snow/fire/chlorophyll, sea ice and the sky.
The krebs.realwallpaper module, its nginx config and the mors
fetchWallpaper config are dropped; the tool runs standalone for now.
Branches, PRs and auto-merge now use the UPDATE_PKGS_TOKEN repo secret
instead of GITHUB_TOKEN, so the normal pull_request ci.yml run (with the
farm build) shows up on each PR and GitHub merges it with a merge commit
once the ruleset is met. The serial verify job (rebase, dispatch ci.yml,
wait, merge) and the final ci dispatch on master are gone: the auto-merge
counts as the token's, so the push to master triggers ci.yml itself.
UPDATE_PKGS_TOKEN: fine-grained PAT (or GitHub App token) for this repo
with Contents: read/write, Pull requests: read/write and Workflows:
read/write (force-pushing an update branch rebased over master commits that
touched .github/workflows is refused without it). The repo needs "Allow
auto-merge" enabled. The workflow fails early if the secret is missing.
Every NixOS machine runs Vector (host metrics, internal metrics, journal)
and writes to VictoriaMetrics/VictoriaLogs on both styx and coaxmetal,
each sink with a 512 MiB disk buffer. Both servers run vmalert (metrics and
LogsQL instances), a two-node Alertmanager cluster posting to Matrix via
matrix-alertmanager-receiver, Gatus (results scraped into VictoriaMetrics,
alerted on by vmalert) and Grafana with provisioned datasources and
dashboards. All ports are open on retiolum only.
Replaces telegraf on neoprism, including the borgbackup status file.
The client gives up after connect-timeout on a store it has never reached.
At farm start envoy has no builder yet (none joined, or not yet dialed and
health checked through its tunnel), answers with grpc-status 14, and a few
machine jobs failed on their first QueryMissing. Probe a health check through
the balancer until a builder answers it.
Encrypting to an age1gpg recipient asked gpg-agent (READKEY) for the
public key, which fails on hosts that only have the public key imported
and no secret key or smartcard stub. Export the key by keygrip with gpg
instead in that case and build the same key as the agent path, so
stanzas are unchanged. Decryption still goes through gpg-agent.
ci.yml calls farm.yml (now reusable) instead of the eval-only nixos-*
checks, which are gone from flake.nix. The farm job is only the
coordinator (scheduler + envoy, which now requires the farm client
cert, reachable over iroh). Each machine job joins as a builder,
evaluates its machine, builds the toplevel through the farm and leaves
when done; its outputs are in niks3 by then.
ci runs for master pushes, PRs and dispatches; a newer run for the same
ref cancels the older one, farms of different refs queue FIFO
(queue: max). update-pkgs verifies PRs with ci.yml alone.
The crit wrapper writes a fresh random 10-char id per daemon port into
/var/lib/crit-routes; a path unit turns that into an nginx map on
coaxmetal:7720 (retiolum only). neoprism proxies *.review.lassul.us there
with a wildcard cert obtained via DNS-01 (RFC2136) against a delegated
_acme-challenge.review.lassul.us zone on knot, so ids never hit CT logs.