Clone this repository
For self-hosted knots, clone URLs may differ based on your setup.
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.
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.
flake.lock: update
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.
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.