Capture the catalogue modules' remaining files directories and templates at mesh-catalog bb0fe93b for the moved catalogue tests (issue 1379)

Part 4 of 4 of go/testdata/beside.
This commit is contained in:
2026-10-20 08:20:05 +00:00
parent 013d6231ed
commit 781ba6aeb4
163 changed files with 3255 additions and 0 deletions
@@ -0,0 +1,11 @@
# Managed by the mesh (module asus-zephyrus-g14). Replaced on every push; edit the catalogue instead.
#
# On low battery the machine suspends rather than powering off, at 7 % — s2idle still draws a little,
# so it leaves headroom. A drop-in over the package's own UPower.conf, which stays the package's.
[UPower]
UsePercentageForPolicy=true
PercentageLow=15.0
PercentageCritical=10.0
PercentageAction=7.0
CriticalPowerAction=Suspend
AllowRiskyCriticalPowerAction=true
@@ -0,0 +1,12 @@
# Managed by the mesh (module asus-zephyrus-g14). Replaced on every push; edit the catalogue instead.
#
# The touchpad's settings, applied by X every time the device appears — at login and after every
# resume, when the device is initialised again. This replaces the predecessor's sleep hook, which ran
# xinput after a resume as a named person on a guessed display.
Section "InputClass"
Identifier "asus-zephyrus-g14 touchpad"
MatchIsTouchpad "on"
Option "Tapping" "on"
Option "NaturalScrolling" "true"
Option "AccelSpeed" "0.15"
EndSection
@@ -0,0 +1,7 @@
# The touchpad's settings again after waking, in the operator's session: X applies the module's
# input class when the device appears, and this covers a wake that does not initialise it again.
# Runs as root from the power module; the reset itself runs as the session's owner.
sleep 2
owner=$(ps -o user= -C i3 | head -n 1)
[ -n "$owner" ] && runuser -u "$owner" -- /usr/local/lib/asus-zephyrus-g14/bin/zephyrus-touchpad reset
true
@@ -0,0 +1,30 @@
# The laptop's own lines in i3 (module asus-zephyrus-g14, novox/hq ADR 0208, ADR 0210). Owned by the
# mesh: replaced at every push. Once the controller places contributions to node-display-session
# (ADR 0210), these become the module's contribution instead of a file in i3's directory.
#
# The keys the firmware turns into ordinary key presses. The vendor keys that reach no X client are
# triggerhappy's (/etc/triggerhappy/triggers.d/asus-g14.conf): M4 and Fn+F4/F5 for media, Fn+F7/F8 for
# the panel, Fn+F10 for the touchpad. The keyboard backlight (Fn+F2/F3) is the firmware's and asusd's.
# Fn+F6, the screenshot key: the firmware sends Super+Shift+S. Released before it runs, because the
# screenshot grabs the pointer to select a region, which fails while the key is still held.
bindsym --release $mod+Shift+s exec --no-startup-id $XDG_CONFIG_HOME/i3/scripts/screenshot.sh
# Fn+F9, the display key: the firmware sends Super+P. Odd workspaces to the panel, even ones to the
# external output.
bindsym $mod+p exec --no-startup-id /usr/local/lib/asus-zephyrus-g14/bin/zephyrus-display order
# The keyboard backlight's level, shown when it changes.
exec --no-startup-id /usr/local/lib/asus-zephyrus-g14/bin/zephyrus-kbd-notify
# The laptop's own lines in i3 (module asus-zephyrus-g14, novox/hq ADR 0208, ADR 0210). Owned by the
# mesh: replaced at every push. Once the controller places contributions to node-display-session
# (ADR 0210), these become the module's contribution instead of a file in i3's directory.
#
# The model's panel and touchpad.
# The internal panel is the primary output, where the bars' tray goes. Which monitors are on and where
# is the display server's (autorandr, run at every session start).
exec --no-startup-id /usr/local/lib/asus-zephyrus-g14/bin/zephyrus-display primary
# The touchpad's settings again, by hand. X applies them itself whenever the device appears.
bindsym $mod+Shift+x exec --no-startup-id /usr/local/lib/asus-zephyrus-g14/bin/zephyrus-touchpad reset
@@ -0,0 +1,10 @@
# The ROG Zephyrus G14's vendor keys, which reach no X client: media (the M-keys), panel brightness,
# and the touchpad key. Each runs this module's own script, as the operator's account.
KEY_PROG1 1 /usr/local/lib/asus-zephyrus-g14/bin/zephyrus-session /usr/local/lib/asus-zephyrus-g14/bin/zephyrus-media play-pause
KEY_PROG3 1 /usr/local/lib/asus-zephyrus-g14/bin/zephyrus-session /usr/local/lib/asus-zephyrus-g14/bin/zephyrus-media previous
KEY_PROG4 1 /usr/local/lib/asus-zephyrus-g14/bin/zephyrus-session /usr/local/lib/asus-zephyrus-g14/bin/zephyrus-media next
KEY_BRIGHTNESSDOWN 1 /usr/local/lib/asus-zephyrus-g14/bin/zephyrus-backlight -
KEY_BRIGHTNESSDOWN 2 /usr/local/lib/asus-zephyrus-g14/bin/zephyrus-backlight -
KEY_BRIGHTNESSUP 1 /usr/local/lib/asus-zephyrus-g14/bin/zephyrus-backlight +
KEY_BRIGHTNESSUP 2 /usr/local/lib/asus-zephyrus-g14/bin/zephyrus-backlight +
KEY_F21 1 /usr/local/lib/asus-zephyrus-g14/bin/zephyrus-touchpad reset
@@ -0,0 +1,16 @@
# Managed by the mesh (module asus-zephyrus-g14). Replaced on every push; edit the catalogue instead.
#
# The discrete GPU's driver options on the ROG Zephyrus G14 (GA403, RTX 40 series, hybrid graphics).
#
# NVreg_DynamicPowerManagement=0x00 turns runtime D3 off. With it on, a change of power source sends
# the driver an ACPI notification it fails to handle on this model ("RmHandleDNotifierEvent: Failed to
# handle ACPI D-Notifier event, status=0x62"), and the GPU stops making progress until the machine is
# powered off. Off costs a few idle watts in hybrid mode and keeps the machine up.
#
# NVreg_PreserveVideoMemoryAllocations=1 saves video memory across suspend, so what used the GPU still
# works after waking. It needs nvidia-suspend, -hibernate and -resume to run around a sleep, which this
# module's drop-ins on the sleep services ask for.
#
# A change here applies when the driver next loads: at the next boot.
options nvidia NVreg_PreserveVideoMemoryAllocations=1
options nvidia NVreg_DynamicPowerManagement=0x00
@@ -0,0 +1,6 @@
# Managed by the mesh (module asus-zephyrus-g14). Replaced on every push; edit the catalogue instead.
#
# The ACPI video driver does not change the backlight itself on the brightness keys: on this model it
# moves the wrong one. The keys are triggerhappy's (see /etc/triggerhappy/triggers.d/asus-g14.conf).
# Applies when the module next loads: at the next boot.
options video brightness_switch_enabled=0
@@ -0,0 +1,7 @@
# Managed by the mesh (module asus-zephyrus-g14). Replaced on every push; edit the catalogue instead.
#
# nvidia-powerd (Dynamic Boost) was the first error in the chain that hung this model's GPU on a change
# of power source, and asusd starts it on mains. It runs only when the kernel command line says
# zephyrus.nvidia-powerd — an explicit opt-in, at boot.
[Unit]
ConditionKernelCommandLine=zephyrus.nvidia-powerd
@@ -0,0 +1,7 @@
# Managed by the mesh (module asus-zephyrus-g14). Replaced on every push; edit the catalogue instead.
#
# The NVIDIA driver's own sleep actions, asked for by the sleep itself rather than enabled as
# links: the mesh declares files and never makes links (novox/hq ADR 0012), and the host's service
# shape must not start these units by hand, which would put the GPU to sleep with the machine awake.
[Unit]
Wants=nvidia-hibernate.service nvidia-resume.service
@@ -0,0 +1,7 @@
# Managed by the mesh (module asus-zephyrus-g14). Replaced on every push; edit the catalogue instead.
#
# The NVIDIA driver's own sleep actions, asked for by the sleep itself rather than enabled as
# links: the mesh declares files and never makes links (novox/hq ADR 0012), and the host's service
# shape must not start these units by hand, which would put the GPU to sleep with the machine awake.
[Unit]
Wants=nvidia-suspend-then-hibernate.service nvidia-resume.service
@@ -0,0 +1,7 @@
# Managed by the mesh (module asus-zephyrus-g14). Replaced on every push; edit the catalogue instead.
#
# The NVIDIA driver's own sleep actions, asked for by the sleep itself rather than enabled as
# links: the mesh declares files and never makes links (novox/hq ADR 0012), and the host's service
# shape must not start these units by hand, which would put the GPU to sleep with the machine awake.
[Unit]
Wants=nvidia-suspend.service nvidia-resume.service
@@ -0,0 +1,5 @@
# Managed by the mesh (module asus-zephyrus-g14). Replaced on every push; edit the catalogue instead.
#
# The backlights are writable by the video group, so the brightness keys and the module's brightness tool
# move the panel without root.
ACTION=="add", SUBSYSTEM=="backlight", RUN+="/usr/bin/chgrp video /sys/class/backlight/%k/brightness", RUN+="/usr/bin/chmod g+w /sys/class/backlight/%k/brightness"
@@ -0,0 +1,15 @@
# Written by the mesh (modules/matrix). Conduit reads this file (CONDUIT_CONFIG); nothing comes
# from the environment. server_name is the homeserver's permanent identity: every user id, room id
# and signature in the database carries it, so it is the name this module is served under
# (${bound:route:name-homeserver}) and never changes once a database exists.
[global]
server_name = "${bound:route:name-homeserver}"
database_backend = "rocksdb"
database_path = "/var/lib/matrix-conduit/"
address = "0.0.0.0"
port = 6167
max_request_size = 20000000
allow_registration = false
allow_federation = true
allow_check_for_updates = true
trusted_servers = ["matrix.org"]
@@ -0,0 +1,53 @@
{
"default_server_name": "${bound:route:name-homeserver}",
"default_server_config": {
"m.homeserver": {
"base_url": "https://${bound:route:name-homeserver}"
},
"m.identity_server": {
"base_url": "https://vector.im"
}
},
"brand": "Element",
"integrations_ui_url": "https://scalar.vector.im/",
"integrations_rest_url": "https://scalar.vector.im/api",
"integrations_widgets_urls": [
"https://scalar.vector.im/_matrix/integrations/v1",
"https://scalar.vector.im/api",
"https://scalar-staging.vector.im/_matrix/integrations/v1",
"https://scalar-staging.vector.im/api",
"https://scalar-staging.riot.im/scalar/api"
],
"bug_report_endpoint_url": "https://element.io/bugreports/submit",
"uisi_autorageshake_app": "element-auto-uisi",
"show_labs_settings": true,
"room_directory": {
"servers": [
"${bound:route:name-homeserver}",
"matrix.org",
"gitter.im",
"libera.chat"
]
},
"enable_presence_by_hs_url": {
"https://matrix.org": false,
"https://matrix-client.matrix.org": false
},
"terms_and_conditions_links": [
{
"url": "https://element.io/privacy",
"text": "Privacy Policy"
},
{
"url": "https://element.io/cookie-policy",
"text": "Cookie Policy"
}
],
"features": {
"feature_video_rooms": true,
"feature_rust_crypto": true
},
"element_call": {
"url": "https://call.element.dev"
}
}
@@ -0,0 +1,8 @@
# Managed by the mesh (module memory-pressure). Replaced on every push; edit the catalogue instead.
#
# systemd-oomd kills the worst unit before the kernel's OOM killer freezes the machine: when swap is
# 90 % used, or when a watched unit stalls on memory for 20 s above its limit.
[OOM]
SwapUsedLimit=90%
DefaultMemoryPressureLimit=60%
DefaultMemoryPressureDurationSec=20s
@@ -0,0 +1,5 @@
# Managed by the mesh (module memory-pressure). Replaced on every push; edit the catalogue instead.
#
# systemd-oomd may act on swap exhaustion across the whole machine.
[Slice]
ManagedOOMSwap=kill
@@ -0,0 +1,9 @@
# Managed by the mesh (module memory-pressure). Replaced on every push; edit the catalogue instead.
#
# Swap-in reads one page, not eight: read-ahead exists to amortise a disk's seek, and compressed swap in
# RAM has none — the speculation only costs decompression and memory.
vm.page-cluster = 0
#
# vm.swappiness is deliberately left alone. The usual zram advice (150-180) holds only while the
# compressed swap has room; once it is full, a higher swappiness moves pages to the disk swap, the thing
# being avoided. Raise it only together with zram-size.
@@ -0,0 +1,7 @@
# Managed by the mesh (module memory-pressure). Replaced on every push; edit the catalogue instead.
#
# systemd-oomd may kill the worst unit in a person's service manager when its memory pressure stays
# above 80 % — instead of the kernel freezing the machine.
[Service]
ManagedOOMMemoryPressure=kill
ManagedOOMMemoryPressureLimit=80%
@@ -0,0 +1,11 @@
# Managed by the mesh (module memory-pressure). Replaced on every push; edit the catalogue instead.
#
# Compressed swap in RAM: fast, at a higher priority than any swap on disk, so it takes the everyday
# pressure before anything spills to a disk. Half the RAM, at most 16 GiB, compressed with zstd.
# The swap on disk, its size and whether one exists at all are the machine's, not this module's.
#
# Read by the generator at boot: a change applies at the next boot.
[zram0]
zram-size = min(ram / 2, 16384)
compression-algorithm = zstd
swap-priority = 100
@@ -0,0 +1 @@
postgresql://${bound:postgres-database:as}:${secret:postgres-database}@${bound:postgres-database:at}:${bound:postgres-database:port}/${bound:postgres-database:as}
@@ -0,0 +1,3 @@
# The operator's command (module mesh-cli, novox/hq ADR 0272): nox is mesh-cli, in interactive zsh only.
# A script, sudo and a document meant to work everywhere say mesh-cli.
alias nox=mesh-cli
@@ -0,0 +1,147 @@
{
"first-number": 365,
"owners": [
"hq",
"mesh-catalog",
"mesh-catalog modules/adwaita",
"mesh-catalog modules/artifact-store-tools",
"mesh-catalog modules/asus-zephyrus-g14",
"mesh-catalog modules/audit-logger",
"mesh-catalog modules/avahi",
"mesh-catalog modules/baserow",
"mesh-catalog modules/betterbird",
"mesh-catalog modules/blueman",
"mesh-catalog modules/bluetooth",
"mesh-catalog modules/ca-trust",
"mesh-catalog modules/claude-code",
"mesh-catalog modules/claude-licence-manager",
"mesh-catalog modules/clipmenu",
"mesh-catalog modules/confluence",
"mesh-catalog modules/cups",
"mesh-catalog modules/dbus",
"mesh-catalog modules/desk-channel",
"mesh-catalog modules/disk-load",
"mesh-catalog modules/distribution",
"mesh-catalog modules/dmenu",
"mesh-catalog modules/dnsmasq",
"mesh-catalog modules/docker",
"mesh-catalog modules/docker-compose",
"mesh-catalog modules/dunst",
"mesh-catalog modules/fail2ban",
"mesh-catalog modules/feh",
"mesh-catalog modules/flatpak",
"mesh-catalog modules/fonts",
"mesh-catalog modules/forticlient",
"mesh-catalog modules/gitea",
"mesh-catalog modules/gitlab",
"mesh-catalog modules/gnome-keyring",
"mesh-catalog modules/grafana",
"mesh-catalog modules/hello-web",
"mesh-catalog modules/home-assistant",
"mesh-catalog modules/hostname",
"mesh-catalog modules/i3",
"mesh-catalog modules/i3status-rust",
"mesh-catalog modules/icecast",
"mesh-catalog modules/influxdb",
"mesh-catalog modules/invoicing",
"mesh-catalog modules/jira",
"mesh-catalog modules/keycloak",
"mesh-catalog modules/lab",
"mesh-catalog modules/lemurs",
"mesh-catalog modules/letta",
"mesh-catalog modules/local-model-consumer",
"mesh-catalog modules/localization",
"mesh-catalog modules/logrotate",
"mesh-catalog modules/mailu",
"mesh-catalog modules/marrytts",
"mesh-catalog modules/matrix",
"mesh-catalog modules/memory-pressure",
"mesh-catalog modules/mesh-catalog",
"mesh-catalog modules/mesh-cli",
"mesh-catalog modules/mesh-delivery",
"mesh-catalog modules/mesh-delivery-gate",
"mesh-catalog modules/mesh-issues",
"mesh-catalog modules/mesh-vault",
"mesh-catalog modules/mesh-watcher",
"mesh-catalog modules/messenger",
"mesh-catalog modules/minio",
"mesh-catalog modules/model-usage",
"mesh-catalog modules/mongodb",
"mesh-catalog modules/mosquitto",
"mesh-catalog modules/mounts",
"mesh-catalog modules/mssql",
"mesh-catalog modules/n8n",
"mesh-catalog modules/nats",
"mesh-catalog modules/netcheck",
"mesh-catalog modules/networkmanager",
"mesh-catalog modules/nextcloud",
"mesh-catalog modules/nextcloud-client",
"mesh-catalog modules/nfs-server",
"mesh-catalog modules/nftables",
"mesh-catalog modules/nm-applet",
"mesh-catalog modules/node-env",
"mesh-catalog modules/nodered",
"mesh-catalog modules/ollama",
"mesh-catalog modules/only-office",
"mesh-catalog modules/openai-consumer",
"mesh-catalog modules/openrazer",
"mesh-catalog modules/pacman",
"mesh-catalog modules/photos-eef",
"mesh-catalog modules/photos-filip",
"mesh-catalog modules/picom",
"mesh-catalog modules/polychromatic",
"mesh-catalog modules/postgres",
"mesh-catalog modules/power",
"mesh-catalog modules/powerlevel10k",
"mesh-catalog modules/records",
"mesh-catalog modules/redis",
"mesh-catalog modules/restic",
"mesh-catalog modules/rofi",
"mesh-catalog modules/route-adapter",
"mesh-catalog modules/screen-lock",
"mesh-catalog modules/searxng",
"mesh-catalog modules/sensors",
"mesh-catalog modules/showcase",
"mesh-catalog modules/slack",
"mesh-catalog modules/snapd",
"mesh-catalog modules/ssh-client",
"mesh-catalog modules/sshd",
"mesh-catalog modules/step-ca",
"mesh-catalog modules/sudo",
"mesh-catalog modules/supabase",
"mesh-catalog modules/systemd",
"mesh-catalog modules/systemd-networkd",
"mesh-catalog modules/systemd-resolved",
"mesh-catalog modules/telegram",
"mesh-catalog modules/time-sync",
"mesh-catalog modules/triggerhappy",
"mesh-catalog modules/umami",
"mesh-catalog modules/unifi",
"mesh-catalog modules/website",
"mesh-catalog modules/xclip",
"mesh-catalog modules/xdg",
"mesh-catalog modules/xorg",
"mesh-catalog modules/xterm",
"mesh-catalog modules/zfs",
"mesh-catalog modules/zsh",
"mesh-catalog modules/zsh-autosuggestions",
"mesh-catalog modules/zsh-syntax-highlighting",
"mesh-controller",
"mesh-controller modules/build-agent",
"mesh-controller modules/route-proxy",
"mesh-delivery",
"mesh-host",
"mesh-lab",
"mesh-media-catalog",
"mesh-sdk",
"mesh-tools"
],
"duplicate-check": "on",
"judge-model": "sonnet",
"forge-owner": "novox",
"stuck-after": "2h",
"worker-stale-after": "24h",
"delivered-not-resolved-after": "1h",
"decision-floor": 293,
"research-floor": 45
}
@@ -0,0 +1 @@
postgresql://${bound:postgres-database:as}:${secret:postgres-database}@${bound:postgres-database:at}:${bound:postgres-database:port}/${bound:postgres-database:as}
@@ -0,0 +1,5 @@
{
"judge-model": "sonnet",
"drafting": "on",
"reserved-for": "168h"
}
@@ -0,0 +1,5 @@
{
"client-id": "${bound:oidc-client:as}",
"client-secret": "${secret:oidc-client}",
"issuer": "${bound:oidc-client:issuer}"
}
@@ -0,0 +1,3 @@
{
"operators": []
}
@@ -0,0 +1,3 @@
{
"accounts": {}
}
@@ -0,0 +1 @@
postgresql://${bound:postgres-database:as}:${secret:postgres-database}@${bound:postgres-database:at}:${bound:postgres-database:port}/${bound:postgres-database:as}
@@ -0,0 +1,3 @@
{
"grants": {}
}
@@ -0,0 +1,9 @@
{
"scheme": "${bound:s3-bucket:scheme}",
"at": "${bound:s3-bucket:at}",
"port": "${bound:s3-bucket:port}",
"bucket": "${bound:s3-bucket:bucket}",
"region": "${bound:s3-bucket:region}",
"key": "${bound:s3-bucket:as}",
"secret": "${secret:s3-bucket}"
}
@@ -0,0 +1,3 @@
{
"telegram-chat-id": ""
}
@@ -0,0 +1,4 @@
{
"time-zone": "",
"only-what-needs-me": []
}
@@ -0,0 +1,2 @@
MINIO_ROOT_USER=meshroot
MINIO_BROWSER_REDIRECT_URL=https://${bound:route:name-console}
@@ -0,0 +1 @@
postgresql://${bound:postgres-database:as}:${secret:postgres-database}@${bound:postgres-database:at}:${bound:postgres-database:port}/${bound:postgres-database:as}
@@ -0,0 +1 @@
${secret:root}
@@ -0,0 +1,7 @@
MESH_PROVISION_MQTT=127.0.0.1:${port:1883}
MESH_PROVISION_ADMIN_USER=mesh-admin
MESH_PROVISION_PASSWORD_FILE=${dir:mesh-state}/admin
MESH_DYNSEC_FILE=${dir:data}/dynamic-security.json
MESH_MQTT_CTRL_IMAGE=eclipse-mosquitto@sha256:38c0da4f2ef84284d47b3b3eeea1cb3bdeabe81ee10caf0cd5c5ff61ee3ea408
MESH_MQTT_CTRL_CONTAINER=mosquitto
MESH_MQTT_ADMIN_APPLIED_FILE=${dir:state}/admin.applied
@@ -0,0 +1,20 @@
persistence true
persistence_location /mosquitto/data
log_dest stdout
log_type warning
log_type error
log_type notice
# Every client authenticates; identities and their per-topic ACLs are managed
# at runtime by the dynamic security plugin, whose store the plugin itself owns.
allow_anonymous false
plugin /usr/lib/mosquitto_dynamic_security.so
plugin_opt_config_file /mosquitto/data/dynamic-security.json
# MQTT listener
listener 1883
# MQTT-over-WebSockets listener
listener 8081
protocol websockets
@@ -0,0 +1,11 @@
# Written by the mesh (module mounts, novox/hq ADR 0263) from this machine's assignment: the settings
# shares and sources, and the binding of nfs-share. Replaced on every push; change the settings, not this.
# The module's process reads it as root every 30 seconds and writes one .mount and .automount per entry.
shares=${config:shares}
sources=${config:sources}
server=${bound:nfs-share:from}
at=${bound:nfs-share:at}
account=${machine:account}
node=${machine:name}
passwords=${dir:state}
state=${dir:state}
@@ -0,0 +1,14 @@
SET NOCOUNT ON;
DECLARE @n sysname, @s nvarchar(max);
DECLARE c CURSOR LOCAL FAST_FORWARD FOR
SELECT name FROM sys.databases WHERE database_id > 4 AND state = 0 AND source_database_id IS NULL;
OPEN c;
FETCH NEXT FROM c INTO @n;
WHILE @@FETCH_STATUS = 0
BEGIN
SET @s = N'BACKUP DATABASE ' + QUOTENAME(@n) + N' TO DISK = N''/var/opt/mssql/backup/' + REPLACE(@n, N'''', N'''''') + N'.bak'' WITH INIT, COPY_ONLY, CHECKSUM';
EXEC (@s);
FETCH NEXT FROM c INTO @n;
END
CLOSE c;
DEALLOCATE c;
@@ -0,0 +1,2 @@
ACCEPT_EULA=Y
MSSQL_SA_PASSWORD=${secret:sa}
@@ -0,0 +1 @@
${secret:postgres-database}
@@ -0,0 +1,12 @@
N8N_HOST=${bound:route:name}
N8N_PORT=5678
N8N_PROTOCOL=https
WEBHOOK_URL=https://${bound:route:name}/
NODE_FUNCTION_ALLOW_BUILTIN=*
NODE_FUNCTION_ALLOW_EXTERNAL=*
DB_TYPE=postgresdb
DB_POSTGRESDB_HOST=${bound:postgres-database:at}
DB_POSTGRESDB_PORT=${bound:postgres-database:port}
DB_POSTGRESDB_DATABASE=${bound:postgres-database:as}
DB_POSTGRESDB_USER=${bound:postgres-database:as}
DB_POSTGRESDB_PASSWORD_FILE=/run/secrets/database
@@ -0,0 +1,46 @@
# The nats module's own server settings. Declared by the module, because a port, a TLS path
# and a store directory are properties of the container this module raises: they live in its
# image and its mounts and change when it does.
#
# The mesh writes accounts.conf beside this one and nothing else. A controller that wrote the
# whole file would have to be kept in step with a Dockerfile it never sees.
port: 4222
# Monitoring on every interface *inside* the container, so the publish below can reach it; the publish
# is 127.0.0.1:8222 on the machine, so nothing beyond the machine reaches it, and the module guards 8222
# too. Bound to the container's own loopback until 2026-10-04, the published port answered nothing
# (connection reset), and the only way in was `docker exec`.
http: 0.0.0.0:8222
# The mesh's own broker certificate — the one every machine already pins by fingerprint and the
# controller already trusts (MESH_BROKER_CERTIFICATE). Serving the new bus with it means no
# machine's pin changes when it moves, and no second certificate exists to be wrong about.
tls {
cert_file: "/tls/tls.crt"
key_file: "/tls/tls.key"
}
# **No `verify`, deliberately, and it was `verify: true` until a probe ran this image.** That
# setting makes the server demand a *client* certificate, and nothing in the mesh presents one: a
# host pins this server's exact certificate and authenticates with the password the mesh minted
# (novox/hq ADR 0004, design 25 §4), and so does a module's runtime. With it on, every connection
# in the mesh is refused at the TLS handshake, before any password is looked at — and the error is
# "client didn't provide a certificate", which reads as a client fault.
#
# TLS is still required: a tls block is what makes it required, and verify only decides whether
# client certificates are checked. What is given up is a second factor the mesh has no machinery
# to issue or rotate — a certificate per module per node — and what is kept is stronger than a
# name check in both directions: an exact pin outward, a per-user password inward.
jetstream {
store_dir: "/data"
}
# Every user of the mesh, composed by the controller and rewritten whenever a module is
# assigned, a node enrols or a person's access changes.
#
# **Relative, and in this same directory, because it has to be.** An absolute include path is
# resolved relative to the including file's directory, not from the root: nats-server given
# `include /etc/nats/accounts.conf` from /etc/nats-server/nats.conf looks for
# /etc/nats-server/etc/nats/accounts.conf and refuses to start. Verified against the server.
include accounts.conf
@@ -0,0 +1,63 @@
# Managed by the mesh (module networkmanager). Replaced on every push; edit the
# catalogue instead.
#
# This machine's uplink is NetworkManager's, and this file is the whole of what
# the mesh asks of it (novox/hq ADR 0117): leave the resolver file to the mesh,
# and leave the private network's interface alone. Nothing more. The mesh never
# declares a connection profile, an address, a route, a wireless network or its
# credentials — those are joined at the machine, by the person using it, and
# the link they make is the only channel the mesh reaches this machine over. A
# push that got a link wrong could not be undone by the next one.
#
# A drop-in of the mesh's own, beside NetworkManager.conf and whatever else the
# operator keeps in this directory. NetworkManager reads the files here sorted by
# name and a later one wins a key it sets again — so a file of the operator's
# that sorts after this one (any name starting with a letter does) and sets dns=
# or unmanaged-devices= overrides it. That is the operator's to decide, and the
# reason this file sets nothing but the two keys it must.
#
# NetworkManager itself is the machine's: the mesh never starts, stops, enables
# or disables it (its service is declared with no state), because stopping it
# takes every link down, this machine's channel to the mesh included — and a
# module unassigned by mistake must not be able to do that. When this file
# changes, a running NetworkManager is reloaded (its D-Bus Reload call, which
# re-reads its configuration — NetworkManager(8)), never restarted.
[main]
# The resolver file is the mesh's: this module writes /etc/resolv.conf itself,
# listing the mesh's resolvers (novox/hq ADR 0223). Without this line
# NetworkManager rewrites that file on
# every connectivity change — every network joined, every lease renewed —
# and the mesh's resolver is silently replaced while every surface of the mesh
# still reads green. none: "NetworkManager will not modify resolv.conf. This
# implies rc-manager unmanaged" (NetworkManager.conf(5), 1.58). On an adopted
# machine the predecessor wrote the same line in a file of its own; both say one
# thing, and the predecessor's is retired by hand after the take.
#
# Not NetworkManager's own global DNS ([global-dns-domain-*] with rc-manager=file):
# it writes its own header and composes the options line itself, so it cannot
# write the mesh's file byte for byte, and the three uplink modules would write
# three different files for one fact. dns=none, and the file declared beside it.
dns=none
[keyfile]
# mesh0 is the private network's interface: the mesh brings it up and the mesh
# alone configures it. A manager that considers every interface its own could
# try to configure it, or tear it down on a profile change.
#
# unmanaged-devices rather than a [device-mesh0] section with managed=0, because
# NetworkManager.conf(5) says a device unmanaged by this key "is strictly
# unmanaged and cannot be overruled by using the API like nmcli device set
# $IFNAME managed yes", while device*.managed "can be overruled at runtime via
# D-Bus". The same page adds that device*.managed "may be a better choice" for
# exactly those reasons — for an interface the operator might want to hand back
# at runtime. For the mesh's own interface, strict is the point.
#
# += rather than =: the same page documents appending to a list-valued key set
# earlier ("plugins+=another-plugin") as an extension of its key file format,
# and unmanaged-devices is a device list. = would replace whatever devices the
# operator already keeps NetworkManager away from; += adds this one to them
# (novox/hq ADR 0102: a list is added to, never replaced). A file of the
# operator's read after this one that sets the key with = replaces it again;
# that is the operator's to decide.
unmanaged-devices+=interface-name:mesh0
@@ -0,0 +1 @@
{}
@@ -0,0 +1,14 @@
POSTGRES_HOST=${bound:postgres-database:at}:${bound:postgres-database:port}
POSTGRES_DB=${bound:postgres-database:as}
POSTGRES_USER=${bound:postgres-database:as}
POSTGRES_PASSWORD=${secret:postgres-database}
NEXTCLOUD_ADMIN_USER=mesh-admin
NEXTCLOUD_ADMIN_PASSWORD=${secret:admin}
OBJECTSTORE_S3_HOST=${bound:s3-bucket:at}
OBJECTSTORE_S3_PORT=${bound:s3-bucket:port}
OBJECTSTORE_S3_BUCKET=${bound:s3-bucket:bucket}
OBJECTSTORE_S3_KEY=${bound:s3-bucket:as}
OBJECTSTORE_S3_SECRET=${secret:s3-bucket}
OBJECTSTORE_S3_SSL=false
OBJECTSTORE_S3_USEPATH_STYLE=true
OBJECTSTORE_S3_REGION=${bound:s3-bucket:region}
@@ -0,0 +1,12 @@
# Written by the mesh (module nfs-server, novox/hq ADR 0263). Replaced on every push; a drop-in of
# the operator's that sorts after this one overrides it, and is theirs.
#
# NFS version 4 only: a client needs port 2049 and nothing else, so the module opens nothing more
# than that, to the private network. Version 3 needs rpcbind and mountd, on ports the mesh does not open.
[nfsd]
vers2=n
vers3=n
vers4=y
vers4.0=n
vers4.1=y
vers4.2=y
@@ -0,0 +1,10 @@
# Written by the mesh (module nfs-server, novox/hq ADR 0263) from this machine's assignment.
# Replaced on every push; change the `shares` setting, never this file.
#
# The shares: name=folder, or name=folder:ro, one share per folder. The grants: share=node:rw|ro[,…],
# which nodes may mount each and how. The module's process exports a share to a node's private address
# only when it is granted here and that node's mounts module wants it, every client mapped to the
# folder's owner, and only to addresses inside the range below.
shares=${config:shares}
grants=${config:grants}
range=${machine:mesh-range}
@@ -0,0 +1,14 @@
[Unit]
Description=The mesh's packet filter, derived from what is assigned to this node
Wants=network-pre.target
Before=network-pre.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=nft -f /etc/nftables.conf
ExecReload=nft -f /etc/nftables.conf
ExecStop=nft delete table inet mesh
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,5 @@
# The mesh: stopping the stock unit deletes only the mesh's table, never the whole ruleset
# (novox/hq ADR 0100) — a flush would take the container runtime's rules and any firewall with it.
[Service]
ExecStop=
ExecStop=nft delete table inet mesh
@@ -0,0 +1,4 @@
# The operator account's environment for its service manager and graphical session, generated by
# the mesh (module node-env, novox/hq ADR 0203). Do not edit: this file is replaced at every push.
# The same facts as ~/.config/mesh/environment.sh, in environment.d(5) syntax.
${collected:environment:systemd}
@@ -0,0 +1,5 @@
# The operator account's environment, generated by the mesh (module node-env, novox/hq ADR 0203).
# Do not edit: this file is replaced at every push. Every line names the module that contributed it.
# Sourced by the login shell from its always-read startup file (for zsh, ~/.zshenv), so a script, a
# login and the shell's execute verb all see it. Your own variables belong in your shell's own lines.
${collected:environment:posix}
@@ -0,0 +1,3 @@
{
"token": "${secret:api-token}"
}
@@ -0,0 +1,4 @@
MESH_NODERED_URL=http://127.0.0.1:${port:1880}
MESH_NODERED_CONFIG_FILE=${dir:mesh-state}/config.json
MESH_PROVISIONS_DIR=${dir:state}
MESH_WRITTEN_DIR=${dir:written}
@@ -0,0 +1,48 @@
// Node-RED's settings, written by the mesh from the nodered module. What an assignment may change
// is settings.json beside this file (merged key by key); the credentials are the mesh's secrets and
// reach Node-RED only through this file. The flows' own credentials stay encrypted in the user
// directory under the key Node-RED keeps there (.config.runtime.json), which is data, not this.
const fs = require("fs");
const path = require("path");
const crypto = require("crypto");
const ADMIN_PASSWORD = "${secret:admin}";
const API_TOKEN = "${secret:api-token}";
const ADMIN = { username: "admin", permissions: "*" };
const settings = JSON.parse(fs.readFileSync(path.join(__dirname, "settings.json"), "utf8"));
// The mesh's keys, not Node-RED's: endpoints lands in every merged file; timeZone is the
// assignment's way to set the zone flows schedule and format in; mqtt names the broker nodes the
// module's MQTT step keeps pointed at the mesh's broker; topics is what nodered asks the broker for.
if (settings.timeZone) process.env.TZ = settings.timeZone;
delete settings.timeZone;
delete settings.endpoints;
delete settings.mqtt;
delete settings.topics;
function same(a, b) {
const x = crypto.createHash("sha256").update(String(a)).digest();
const y = crypto.createHash("sha256").update(String(b)).digest();
return crypto.timingSafeEqual(x, y);
}
// The admin secret is a password, or, accepted from an existing install, the bcrypt hash its
// settings held, so the password people already use keeps working.
function passwordMatches(given) {
if (/^\$2[aby]\$\d\d\$/.test(ADMIN_PASSWORD)) return require("bcryptjs").compare(String(given), ADMIN_PASSWORD);
return Promise.resolve(same(given, ADMIN_PASSWORD));
}
module.exports = Object.assign(settings, {
uiPort: 1880,
adminAuth: {
type: "credentials",
users: (username) => Promise.resolve(username === ADMIN.username ? ADMIN : null),
authenticate: (username, password) =>
username === ADMIN.username
? passwordMatches(password).then((ok) => (ok ? ADMIN : null))
: Promise.resolve(null),
// The module's own tools call the admin API with this bearer token.
tokens: (token) => Promise.resolve(same(token, API_TOKEN) ? { username: "mesh", permissions: "*" } : null),
},
});
@@ -0,0 +1,14 @@
{
"flowFile": "flows.json",
"flowFilePretty": true,
"diagnostics": { "enabled": true, "ui": true },
"runtimeState": { "enabled": false, "ui": false },
"logging": { "console": { "level": "info", "metrics": false, "audit": false } },
"exportGlobalContextKeys": false,
"externalModules": {},
"editorTheme": { "projects": { "enabled": false } },
"functionExternalModules": true,
"debugMaxLength": 1000,
"mqttReconnectTime": 15000,
"serialReconnectTime": 15000
}
@@ -0,0 +1,5 @@
JWT_ENABLED=true
JWT_SECRET=${secret:jwt}
JWT_HEADER=Authorization
JWT_IN_BODY=true
ALLOW_PRIVATE_IP_ADDRESS=true
@@ -0,0 +1,29 @@
# The mesh's (module pacman, novox/hq to-be 42): the package manager's configuration. Written
# whole at every push: an edit here is overwritten, and the file a machine had before is kept once by
# the host. Owned whole because [options] cannot take a block by appending: anything added at the end
# of the file lands in the last repository's section.
#
# The repositories are the union of what the machines had enabled when the module was written
# (core, extra, multilib). The options are the distribution's defaults with four more: colour on a
# terminal, parallel downloads, package lists in columns, and downloads run as the unprivileged
# alpm user, which pacman 7 creates.
[options]
HoldPkg = pacman glibc
Architecture = auto
CheckSpace
Color
VerbosePkgLists
ParallelDownloads = 5
DownloadUser = alpm
SigLevel = Required DatabaseOptional
LocalFileSigLevel = Optional
[core]
Include = /etc/pacman.d/mirrorlist
[extra]
Include = /etc/pacman.d/mirrorlist
[multilib]
Include = /etc/pacman.d/mirrorlist
@@ -0,0 +1,8 @@
# The mesh's (module pacman, novox/hq to-be 42): how reflector refreshes the mirror list, weekly,
# through reflector.timer. Written whole at every push. Before the module, every machine's list was
# generated once and never again.
--save /etc/pacman.d/mirrorlist
--protocol https
--country Belgium,Netherlands,Luxembourg,Germany,France
--latest 20
--sort rate
@@ -0,0 +1,38 @@
# picom, the X compositor (module picom, novox/hq ADR 0208). Owned by the mesh: this file is
# replaced at every push. Adopted from the two workstations' file of 2026-10-04 and moved to
# picom's window rules, which supersede opacity-rule, inactive-opacity and inactive-dim.
# The backend and damage tracking are each machine's settings (backend, use-damage; novox/hq research
# 037, issue 345): the right ones depend on the machine's graphics driver. The defaults are what every
# machine ran before: glx, and picom's own default of repainting only what changed. xrender, or
# use-damage false (repaint the whole screen each frame), is what to try on a machine where a window is
# left unpainted or stale.
backend = "${config:backend}";
use-damage = ${config:use-damage};
vsync = true;
fading = true;
fade-in-step = 0.05;
fade-out-step = 0.05;
# Tiled windows hide their shadows and corners, so neither is drawn.
shadow = false;
corner-radius = 0;
blur-method = "none";
# "Focused" is i3's _NET_ACTIVE_WINDOW, not X focus events: under i3 those do not reliably reach
# the toplevel picom tracks, and a terminal then never lifts to its focused opacity.
use-ewmh-active-win = true;
# Window rules are applied in order, and a later match wins. Only terminals are translucent:
# nothing else on screen (browsers, video, games) is touched. A terminal is matched by its class;
# the node's terminal emulator today is xterm (class XTerm). A window's own opacity property is
# used wherever no rule sets one, which is what the window-opacity tool sets.
rules = (
{ match = "window_type = 'tooltip'"; fade = false; opacity = 0.95; },
{ match = "window_type = 'dock' || window_type = 'desktop'"; fade = false; },
{ match = "class_g = 'XTerm' && focused"; opacity = 0.95; },
{ match = "class_g = 'XTerm' && !focused"; opacity = 0.75; },
# A full-screen window is opaque, a terminal included: a video or a game is never see-through.
{ match = "fullscreen"; opacity = 1; }
);
@@ -0,0 +1,6 @@
# The Razer peripherals' tray (module polychromatic, novox/hq ADR 0208, ADR 0212). Owned by the mesh:
# replaced at every push. This line is the tray's one start, at the session's start (exec, not
# exec_always, so a reload starts nothing). The package's own login helper starts it too while the
# application's setting "Start the tray applet when I log on" is ticked; polychromatic_check names
# that as a second start.
exec --no-startup-id polychromatic-tray-applet
@@ -0,0 +1 @@
${consumers:postgres-database}
@@ -0,0 +1,90 @@
#!/bin/sh
# power-moment MOMENT [--force] — run every module's code for one power moment (novox/hq ADR 0211).
# Shipped by the mesh's power module; edit the catalogue.
#
# MOMENT is after-boot, before-sleep, after-wake, before-shutdown, on-mains or on-battery, or
# `supply`, which reads the machine's power source and runs on-mains or on-battery once per change.
#
# The moment's file holds what the controller placed: each module's code under a line `# <module>`.
# Each module's piece runs on its own, with sh, as root, bounded by MESH_POWER_BOUND seconds
# (default 30) and killed 5 s after that, so one module that hangs cannot hold the machine awake or
# stop the next module. Every outcome goes to the journal under the tag mesh-power.
set -u
moment=${1:-}
force=${2:-}
moments=/etc/mesh-power/moments
bound=${MESH_POWER_BOUND:-30}
runtime=/run/mesh-power
say() { logger -t mesh-power -- "$*" 2>/dev/null || true; printf '%s\n' "$*"; }
# The power source as the kernel says it: on mains when any Mains supply is online. A machine with
# no Mains supply (a desktop, a server, a USB-C source only) has no source to change.
source_now() {
found=no
for s in /sys/class/power_supply/*; do
[ "$(cat "$s/type" 2>/dev/null)" = Mains ] || continue
found=yes
[ "$(cat "$s/online" 2>/dev/null)" = 1 ] && { echo on-mains; return; }
done
[ "$found" = yes ] && echo on-battery || echo none
}
case "$moment" in
supply)
now=$(source_now)
[ "$now" = none ] && exit 0
mkdir -p "$runtime"
last=$(cat "$runtime/source" 2>/dev/null || true)
[ "$now" = "$last" ] && [ "$force" != --force ] && exit 0
echo "$now" >"$runtime/source"
moment=$now
;;
after-boot | before-sleep | after-wake | on-mains | on-battery) ;;
before-shutdown)
# Run only while the machine is really going down. The unit that runs it is stopped at shutdown,
# and also when the module is taken off the machine; the second must not run anyone's shutdown code.
state=$(systemctl is-system-running 2>/dev/null || true)
if [ "$state" != stopping ] && [ "$force" != --force ]; then
say "moment=before-shutdown skipped: the system is $state, not stopping"
exit 0
fi
;;
*)
echo "power-moment: no moment $moment (after-boot, before-sleep, after-wake, before-shutdown, on-mains, on-battery, supply)" >&2
exit 2
;;
esac
file="$moments/$moment"
[ -r "$file" ] || { say "moment=$moment: nothing placed"; exit 0; }
work=$(mktemp -d) || exit 1
trap 'rm -rf "$work"' EXIT
# One file per module piece, in the order the controller placed them. A line that is exactly
# "# <module name>" opens a piece; lines before the first are the holder's own header.
awk -v dir="$work" '
/^# [a-z0-9][a-z0-9-]*$/ { n++; name = substr($0, 3); out = sprintf("%s/%03d-%s", dir, n, name); next }
n > 0 { print > out }
' "$file"
ran=0 failed=0
for piece in "$work"/*; do
[ -e "$piece" ] || continue
module=${piece##*/}
module=${module#*-}
start=$(date +%s)
timeout --kill-after=5 "$bound" sh "$piece" </dev/null >"$piece.out" 2>&1
rc=$?
took=$(($(date +%s) - start))
ran=$((ran + 1))
case $rc in
0) say "moment=$moment module=$module ok in ${took}s" ;;
124 | 137) failed=$((failed + 1)); say "moment=$moment module=$module ended after ${bound}s: it did not finish" ;;
*) failed=$((failed + 1)); say "moment=$moment module=$module exited $rc: $(tail -n 3 "$piece.out" | tr '\n' ' ')" ;;
esac
done
say "moment=$moment: $ran piece(s), $failed failed"
exit 0
@@ -0,0 +1,9 @@
[[block]]
block = "battery"
format = "$icon $percentage{ $time|}"
charging_format = "$icon $percentage{ $time|}"
not_charging_format = "$icon $percentage"
full_format = "$icon $percentage"
empty_format = "$icon $percentage"
missing_format = ""
@@ -0,0 +1,6 @@
[[block]]
block = "custom"
command = "~/.local/bin/i3status-value power.draw ${machine:name}"
interval = 5
hide_when_empty = true
@@ -0,0 +1,4 @@
# What every module on this machine runs at the moment after-boot, as the mesh placed it (module power,
# novox/hq ADR 0211). Replaced on every push: code is added by the module it belongs to, as a
# contribution for this moment. Each module's piece runs on its own, as root, bounded in time.
${shell:after-boot:first}${shell:after-boot:normal}${shell:after-boot:last}
@@ -0,0 +1,4 @@
# What every module on this machine runs at the moment after-wake, as the mesh placed it (module power,
# novox/hq ADR 0211). Replaced on every push: code is added by the module it belongs to, as a
# contribution for this moment. Each module's piece runs on its own, as root, bounded in time.
${shell:after-wake:first}${shell:after-wake:normal}${shell:after-wake:last}
@@ -0,0 +1,4 @@
# What every module on this machine runs at the moment before-shutdown, as the mesh placed it (module power,
# novox/hq ADR 0211). Replaced on every push: code is added by the module it belongs to, as a
# contribution for this moment. Each module's piece runs on its own, as root, bounded in time.
${shell:before-shutdown:first}${shell:before-shutdown:normal}${shell:before-shutdown:last}
@@ -0,0 +1,4 @@
# What every module on this machine runs at the moment before-sleep, as the mesh placed it (module power,
# novox/hq ADR 0211). Replaced on every push: code is added by the module it belongs to, as a
# contribution for this moment. Each module's piece runs on its own, as root, bounded in time.
${shell:before-sleep:first}${shell:before-sleep:normal}${shell:before-sleep:last}
@@ -0,0 +1,4 @@
# What every module on this machine runs at the moment on-battery, as the mesh placed it (module power,
# novox/hq ADR 0211). Replaced on every push: code is added by the module it belongs to, as a
# contribution for this moment. Each module's piece runs on its own, as root, bounded in time.
${shell:on-battery:first}${shell:on-battery:normal}${shell:on-battery:last}
@@ -0,0 +1,4 @@
# What every module on this machine runs at the moment on-mains, as the mesh placed it (module power,
# novox/hq ADR 0211). Replaced on every push: code is added by the module it belongs to, as a
# contribution for this moment. Each module's piece runs on its own, as root, bounded in time.
${shell:on-mains:first}${shell:on-mains:normal}${shell:on-mains:last}
@@ -0,0 +1,8 @@
# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.
#
# The power key and the lid, from this module's settings: the mesh's, then this machine's.
[Login]
HandlePowerKey=${config:handle-power-key}
HandleLidSwitch=${config:handle-lid-switch}
HandleLidSwitchExternalPower=${config:handle-lid-switch-external-power}
HandleLidSwitchDocked=${config:handle-lid-switch-docked}
@@ -0,0 +1,3 @@
# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.
[Unit]
Wants=mesh-power-after-wake.service
@@ -0,0 +1,3 @@
# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.
[Unit]
Wants=mesh-power-after-wake.service
@@ -0,0 +1,13 @@
# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.
[Unit]
Description=Every module's code after boot (mesh power)
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/lib/mesh-power/bin/power-moment after-boot
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,11 @@
# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.
#
# Pulled in by the four sleep targets through this module's drop-ins on them, and ordered after
# them, so it runs once the machine is awake again. Never enabled.
[Unit]
Description=Every module's code after waking (mesh power)
After=suspend.target hibernate.target hybrid-sleep.target suspend-then-hibernate.target
[Service]
Type=oneshot
ExecStart=/usr/local/lib/mesh-power/bin/power-moment after-wake
@@ -0,0 +1,18 @@
# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.
#
# Started at boot and left active; stopping it is the shutdown. Ordered after the network, so at
# shutdown it stops, and runs every module's code, while the network is still up.
[Unit]
Description=Every module's code before shutdown (mesh power)
Wants=network-online.target
After=network-online.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/true
ExecStop=/usr/local/lib/mesh-power/bin/power-moment before-shutdown
TimeoutStopSec=120
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,11 @@
# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.
#
# Pulled in by sleep.target through this module's drop-in on it, never enabled: a one-shot waiting
# for a sleep is not a service whose state the mesh can declare.
[Unit]
Description=Every module's code before sleep (mesh power)
Before=sleep.target
[Service]
Type=oneshot
ExecStart=/usr/local/lib/mesh-power/bin/power-moment before-sleep
@@ -0,0 +1,10 @@
# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.
#
# Started by the module's udev rule when a power supply changes; runs on-mains or on-battery once per
# change of source.
[Unit]
Description=Every module's code for the power source (mesh power)
[Service]
Type=oneshot
ExecStart=/usr/local/lib/mesh-power/bin/power-moment supply
@@ -0,0 +1,3 @@
# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.
[Unit]
Wants=mesh-power-before-sleep.service
@@ -0,0 +1,3 @@
# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.
[Unit]
Wants=mesh-power-after-wake.service
@@ -0,0 +1,3 @@
# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.
[Unit]
Wants=mesh-power-after-wake.service
@@ -0,0 +1,5 @@
# Written by the mesh (module power, novox/hq ADR 0211). Replaced on every push.
# Only the charger's change can switch the machine between mains and battery. A battery reports its
# level many times a minute, and USB-C supplies come and go; matching them started the supply unit
# every second or two on the laptop.
SUBSYSTEM=="power_supply", ACTION=="change", ATTR{type}=="Mains", RUN+="/usr/bin/systemctl --no-block start mesh-power-supply.service"
@@ -0,0 +1,3 @@
# The prompt: powerlevel10k v1.20.0, pinned in this module (novox/hq ADR 0205), and its configuration.
[[ ! -f ~/.local/share/powerlevel10k/powerlevel10k.zsh-theme ]] || source ~/.local/share/powerlevel10k/powerlevel10k.zsh-theme
[[ ! -f ~/.config/powerlevel10k/p10k.zsh ]] || source ~/.config/powerlevel10k/p10k.zsh
@@ -0,0 +1 @@
{}
@@ -0,0 +1 @@
${bound:git:scheme}://${bound:git:at}:${bound:git:port}
@@ -0,0 +1,3 @@
requirepass ${secret:secret}
appendonly yes
dir /data
@@ -0,0 +1,2 @@
# What the modules on this machine back up, composed by the mesh (novox/hq to-be 43). Do not edit.
${backup:lines}
@@ -0,0 +1,2 @@
# The data the modules on this machine declare, composed by the mesh (novox/hq ADR 0233). Do not edit.
${backup:data}
@@ -0,0 +1,6 @@
# How many restore points the night keeps, written by the mesh from this machine's settings keep-daily,
# keep-weekly, keep-monthly (novox/hq issue 416) and keep-hand-taken-days (novox/hq issue 453). Replaced on every push; change the settings, never this file.
daily=${config:keep-daily}
weekly=${config:keep-weekly}
monthly=${config:keep-monthly}
hand=${config:keep-hand-taken-days}
@@ -0,0 +1,26 @@
#!/bin/sh
# dmenu, as this node's launcher answers it (module rofi, novox/hq ADR 0208). The node-launcher
# seat's dmenu-compatible command: choices on standard input, the chosen one on standard output,
# exit 1 when nothing was chosen. A script calls `dmenu` and works whichever module holds the seat.
#
# rofi understands dmenu's -p, -l and -i. dmenu's look options (-fn, -nb, -nf, -sb, -sf, -m, -w)
# take a value rofi would misread, so they are dropped with it; -b and -f are dropped alone. The
# look is the launcher's theme.
set -u
n=$#
while [ "$n" -gt 0 ]; do
arg=$1
shift
n=$((n - 1))
case $arg in
-fn | -nb | -nf | -sb | -sf | -m | -w)
if [ "$n" -gt 0 ]; then
shift
n=$((n - 1))
fi
;;
-b | -f) ;;
*) set -- "$@" "$arg" ;;
esac
done
exec rofi -dmenu "$@"
@@ -0,0 +1,39 @@
#!/bin/sh
# rofi-launch drun|run|window|filebrowser|sudo (module rofi, novox/hq ADR 0208): what the launcher's
# key bindings run.
#
# After a resume the keyboard grab can fail for a moment, and rofi then exits at once: a key press
# that opens nothing. So a failure within half a second is tried again, up to five times. A person
# closing the menu takes longer than that, and is never shown it a second time.
set -u
mode=${1:-drun}
now_ms() { date +%s%3N; }
attempt() {
i=0
while [ "$i" -lt 5 ]; do
start=$(now_ms)
"$@" && return 0
[ $(($(now_ms) - start)) -ge 500 ] && return 1
i=$((i + 1))
sleep 0.1
done
return 1
}
case $mode in
drun | run | window | filebrowser)
attempt rofi -show "$mode"
;;
sudo)
# A command line, run with sudo in the node's terminal.
command=$(attempt rofi -dmenu -p sudo </dev/null) || exit 0
[ -n "$command" ] || exit 0
exec rofi-sensible-terminal -e sudo sh -c "$command"
;;
*)
echo "rofi-launch: no mode $mode (drun, run, window, filebrowser, sudo)" >&2
exit 2
;;
esac
@@ -0,0 +1,54 @@
#!/usr/bin/env bash
# rofi-powermenu (module rofi, novox/hq ADR 0208): lock, suspend, log out, reboot, shut down.
#
# It belongs to the session, and it is the launcher's: its whole face is rofi's. Every action is a
# verb of logind or the service manager, so nothing here names a window manager or a locker:
# - lock asks logind to lock the session, which the holder of node-lock-screen answers;
# - log out ends this login session, whichever window manager runs in it.
# Adopted from the workstations' power menu of 2026-10-04 (after adi1090x's powermenu type-2).
set -u
theme=mesh-powermenu
uptime="$(uptime -p | sed -e 's/^up //')"
shutdown='󰤂'
reboot='󰜉'
lock='󰌾'
suspend='󰤄'
logout='󰍃'
yes='󰄲'
no='󰅖'
menu() {
rofi -dmenu -p "Uptime: $uptime" -mesg "Uptime: $uptime" -theme "$theme"
}
confirm() {
printf '%s\n%s\n' "$yes" "$no" | rofi -dmenu -p 'Confirmation' -mesg 'Are you sure?' -theme "$theme" \
-theme-str 'window {location: center; anchor: center; fullscreen: false; width: 350px;}' \
-theme-str 'mainbox {children: [ "message", "listview" ];}' \
-theme-str 'listview {columns: 2; lines: 1;}' \
-theme-str 'element-text {horizontal-align: 0.5;}' \
-theme-str 'textbox {horizontal-align: 0.5;}'
}
session() {
# The login session this menu runs in: the session's own id, else logind's answer for this process.
if [ -n "${XDG_SESSION_ID:-}" ]; then
echo "$XDG_SESSION_ID"
else
loginctl show-session auto -p Id --value 2>/dev/null
fi
}
confirmed() { [ "$(confirm)" = "$yes" ]; }
chosen="$(printf '%s\n' "$lock" "$suspend" "$logout" "$reboot" "$shutdown" | menu)" || exit 0
case $chosen in
"$lock") loginctl lock-session "$(session)" ;;
"$suspend") confirmed && systemctl suspend ;;
"$logout") confirmed && loginctl terminate-session "$(session)" ;;
"$reboot") confirmed && systemctl reboot ;;
"$shutdown") confirmed && systemctl poweroff ;;
esac
exit 0
@@ -0,0 +1,54 @@
/* rofi, the launcher (module rofi, novox/hq ADR 0208). Owned by the mesh: this file is replaced at
* every push. Adopted from the two workstations' file of 2026-10-04: the settings that differ from
* rofi's defaults, nothing else. Its look is the theme "mesh", in themes/ beside this file. */
configuration {
modi: "drun,run,window,filebrowser";
show-icons: true;
case-sensitive: false;
normalize-match: true;
matching: "normal";
tokenize: true;
cycle: true;
steal-focus: false;
click-to-exit: true;
sort: false;
/* Applications: the user's and the system's desktop entries, read fresh each time so a
* program installed a minute ago is there. */
drun-match-fields: "name,generic,exec,categories,keywords";
drun-display-format: "{name} [<span weight='light' size='small'><i>({generic})</i></span>]";
drun-show-actions: false;
drun-url-launcher: "xdg-open";
drun-use-desktop-cache: false;
drun-reload-desktop-cache: false;
run,drun {
fallback-icon: "application-x-addon";
}
/* A command run from `run` opens in the node's terminal when it asks for one. */
terminal: "rofi-sensible-terminal";
run-shell-command: "{terminal} -e {cmd}";
ssh-command: "{terminal} -e {ssh-client} {host} [-p {port}]";
parse-hosts: true;
parse-known-hosts: true;
window-match-fields: "title,class,role,name,desktop";
window-format: "{w} - {c} - {t:0}";
disable-history: false;
max-history-size: 25;
filebrowser {
directories-first: true;
sorting-method: "name";
}
display-window: "Windows";
display-run: "Run";
display-ssh: "SSH";
display-drun: "Apps";
display-filebrowser: "Files";
}
@theme "mesh"
@@ -0,0 +1,8 @@
# The launcher's key bindings (module rofi, novox/hq ADR 0208). Owned by the mesh: replaced at every
# push. i3 reads this file through its configuration's `include ~/.config/i3/config.d/*.conf`, after
# the variables it sets, so $mod is i3's.
bindsym $mod+d exec --no-startup-id ~/.local/bin/rofi-launch drun
bindsym $mod+t exec --no-startup-id ~/.local/bin/rofi-launch run
bindsym $mod+Shift+t exec --no-startup-id ~/.local/bin/rofi-launch sudo
bindsym $mod+Shift+w exec --no-startup-id ~/.local/bin/rofi-launch window
bindsym $mod+Escape exec --no-startup-id ~/.local/bin/rofi-powermenu
@@ -0,0 +1,172 @@
/**
* The theme "mesh-powermenu" (module rofi, novox/hq ADR 0208): the power menu's five buttons.
* Owned by the mesh; replaced at every push. Adopted from the workstations' power menu theme of
* 2026-10-04 (after adi1090x's powermenu type-2, style-1), its colours inlined.
**/
/*****----- Configuration -----*****/
configuration {
show-icons: false;
}
/*****----- Global Properties -----*****/
* {
background: #000000FF;
background-alt: #282B31FF;
foreground: #FFFFFFFF;
selected: #DE5200FF;
active: #DE5200FF;
urgent: #CC0000FF;
font: "JetBrainsMono Nerd Font 11";
}
/*****----- Main Window -----*****/
window {
/* properties for window widget */
transparency: "real";
location: center;
anchor: center;
fullscreen: false;
width: 800px;
x-offset: 0px;
y-offset: 0px;
/* properties for all widgets */
enabled: true;
margin: 0px;
padding: 0px;
border: 0px solid;
border-radius: 0px;
border-color: @selected;
cursor: "default";
background-color: @background;
}
/*****----- Main Box -----*****/
mainbox {
enabled: true;
spacing: 15px;
margin: 0px;
padding: 30px;
border: 0px solid;
border-radius: 0px;
border-color: @selected;
background-color: transparent;
children: [ "inputbar", "listview" ];
}
/*****----- Inputbar -----*****/
inputbar {
enabled: true;
spacing: 15px;
margin: 0px;
padding: 0px;
border: 0px;
border-radius: 0px;
border-color: @selected;
background-color: transparent;
text-color: @foreground;
children: [ "textbox-prompt-colon", "prompt"];
}
dummy {
background-color: transparent;
}
textbox-prompt-colon {
enabled: true;
expand: false;
str: "";
padding: 12px 16px;
border-radius: 0px;
background-color: @urgent;
text-color: @background;
}
prompt {
enabled: true;
padding: 12px;
border-radius: 0px;
background-color: @active;
text-color: @background;
}
/*****----- Message -----*****/
message {
enabled: true;
margin: 0px;
padding: 12px;
border: 0px solid;
border-radius: 0px;
border-color: @selected;
background-color: @background-alt;
text-color: @foreground;
}
textbox {
background-color: inherit;
text-color: inherit;
vertical-align: 0.5;
horizontal-align: 0.5;
placeholder-color: @foreground;
blink: true;
markup: true;
}
error-message {
padding: 12px;
border: 0px solid;
border-radius: 0px;
border-color: @selected;
background-color: @background;
text-color: @foreground;
}
/*****----- Listview -----*****/
listview {
enabled: true;
columns: 5;
lines: 1;
cycle: true;
dynamic: true;
scrollbar: false;
layout: vertical;
reverse: false;
fixed-height: true;
fixed-columns: true;
spacing: 15px;
margin: 0px;
padding: 0px;
border: 0px solid;
border-radius: 0px;
border-color: @selected;
background-color: transparent;
text-color: @foreground;
cursor: "default";
}
/*****----- Elements -----*****/
element {
enabled: true;
spacing: 0px;
margin: 0px;
padding: 40px 10px;
border: 0px solid;
border-radius: 0px;
border-color: @selected;
background-color: @background-alt;
text-color: @foreground;
cursor: pointer;
}
element-text {
font: "JetBrainsMono Nerd Font Bold 32";
background-color: transparent;
text-color: inherit;
cursor: inherit;
vertical-align: 0.5;
horizontal-align: 0.5;
}
element selected.normal {
background-color: var(selected);
text-color: var(background);
}
@@ -0,0 +1,263 @@
/**
* The theme "mesh" (module rofi, novox/hq ADR 0208): every rofi window unless a caller names
* another. Owned by the mesh; replaced at every push. Adopted from the workstations' theme of
* 2026-10-04 (after adi1090x's launcher type-4, style-1), its colour file inlined so the theme is
* one file, and its face the monospace one every desktop module names.
**/
/*****----- Global Properties -----*****/
* {
background: #000000FF;
background-alt: #282B31FF;
foreground: #FFFFFFFF;
selected: #DE5200FF;
active: #DE5200FF;
urgent: #CC0000FF;
font: "JetBrainsMono Nerd Font 11";
border-colour: var(selected);
handle-colour: var(selected);
background-colour: var(background);
foreground-colour: var(foreground);
alternate-background: var(background-alt);
normal-background: var(background);
normal-foreground: var(foreground);
urgent-background: var(urgent);
urgent-foreground: var(background);
active-background: var(active);
active-foreground: var(background);
selected-normal-background: var(selected);
selected-normal-foreground: var(background);
selected-urgent-background: var(active);
selected-urgent-foreground: var(background);
selected-active-background: var(urgent);
selected-active-foreground: var(background);
alternate-normal-background: var(background);
alternate-normal-foreground: var(foreground);
alternate-urgent-background: var(urgent);
alternate-urgent-foreground: var(background);
alternate-active-background: var(active);
alternate-active-foreground: var(background);
}
/*****----- Main Window -----*****/
window {
transparency: "real";
location: center;
anchor: center;
fullscreen: false;
width: 600px;
x-offset: 0px;
y-offset: 0px;
enabled: true;
margin: 0px;
padding: 0px;
border: 1px solid;
border-radius: 0px;
border-color: @border-colour;
cursor: "default";
background-color: @background-colour;
}
/*****----- Main Box -----*****/
mainbox {
enabled: true;
spacing: 10px;
margin: 0px;
padding: 10px;
border: 0px solid;
border-radius: 0px;
border-color: @border-colour;
background-color: transparent;
children: [ "inputbar", "message", "listview", "mode-switcher" ];
}
/*****----- Inputbar -----*****/
inputbar {
enabled: true;
spacing: 10px;
margin: 0px;
padding: 10px;
border: 0px 0px 1px 0px;
border-radius: 0px;
border-color: @border-colour;
background-color: @alternate-background;
text-color: @foreground-colour;
children: [ "prompt", "entry" ];
}
prompt {
enabled: true;
background-color: inherit;
text-color: inherit;
}
textbox-prompt-colon {
enabled: true;
expand: false;
str: "::";
background-color: inherit;
text-color: inherit;
}
entry {
enabled: true;
background-color: inherit;
text-color: inherit;
cursor: text;
placeholder: "Search...";
placeholder-color: inherit;
}
/*****----- Listview -----*****/
listview {
enabled: true;
columns: 1;
lines: 8;
cycle: true;
dynamic: true;
scrollbar: false;
layout: vertical;
reverse: false;
fixed-height: true;
fixed-columns: true;
spacing: 0px;
margin: 0px;
padding: 0px;
border: 0px solid;
border-radius: 0px;
border-color: @border-colour;
background-color: transparent;
text-color: @foreground-colour;
cursor: "default";
}
scrollbar {
handle-width: 5px;
handle-color: @handle-colour;
border-radius: 0px;
background-color: @alternate-background;
}
/*****----- Elements -----*****/
element {
enabled: true;
spacing: 10px;
margin: 0px;
padding: 10px;
border: 0px solid;
border-radius: 0px;
border-color: @border-colour;
background-color: transparent;
text-color: @foreground-colour;
cursor: pointer;
}
element normal.normal {
background-color: var(normal-background);
text-color: var(normal-foreground);
}
element normal.urgent {
background-color: var(urgent-background);
text-color: var(urgent-foreground);
}
element normal.active {
background-color: var(active-background);
text-color: var(active-foreground);
}
element selected.normal {
background-color: var(selected-normal-background);
text-color: var(selected-normal-foreground);
}
element selected.urgent {
background-color: var(selected-urgent-background);
text-color: var(selected-urgent-foreground);
}
element selected.active {
background-color: var(selected-active-background);
text-color: var(selected-active-foreground);
}
element alternate.normal {
background-color: var(alternate-normal-background);
text-color: var(alternate-normal-foreground);
}
element alternate.urgent {
background-color: var(alternate-urgent-background);
text-color: var(alternate-urgent-foreground);
}
element alternate.active {
background-color: var(alternate-active-background);
text-color: var(alternate-active-foreground);
}
element-icon {
background-color: transparent;
text-color: inherit;
size: 24px;
cursor: inherit;
}
element-text {
background-color: transparent;
text-color: inherit;
highlight: inherit;
cursor: inherit;
vertical-align: 0.5;
horizontal-align: 0.0;
}
/*****----- Mode Switcher -----*****/
mode-switcher {
enabled: true;
spacing: 10px;
margin: 0px;
padding: 0px;
border: 0px solid;
border-radius: 0px;
border-color: @border-colour;
background-color: transparent;
text-color: @foreground-colour;
}
button {
padding: 10px;
border: 0px solid;
border-radius: 0px;
border-color: @border-colour;
background-color: @alternate-background;
text-color: inherit;
cursor: pointer;
}
button selected {
background-color: var(selected-normal-background);
text-color: var(selected-normal-foreground);
}
/*****----- Message -----*****/
message {
enabled: true;
margin: 0px;
padding: 0px;
border: 0px solid;
border-radius: 0px;
border-color: @border-colour;
background-color: transparent;
text-color: @foreground-colour;
}
textbox {
font: "Inter 11";
padding: 10px;
border: 0px solid;
border-radius: 0px;
border-color: @border-colour;
background-color: @alternate-background;
text-color: @foreground-colour;
vertical-align: 0.5;
horizontal-align: 0.0;
highlight: none;
placeholder-color: @foreground-colour;
blink: true;
markup: true;
}
error-message {
padding: 10px;
border: 0px solid;
border-radius: 0px;
border-color: @border-colour;
background-color: @background-colour;
text-color: @foreground-colour;
}
@@ -0,0 +1,6 @@
{
"dynamic": "/services/traefik/dynamic",
"entrypoint": "websecure",
"certificate-resolver": "le",
"machine": "host.docker.internal"
}
@@ -0,0 +1,57 @@
#!/usr/bin/env bash
# screen-lock (module screen-lock, novox/hq ADR 0208): the locker. xss-lock runs it on idle, before
# suspend and on logind's Lock, and node-lock-screen's `lock` ends in it.
#
# The operator's look, adopted from the predecessor's my-i3lock: the screen as it was, blurred, with
# an orange ring, the time and the date. That needs i3lock-color, from the distribution's user
# repository, kept as found until the mesh carries such software (novox/hq research 027, question 1).
# On a machine without it the distribution's i3lock shows the same blurred screen, taken here, with
# its own plain ring; failing that, black.
#
# Before a suspend, xss-lock hands this script a lock on the sleep (XSS_SLEEP_LOCK_FD) and suspends
# once it is released. The locker must not inherit it, or the machine would wait for the unlock
# before sleeping; it is released once i3lock is up, so the machine never sleeps unlocked. This is
# xss-lock's own documented pattern for i3lock.
set -u
# One locker: a second press of the key, or a lock while locked, changes nothing.
if pgrep -xu "$EUID" i3lock >/dev/null; then
exit 0
fi
# The colour build numbers its versions <n>.c.<n> (2.13.c.5); its version line never says "color".
if i3lock --version 2>&1 | grep -qE '[0-9]\.c\.[0-9]'; then
blank='#00000000' clear='#ffffff22' accent='#ca4a00' wrong='#880000bb' verifying='#bb00bbbb'
options=(
--insidever-color="$clear" --ringver-color="$verifying"
--insidewrong-color="$clear" --ringwrong-color="$wrong"
--inside-color="$blank" --ring-color="$accent" --line-color="$blank" --separator-color="$accent"
--verif-color="$accent" --wrong-color="$accent" --time-color="$accent" --date-color="$accent"
--layout-color="$accent" --keyhl-color="$wrong" --bshl-color="$wrong"
--screen 1 --blur 5 --ring-width=7.0 --clock --indicator
--time-str="%H:%M:%S" --date-str="%A, %Y-%m-%d"
--time-font=sans-serif --date-font=sans-serif --verif-font=sans-serif
--wrong-font=sans-serif --layout-font=sans-serif --keylayout 1
--show-failed-attempts --ignore-empty-password
)
else
options=(--color=000000 --show-failed-attempts --ignore-empty-password)
shot="${XDG_RUNTIME_DIR:-/tmp}/screen-lock.png"
if command -v magick >/dev/null && magick import -window root -resize 25% -blur 0x3 -resize 400% "$shot" 2>/dev/null; then
options+=(--image="$shot")
fi
fi
if [[ -e /dev/fd/${XSS_SLEEP_LOCK_FD:--1} ]]; then
kill_i3lock() { pkill -xu "$EUID" "$@" i3lock; }
trap kill_i3lock TERM INT
i3lock "${options[@]}" {XSS_SLEEP_LOCK_FD}<&-
exec {XSS_SLEEP_LOCK_FD}<&-
while kill_i3lock -0; do
sleep 0.5
done
else
trap 'kill %%' TERM INT
i3lock --nofork "${options[@]}" &
wait
fi
@@ -0,0 +1,8 @@
# The lock screen (module screen-lock, novox/hq ADR 0208): the session locks after 30 minutes idle,
# the displays go to standby and suspend then and off after an hour, and xss-lock runs the locker on
# idle, before suspend and on logind's Lock. xss-lock needs this login session, so it starts here and
# not as a unit. It is started again if it exits, for as long as this session lasts ($$ is the
# session's own process, which becomes the window manager).
xset s 1800 1800
xset dpms 1800 1800 3600
(while kill -0 $$ 2>/dev/null; do xss-lock --transfer-sleep-lock -- "$HOME/.local/bin/screen-lock"; sleep 2; done) &

Some files were not shown because too many files have changed in this diff Show More