Test PR from the hermes account (fork on knot.lassul.us, no merge rights). Fine to close instead of merging.
+3
-2
configs/ssh-redirect.nix
+16
-14
configs/dns/knot.nix
+7
configs/dns/lassul.us.zone
+3
machines/neoprism/config.nix
+11
pkgs/tangled/appview-knot-members-did-web.patch
+58
pkgs/tangled/assets.json
+183
pkgs/tangled/package.nix
+69
pkgs/tangled/update.sh
+33
pkgs/tranquil-pds/package.nix
+132
configs/services/git/appview.nix
+8
configs/services/git/default.nix
+216
configs/services/git/knot.nix
+133
configs/services/git/knotmirror.nix
+152
configs/services/git/pds.nix
History
3 rounds
0 comments
11 commits
Expand
Collapse
nixpkgs has tranquil-pds 0.6.7 with its frontend and a NixOS module;
this bumps both to the 0.7.0 release. The frontend is passthru.frontend,
for services.tranquil-pds.settings.frontend.dir.
Same delegation and TSIG key as for *.review.lassul.us, so lego can get
the wildcard cert for PDS handles via DNS-01.
pds.lassul.us with handles <name>.pds.lassul.us (wildcard cert via
DNS-01). Login through the zitadel project pds (access = declarative,
only lass@lassul.us), via Tranquil's generic OIDC SSO. Accounts are
created only through the SSO signup, without invite codes; nginx
returns 403 for createAccount, createPasskeyAccount and
createDelegatedAccount (this also blocks migrating accounts in).
SMTP username and SSO_OIDC_ENABLED are set via the environment
because the nixpkgs module validates the config file at build time
without the secrets they require. Mail goes out as send-only
pds@lassul.us, a mailbox on neoprism's mailserver defined next to the
PDS.
Sets the knot's owner DID (lass.pds.lassul.us), which turns the knot on.
knot-rs binds one SSH address, so it stays on [::]:2222 and port 22 on
neoprism's two public addresses is redirected to it (configs/ssh-redirect.nix
only rejects 22 that still reaches sshd). Admin SSH is unchanged.
Expand 0 comments
37 commits
Expand
Collapse
The Android app now handles VOICE_COMMAND (HFP AT+BVRA from headsets),
shows over the lock screen and asks to unlock before opening the page.
It opens /?listen, which starts the call without pressing Talk.
/?listen, which the Android app opens, matched no static file and got 404.
The app holds each call of the page as a self-managed Telecom call
(transactional API, foreground service), so a headset's AT+CHUP reaches
it. Page and app talk over the Custom Tabs postMessage channel: the page
reports started/ended, the app sends hangup. assetlinks.json grants the
app use_as_origin for that.
The Android app holds the call itself instead of opening the page in a
Custom Tab: hold the headset's voice button (HFP AT+BVRA=1 starts
VOICE_COMMAND), talk, short press to hang up, all with the phone locked
and without looking at it.
- VoiceCall: the page's Call in Java over WebRTC (webrtc-sdk/android):
delegation to Hermes, voice approvals, end_call, idle hang-up.
- CallService: a self-managed Telecom call in the foreground, so the
audio goes to the headset and its hang-up (AT+CHUP) ends the call; a
tone says it listens.
- Screens: the current conversation with Call / Hang up, the list of
conversations, and the settings (app token, permissions).
- hermes-voice accepts the app's own token (--app-token-file, clan var
hermes-voice-app on coaxmetal); assist.lassul.us/app/ passes it
through without the web login.
- The page keeps Talk / Hang up in view: only the transcript scrolls.
assetlinks.json, the TWA and the page's ?listen go away with the
Custom Tab.
The voice model sometimes answered a repeated light command with "on it" without emitting a delegation, so the backend never saw it. The persona now says the voice model can do nothing itself, that every action request is delegated every time, and that it may only say it is on it together with the delegation.
lass.backup.state.<name> lets every service declare its folders, excludes,
pre/post backup hooks and the paths its pre hook writes to next to its
definition. The borg job (still `hetzner`, so archive names and pruning
continue) and the borgbackup ssh key generator exist only on machines that
set lass.backup.repo; neoprism's backup.nix now only points to the storage
box.
Pre hooks run inside the borg unit as before; each runs on its own with
pipefail, all of them run and the job fails if any did (a failing pg_dump
used to go unnoticed behind the gzip pipe). Post hooks run in ExecStopPost
as root, also after a failed backup.
The four bridge dumps are split from the old loop into the bridges' files.
The dump subdirs are created by the hooks instead of tmpfiles. hedgedoc's
uploads directory is backed up now.
The imgur items (hedgedoc image uploads) are backed up now too.
Every machine now backs up into its own Hetzner storage box sub-account
(isolation: one machine's ssh key can't read or delete another's
archives), into ./borg in the sub-account's home. lass.backup.user replaces
lass.backup.repo and enables the job; host, port 23 and the box's host key
are module defaults, so a machine's backup.nix is only the sub-account.
Repos are always encrypted (repokey-blake2). The passphrase comes from a
per-machine clan vars generator borgbackup-repokey; it lives in the
password store, so passphrase + repo are enough to restore. The borgbackup
ssh key generator is unchanged.
neoprism moves to a new repo on u550643-sub1 (doInit creates it); the old
unencrypted repo u550643@...:/./neoprism is no longer referenced and is
removed by hand. Its paths, excludes and hooks are unchanged. The job stays
`hetzner`: with fresh repos nothing gains from a rename, and the unit name
is used for the postBackup hooks.
The timers get a fixed random delay of up to 1h after 03:00, so the
machines sharing the box don't all start at once.
Each machine gets a backup.nix with its storage box sub-account
(coaxmetal sub2, styx sub3, acheron sub4, the yellow container sub5);
what is backed up is declared next to the services:
- coaxmetal: the hermes home without caches, toolchains, git clones,
node_modules and target dirs. Every *.db under .hermes (except cache/),
also in subdirs such as cron/ and the matrix crypto store, is dumped
with sqlite3 .backup into /var/backup/hermes, recreated each run so
removed databases don't linger; the live files are excluded.
- styx: n8n's DynamicUser state (/var/lib/private/n8n), its default
SQLite database dumped.
- acheron: zigbee2mqtt (devices, coordinator backup; database.db is JSON)
and mosquitto's retained messages as plain copies.
- yellow: jellyfin, sonarr, radarr, prowlarr, jellyseerr and sabnzbd
configs with their SQLite databases dumped. Excluded: jellyfin metadata,
logs and transcodes, servarr MediaCover and logs (also logs.db),
jellyseerr cache and logs, sabnzbd's incomplete Downloads and logs.
Media and downloads live in /var/download and are not backed up. A
missing database fails the hook instead of sqlite3 creating an empty one.
Grafana (coaxmetal, styx) is not backed up: datasources and dashboards
are provisioned from the repo, admin password and secret key come from
vars, anonymous users are viewers; both instances only have the four
provisioned dashboards.
Based on nixpkgs PR 561044 (zitadel 4.17.3), built from source with
the console and protoc-gen-js 4.0.2; without the Login V2 app.
Keeps Login V1 (also for a new instance) and turns off the service ping
that v4 sends to zitadel.com by default.
Zitadel project hass on neoprism with a public app (PKCE, no secret) and access = "managed": users get access via a user grant an admin gives in the id.lassul.us console. On acheron the auth_oidc custom component from nixpkgs is mounted into the Home Assistant container and configured via /config/packages, next to the username/password login.
modules.zitadel gets authMethod = "none" for public clients: reconcile generates no secret for them and writes only the client id to the env file, so nothing secret has to reach acheron.
Zitadel generates the client id, so it cannot be known at eval time on acheron. It is not secret; after neoprism is deployed it is copied from /var/lib/zitadel-apps/hass-web.env into the prompted hass-oidc var and passed to the container as HASS_OIDC_CLIENT_ID (!env_var in the yaml).
automatic_user_linking reuses the existing HA user whose username equals the preferred_username claim instead of creating a new one; it stays on because only users granted the project can log in. The groups scope is not requested since Zitadel has no groups claim.
Mounts an empty auth_providers list to /config/auth_providers.yaml. homeassistant: auth_providers is read only from configuration.yaml, packages can't set it, so the volume's configuration.yaml includes it once (auth_providers: !include auth_providers.yaml under homeassistant:). Without the homeassistant provider only auth_oidc remains: the welcome page redirects straight to id.lassul.us and no password form is offered.
Long-lived access tokens have no credential and are not checked against a provider, so they keep working. Refresh tokens from earlier password logins fail on the next refresh and need one id.lassul.us login. The password credentials stay in the auth store, so a revert brings the password login back.
When VictoriaMetrics has no sample shortly before the increase() window
(the monitoring server was down, or the series is new), MetricsQL's
increase() treats the first sample in the window as an increase from 0 and
returns the whole counter. After styx lost power for ~9 minutes, OOMKill
fired for coaxmetal with all 5 OOM kills since its boot 49 days ago,
although the counter never moved.
Only alert when the series has a sample in the minute before the window
(two minutes for the 60s smartctl scrape). Then increase() has a real
previous value, and still handles counter resets on a monitored host's
reboot. increase_pure() does not help: it assumes counters start at 0
and so counts the whole value as well.
NvmeMediaErrors uses increase() on a counter the same way and would
fire for any drive with past media errors after a gap, so it gets the
same guard. MemoryPressure's rate() is not affected: rate() does not
count the first sample in the window as an increase.
nixpkgs has tranquil-pds 0.6.7 with its frontend and a NixOS module;
this bumps both to the 0.7.0 release. The frontend is passthru.frontend,
for services.tranquil-pds.settings.frontend.dir.
Same delegation and TSIG key as for *.review.lassul.us, so lego can get
the wildcard cert for PDS handles via DNS-01.
pds.lassul.us with handles <name>.pds.lassul.us (wildcard cert via
DNS-01). Login through the zitadel project pds (access = declarative,
only lass@lassul.us), via Tranquil's generic OIDC SSO. Accounts are
created only through the SSO signup, without invite codes; nginx
returns 403 for createAccount, createPasskeyAccount and
createDelegatedAccount (this also blocks migrating accounts in).
SMTP username and SSO_OIDC_ENABLED are set via the environment
because the nixpkgs module validates the config file at build time
without the secrets they require. Mail goes out as send-only
pds@lassul.us, a mailbox on neoprism's mailserver defined next to the
PDS.
Sets the knot's owner DID (lass.pds.lassul.us), which turns the knot on.
knot-rs binds one SSH address, so it stays on [::]:2222 and port 22 on
neoprism's two public addresses is redirected to it (configs/ssh-redirect.nix
only rejects 22 that still reaches sshd). Admin SSH is unchanged.