Compare commits

..

3 commits

Author SHA1 Message Date
d482b7ab3a
WIP: feat(webserver): Reimplement music player 2025-11-30 00:27:04 +08:00
17ff62f0b9
flake.lock: Update
Flake lock file updates:

• Updated input 'disko':
    'github:nix-community/disko/af087d076d3860760b3323f6b583f4d828c1ac17' (2025-11-04)
  → 'github:nix-community/disko/2055a08fd0e2fd41318279a5355eb8a161accf26' (2025-11-28)
• Updated input 'nixpkgs':
    'https://releases.nixos.org/nixos/25.05/nixos-25.05.813095.1c8ba8d3f763/nixexprs.tar.xz?lastModified=1763948260&narHash=sha256-zZk7fn2ARAqmLwaYTpxBJmj81KIdz11NiWt7ydHHD/M%3D&rev=1c8ba8d3f7634acac4a2094eef7c32ad9106532c' (2025-11-24)
  → 'https://releases.nixos.org/nixos/25.05/nixos-25.05.813221.9a7b80b6f82a/nixexprs.tar.xz?lastModified=1764316264&narHash=sha256-UcoE0ISg9Nnzx/2n7VvQl3fRsLg%2BDcVa/ZGf/DZNHbs%3D&rev=9a7b80b6f82a71ea04270d7ba11b48855681c4b0' (2025-11-28)
• Updated input 'nixpkgs-unstable':
    'https://releases.nixos.org/nixos/unstable/nixos-25.11pre900642.050e09e09111/nixexprs.tar.xz?lastModified=1763835633&narHash=sha256-nzRnw0UkYQpDm0o20AKvG/5oHCXy5qEGOsFAVhB5NmA%3D&rev=050e09e091117c3d7328c7b2b7b577492c43c134' (2025-11-22)
  → 'https://releases.nixos.org/nixos/unstable/nixos-26.05pre903292.2fad6eac6077/nixexprs.tar.xz?lastModified=1764242076&narHash=sha256-6/1EG2fiKvLoUJ8FD7ymRx87e4zcfJTzAdUYgo4CDLA%3D&rev=2fad6eac6077f03fe109c4d4eb171cf96791faa4' (2025-11-27)
• Updated input 'sops-nix':
    'github:Mic92/sops-nix/877bb495a6f8faf0d89fc10bd142c4b7ed2bcc0b' (2025-11-20)
  → 'github:Mic92/sops-nix/c482a1c1bbe030be6688ed7dc84f7213f304f1ec' (2025-11-24)
2025-11-30 00:27:04 +08:00
823caecc59
feat(vm): Fix shutdown 2025-11-30 00:26:50 +08:00
28 changed files with 941 additions and 364 deletions

View file

@ -7,6 +7,8 @@
networking.hostName = "testvm"; networking.hostName = "testvm";
systemd.services.matrix-hookshot.enable = lib.mkForce false;
services = { services = {
# Sets the base domain for nginx to a local domain so that we can # Sets the base domain for nginx to a local domain so that we can
# easily test locally with the VM. # easily test locally with the VM.
@ -43,14 +45,6 @@
source = ../../keys/hosts/staging.key; source = ../../keys/hosts/staging.key;
}; };
# Pretend the acme renew succeeds.
#
# TODO(tlater): Set up pebble to retrieve certs "properly"
# instead
systemd.services."acme-order-renew-tlater.net".script = ''
touch out/acme-success
'';
virtualisation.vmVariant = { virtualisation.vmVariant = {
virtualisation = { virtualisation = {
memorySize = 3941; memorySize = 3941;

View file

@ -51,9 +51,20 @@
paths = [ "/var/lib/acme/tlater.net" ]; paths = [ "/var/lib/acme/tlater.net" ];
}; };
systemd.services.nginx.serviceConfig.SupplementaryGroups = [ systemd.services = {
config.security.acme.certs."tlater.net".group nginx.serviceConfig.SupplementaryGroups = [ config.security.acme.certs."tlater.net".group ];
];
# Don't attempt to retrieve a certificate if the domain name
# doesn't *actually* match the cert name
#
# TODO(tlater): Set up pebble to retrieve certs "properly"
# instead
"acme-tlater.net".serviceConfig.ExecCondition =
let
confirm = ''[[ "tlater.net" = "${config.services.nginx.domain}" ]]'';
in
''${pkgs.runtimeShell} -c '${confirm}' '';
};
sops.secrets = { sops.secrets = {
"porkbun/api-key".owner = "acme"; "porkbun/api-key".owner = "acme";
@ -74,18 +85,10 @@
security.acme.certs."tlater.net".extraDomainNames = [ config.services.nginx.domain ]; security.acme.certs."tlater.net".extraDomainNames = [ config.services.nginx.domain ];
# Pretend the acme renew succeeds.
#
# TODO(tlater): Set up pebble to retrieve certs "properly"
# instead
systemd.services."acme-order-renew-tlater.net".script = ''
touch out/acme-success
'';
services.nginx = { services.nginx = {
domain = "testHost.test"; domain = "testHost";
virtualHosts."${config.services.nginx.domain}.local" = { virtualHosts."${config.services.nginx.domain}" = {
useACMEHost = "tlater.net"; useACMEHost = "tlater.net";
onlySSL = true; onlySSL = true;
enableHSTS = true; enableHSTS = true;
@ -106,7 +109,6 @@
{ pkgs, ... }: { pkgs, ... }:
{ {
environment.systemPackages = [ pkgs.curl ]; environment.systemPackages = [ pkgs.curl ];
networking.hosts."192.168.1.2" = [ "testHost.test" ];
}; };
}; };
@ -123,7 +125,7 @@
"--silent", "--silent",
"--dump-header -", "--dump-header -",
"--cacert /certs/tlater.net/fullchain.pem", "--cacert /certs/tlater.net/fullchain.pem",
"https://testHost.test", "https://testHost",
"-o /dev/null" "-o /dev/null"
])) ]))

View file

@ -12,7 +12,10 @@ let
turn-realm = "turn.${config.services.nginx.domain}"; turn-realm = "turn.${config.services.nginx.domain}";
in in
{ {
imports = [ ./heisenbridge.nix ]; imports = [
./heisenbridge.nix
./matrix-hookshot.nix
];
networking.firewall = { networking.firewall = {
allowedTCPPorts = [ allowedTCPPorts = [

View file

@ -0,0 +1,172 @@
{
pkgs,
lib,
config,
...
}:
let
matrixLib = pkgs.callPackage ./lib.nix { };
cfg = config.services.matrix-hookshot;
conduitCfg = config.services.matrix-conduit;
domain = conduitCfg.settings.global.server_name;
registration = matrixLib.writeRegistrationScript {
id = "matrix-hookshot";
url = "http://127.0.0.1:9993";
sender_localpart = "hookshot";
namespaces = {
aliases = [ ];
rooms = [ ];
users = [
{
regex = "@${cfg.settings.generic.userIdPrefix}.*:${domain}";
exclusive = true;
}
];
};
# Encryption support
# TODO(tlater): Enable when
# https://github.com/matrix-org/matrix-hookshot/issues/1060 is
# fixed
# extraSettings = {
# "de.sorunome.msc2409.push_ephemeral" = true;
# push_ephemeral = true;
# "org.matrix.msc3202" = true;
# };
runtimeRegistration = "${cfg.registrationFile}";
};
in
{
# users = {
# users.matrix-hookshot = {
# home = "/run/matrix-hookshot";
# group = "matrix-hookshot";
# isSystemUser = true;
# };
# groups.matrix-hookshot = { };
# };
systemd.services.matrix-hookshot = {
serviceConfig = {
Type = lib.mkForce "exec";
LoadCredential = "matrix-hookshot:/run/secrets/matrix-hookshot";
inherit (registration) ExecStartPre;
# Some library in matrix-hookshot wants a home directory
Environment = [ "HOME=/run/matrix-hookshot" ];
# User = "matrix-hookshot";
DynamicUser = true;
StateDirectory = "matrix-hookshot";
RuntimeDirectory = "matrix-hookshot";
RuntimeDirectoryMode = "0700";
RestrictNamespaces = true;
PrivateUsers = true;
ProtectHostname = true;
ProtectClock = true;
ProtectKernelTunables = true;
ProtectKernelModules = true;
ProtectKernelLogs = true;
ProtectControlGroups = true;
RestrictAddressFamilies = [
# "AF_UNIX"
"AF_INET"
"AF_INET6"
];
LockPersonality = true;
RestrictRealtime = true;
ProtectProc = "invisible";
ProcSubset = "pid";
UMask = 77;
};
};
# services.redis.servers.matrix-hookshot = {
# enable = true;
# user = "matrix-hookshot";
# };
services.matrix-hookshot = {
enable = true;
serviceDependencies = [ "conduit.service" ];
registrationFile = "/run/matrix-hookshot/registration.yaml";
settings = {
bridge = {
inherit domain;
url = "http://localhost:${toString conduitCfg.settings.global.port}";
mediaUrl = conduitCfg.settings.global.well_known.client;
port = 9993;
bindAddress = "127.0.0.1";
};
bot.displayname = "Hookshot";
# cache.redisUri = "redis://${config.services.redis.servers.matrix-hookshot.unixSocket}";
generic = {
enabled = true;
outbound = false;
# Only allow webhooks from localhost for the moment
urlPrefix = "http://127.0.0.1:9000/webhook";
userIdPrefix = "_webhooks_";
allowJsTransformationFunctions = true;
};
# TODO(tlater): Enable when
# https://github.com/matrix-org/matrix-hookshot/issues/1060 is
# fixed
# encryption.storagePath = "/var/lib/matrix-hookshot/cryptostore";
permissions = [
{
actor = "matrix.tlater.net";
services = [
{
service = "*";
level = "notifications";
}
];
}
{
actor = "@tlater:matrix.tlater.net";
services = [
{
service = "*";
level = "admin";
}
];
}
];
listeners = [
{
port = 9000;
resources = [ "webhooks" ];
}
{
port = 9001;
resources = [ "metrics" ];
}
];
metrics.enabled = true;
};
};
sops.secrets = {
# Accessed via systemd cred through /run/secrets/matrix-hookshot
"matrix-hookshot/as-token" = { };
"matrix-hookshot/hs-token" = { };
};
}

View file

@ -23,7 +23,7 @@ in
minifyStaticFiles = true; minifyStaticFiles = true;
proxySSL = true; proxySSL = true;
proxyPort = 443; proxyPort = 443;
package = flake-inputs.foundryvtt.packages.${pkgs.stdenv.hostPlatform.system}.foundryvtt_13; package = flake-inputs.foundryvtt.packages.${pkgs.system}.foundryvtt_13;
}; };
nginx.virtualHosts."${domain}" = nginx.virtualHosts."${domain}" =

View file

@ -18,9 +18,6 @@ in
enable = true; enable = true;
settings.server.externalDomain = "https://${hostName}"; settings.server.externalDomain = "https://${hostName}";
# We're using vectorchord now
database.enableVectors = false;
environment.IMMICH_TELEMETRY_INCLUDE = "all"; environment.IMMICH_TELEMETRY_INCLUDE = "all";
}; };

View file

@ -57,19 +57,6 @@ in
access = "proxy"; access = "proxy";
} }
]; ];
alerting.contactPoints.settings.contactPoints = [
{
name = "ntfy";
receivers = [
{
uid = "ntfy";
type = "webhook";
settings.url = "http://${config.services.ntfy-sh.settings.listen-http}/local-alerts?template=grafana";
}
];
}
];
}; };
}; };

View file

@ -89,6 +89,10 @@ in
"127.0.0.1:8082" "127.0.0.1:8082"
]; ];
# Configured in the hookshot listeners, but it's hard to filter
# the correct values out of that config.
matrixHookshot.targets = [ "127.0.0.1:9001" ];
victorialogs.targets = [ config.services.victorialogs.bindAddress ]; victorialogs.targets = [ config.services.victorialogs.bindAddress ];
}; };
}; };

View file

@ -5,7 +5,7 @@
... ...
}: }:
let let
nextcloud = pkgs.nextcloud32; nextcloud = pkgs.nextcloud31;
hostName = "nextcloud.${config.services.nginx.domain}"; hostName = "nextcloud.${config.services.nginx.domain}";
in in
{ {
@ -104,7 +104,7 @@ in
}; };
# Ensure that this service doesn't start before postgres is ready # Ensure that this service doesn't start before postgres is ready
systemd.services.nextcloud-setup.after = [ "postgresql.target" ]; systemd.services.nextcloud-setup.after = [ "postgresql.service" ];
sops.secrets."nextcloud/tlater" = { sops.secrets."nextcloud/tlater" = {
owner = "nextcloud"; owner = "nextcloud";

View file

@ -17,6 +17,7 @@ in
services.ntfy-sh = { services.ntfy-sh = {
enable = true; enable = true;
package = flake-inputs.nixpkgs-unstable.legacyPackages.${pkgs.system}.ntfy-sh;
environmentFile = config.sops.secrets."ntfy/users".path; environmentFile = config.sops.secrets."ntfy/users".path;
@ -137,17 +138,17 @@ in
"curl", "curl",
"--silent", "--silent",
"--show-error", "--show-error",
f"--max-time {2 + timeout}", f"--max-time {timeout}",
"-u tlater:insecure", "-u tlater:insecure",
f"http://ntfy.testHost/{topic}/json", f"http://ntfy.testHost/{topic}/json",
"-o messages" "> messages"
] ]
client.succeed(f'{" ".join(systemd_invocation)} "{" ".join(curl)}"') client.succeed(f'{" ".join(systemd_invocation)} "{" ".join(curl)}"')
# Give some slack so the host doesn't send messages before # Give some slack so the host doesn't send messages before
# we're listening # we're listening
time.sleep(2) time.sleep(1)
yield yield

View file

@ -19,7 +19,7 @@ in
serviceConfig = { serviceConfig = {
ExecStart = "${ ExecStart = "${
flake-inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.starbound flake-inputs.self.packages.${pkgs.system}.starbound
}/bin/launch-starbound ${./configs/starbound.json}"; }/bin/launch-starbound ${./configs/starbound.json}";
Type = "simple"; Type = "simple";

View file

@ -20,7 +20,7 @@ in
after = [ "network.target" ]; after = [ "network.target" ];
script = '' script = ''
${lib.getExe flake-inputs.self.packages.${pkgs.stdenv.hostPlatform.system}.webserver} ${lib.getExe flake-inputs.self.packages.${pkgs.system}.webserver}
''; '';
environment = { environment = {

62
flake.lock generated
View file

@ -77,11 +77,11 @@
"utils": "utils" "utils": "utils"
}, },
"locked": { "locked": {
"lastModified": 1770019181, "lastModified": 1762286984,
"narHash": "sha256-hwsYgDnby50JNVpTRYlF3UR/Rrpt01OrxVuryF40CFY=", "narHash": "sha256-9I2H9x5We6Pl+DBYHjR1s3UT8wgwcpAH03kn9CqtdQc=",
"owner": "serokell", "owner": "serokell",
"repo": "deploy-rs", "repo": "deploy-rs",
"rev": "77c906c0ba56aabdbc72041bf9111b565cdd6171", "rev": "9c870f63e28ec1e83305f7f6cb73c941e699f74f",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -123,11 +123,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1769524058, "lastModified": 1764350888,
"narHash": "sha256-zygdD6X1PcVNR2PsyK4ptzrVEiAdbMqLos7utrMDEWE=", "narHash": "sha256-6Rp18zavTlnlZzcoLoBTJMBahL2FycVkw2rAEs3cQvo=",
"owner": "nix-community", "owner": "nix-community",
"repo": "disko", "repo": "disko",
"rev": "71a3fc97d80881e91710fe721f1158d3b96ae14d", "rev": "2055a08fd0e2fd41318279a5355eb8a161accf26",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -181,11 +181,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1767431140, "lastModified": 1762437643,
"narHash": "sha256-ug37Jt6r8LP3161suTh6IW+fkx0a7kiSAhAPsVcPrkA=", "narHash": "sha256-nQ2ItqrkvOYEjJr1HcXkIEFS4SEy5q1ax0Y1CTuKhHs=",
"owner": "NotAShelf", "owner": "NotAShelf",
"repo": "flint", "repo": "flint",
"rev": "7832a5b5f5ef1243818f8f5e357ad1ee2d35d2b7", "rev": "36c565edd971166718d21ae973c792b194ca737d",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -201,11 +201,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1767491610, "lastModified": 1761916399,
"narHash": "sha256-/Nldo9ILD7T5aQKuyeUccNPXjhNBrovGXEoi5k7m9Bo=", "narHash": "sha256-wLZ8km5ftKlIDdHJrFiDQivXc5b+7DRxmBp2347H5g8=",
"owner": "reckenrode", "owner": "reckenrode",
"repo": "nix-foundryvtt", "repo": "nix-foundryvtt",
"rev": "35e789ba383fbfaa9039005b9b24669c5be6b8ab", "rev": "8cceb7af3dfbe465b5108db5c098b097edf85790",
"type": "github" "type": "github"
}, },
"original": { "original": {
@ -255,15 +255,28 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1769900590, "lastModified": 1764316264,
"narHash": "sha256-OGuC+gtMQt8j7FPOx3p5ASig+SkaXnpf9yNjRpetg4Y=", "narHash": "sha256-UcoE0ISg9Nnzx/2n7VvQl3fRsLg+DcVa/ZGf/DZNHbs=",
"rev": "41e216c0ca66c83b12ab7a98cc326b5db01db646", "rev": "9a7b80b6f82a71ea04270d7ba11b48855681c4b0",
"type": "tarball", "type": "tarball",
"url": "https://releases.nixos.org/nixos/25.11/nixos-25.11.5065.41e216c0ca66/nixexprs.tar.xz" "url": "https://releases.nixos.org/nixos/25.05/nixos-25.05.813221.9a7b80b6f82a/nixexprs.tar.xz?lastModified=1764316264&rev=9a7b80b6f82a71ea04270d7ba11b48855681c4b0"
}, },
"original": { "original": {
"type": "tarball", "type": "tarball",
"url": "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz" "url": "https://channels.nixos.org/nixos-25.05/nixexprs.tar.xz"
}
},
"nixpkgs-unstable": {
"locked": {
"lastModified": 1764242076,
"narHash": "sha256-6/1EG2fiKvLoUJ8FD7ymRx87e4zcfJTzAdUYgo4CDLA=",
"rev": "2fad6eac6077f03fe109c4d4eb171cf96791faa4",
"type": "tarball",
"url": "https://releases.nixos.org/nixos/unstable/nixos-26.05pre903292.2fad6eac6077/nixexprs.tar.xz?lastModified=1764242076&rev=2fad6eac6077f03fe109c4d4eb171cf96791faa4"
},
"original": {
"type": "tarball",
"url": "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz"
} }
}, },
"pre-commit-hooks": { "pre-commit-hooks": {
@ -312,6 +325,7 @@
"flint": "flint", "flint": "flint",
"foundryvtt": "foundryvtt", "foundryvtt": "foundryvtt",
"nixpkgs": "nixpkgs", "nixpkgs": "nixpkgs",
"nixpkgs-unstable": "nixpkgs-unstable",
"sonnenshift": "sonnenshift", "sonnenshift": "sonnenshift",
"sops-nix": "sops-nix" "sops-nix": "sops-nix"
} }
@ -324,11 +338,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1764578400, "lastModified": 1763619077,
"narHash": "sha256-8V0SpIcYyjpP+nAHfYJDof7CofLTwVVDo5QLZ0epjOQ=", "narHash": "sha256-dlfamaoIzFEgwgtzPJuw5Tl5SqjbWcV8CsbP2hVBeuI=",
"ref": "refs/heads/main", "ref": "refs/heads/main",
"rev": "bf17617899692c9c2bfebfce87320a4174e6dc28", "rev": "64a2c8a3743ea6897ecac6692fba8aebc3389fca",
"revCount": 27, "revCount": 26,
"type": "git", "type": "git",
"url": "ssh://git@github.com/sonnenshift/battery-manager" "url": "ssh://git@github.com/sonnenshift/battery-manager"
}, },
@ -344,11 +358,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1769921679, "lastModified": 1764021963,
"narHash": "sha256-twBMKGQvaztZQxFxbZnkg7y/50BW9yjtCBWwdjtOZew=", "narHash": "sha256-1m84V2ROwNEbqeS9t37/mkry23GBhfMt8qb6aHHmjuc=",
"owner": "Mic92", "owner": "Mic92",
"repo": "sops-nix", "repo": "sops-nix",
"rev": "1e89149dcfc229e7e2ae24a8030f124a31e4f24f", "rev": "c482a1c1bbe030be6688ed7dc84f7213f304f1ec",
"type": "github" "type": "github"
}, },
"original": { "original": {

View file

@ -2,7 +2,8 @@
description = "tlater.net host configuration"; description = "tlater.net host configuration";
inputs = { inputs = {
nixpkgs.url = "https://channels.nixos.org/nixos-25.11/nixexprs.tar.xz"; nixpkgs.url = "https://channels.nixos.org/nixos-25.05/nixexprs.tar.xz";
nixpkgs-unstable.url = "https://channels.nixos.org/nixos-unstable/nixexprs.tar.xz";
## Nix/OS utilities ## Nix/OS utilities
@ -137,7 +138,10 @@
packages.${system} = { packages.${system} = {
default = vm.config.system.build.vm; default = vm.config.system.build.vm;
} }
// import ./pkgs { pkgs = nixpkgs.legacyPackages.${system}; }; // import ./pkgs {
pkgs = nixpkgs.legacyPackages.${system};
flake-inputs = inputs;
};
################### ###################
# Utility scripts # # Utility scripts #

View file

@ -20,6 +20,9 @@ steam:
heisenbridge: heisenbridge:
as-token: ENC[AES256_GCM,data:+2yo6T18j34622H8ZWblAFB2phLw1q0k0vUQEZ5sFj7dQaRnkEiAMi0R3p17Zq0pOtGEC0RRZuPLYkcZ1oKP0w==,iv:lGwrQYp//FufpmJocrLIVyy9RK7lEEVcpAi0wmkjr34=,tag:yV06UbhAYJQz36O2XdhY+A==,type:str] as-token: ENC[AES256_GCM,data:+2yo6T18j34622H8ZWblAFB2phLw1q0k0vUQEZ5sFj7dQaRnkEiAMi0R3p17Zq0pOtGEC0RRZuPLYkcZ1oKP0w==,iv:lGwrQYp//FufpmJocrLIVyy9RK7lEEVcpAi0wmkjr34=,tag:yV06UbhAYJQz36O2XdhY+A==,type:str]
hs-token: ENC[AES256_GCM,data:u52WpkQFd/J7JFoE/rfNluebyZQLOokvkVdL7+AEAvrhJhrkJli1ztkD79lbC+6tGUH4tT3T+nX9wvGKnrRUQg==,iv:as+9fVuvMg2IoE2WIKD9mHi+znhNcWRh5Zq+yr0xcDQ=,tag:mZ7fh7U0MfgI8hyq/28Bcg==,type:str] hs-token: ENC[AES256_GCM,data:u52WpkQFd/J7JFoE/rfNluebyZQLOokvkVdL7+AEAvrhJhrkJli1ztkD79lbC+6tGUH4tT3T+nX9wvGKnrRUQg==,iv:as+9fVuvMg2IoE2WIKD9mHi+znhNcWRh5Zq+yr0xcDQ=,tag:mZ7fh7U0MfgI8hyq/28Bcg==,type:str]
matrix-hookshot:
as-token: ENC[AES256_GCM,data:nXTanPhDyDF7R3AllLqpM5dzljBrHwlh1KJnTGIi5PhbDY2lPj4+uXkMEwvm1u+hQjPyM7vKZPfK+0/dms6Y7A==,iv:fSakJN+yai0gfOJKFxxaxgyUtk0pNmIeqVgrdq92/24=,tag:Qc7+SUnm5/Nq5+QIScR9kQ==,type:str]
hs-token: ENC[AES256_GCM,data:Bwyj0JTTN0NNnwOs1zA8CqbtZSNcvlINeT7QVc2eJiHda92J6vQk7bSxy6KuqCN9DxlUsK13ggYjNORY2vic5w==,iv:Npnp8arYQ3Yb6CXrnKgE03hD7ZjGINPa/DwFI8D+5tA=,tag:FqNE6yI0nF4puEUw9MGAjQ==,type:str]
wireguard: wireguard:
server-key: ENC[AES256_GCM,data:mXb7ZznJHf5CgV8rI4uzPBATMRbmd7LimgtCkQM9kAjbIaGwUBqJZBN3fXs=,iv:3Po1Orinzov9rnEm9cLzgJY1PeD+5Jl9115MriABHh8=,tag:E/2CjDO1JCvJzxCnqKcNyw==,type:str] server-key: ENC[AES256_GCM,data:mXb7ZznJHf5CgV8rI4uzPBATMRbmd7LimgtCkQM9kAjbIaGwUBqJZBN3fXs=,iv:3Po1Orinzov9rnEm9cLzgJY1PeD+5Jl9115MriABHh8=,tag:E/2CjDO1JCvJzxCnqKcNyw==,type:str]
restic: restic:
@ -29,8 +32,8 @@ turn:
env: ENC[AES256_GCM,data:kt5nhVo9pb/ZbPUEcqSYXxN9YMgQKnFb5VRfFFS/qoIaJ73uD2fuJKqcxAyVRrdLqnSAWSQBgTgunBzdP7xqLAK2qt8DYAQWHkIe9uxFbSXZpdmw,iv:9lq6SFwTFN4GGm6gPiJpUMasMdnHVF6XLGYrsyG3kjU=,tag:428Qf9DOiiHt/Wjb188b8g==,type:str] env: ENC[AES256_GCM,data:kt5nhVo9pb/ZbPUEcqSYXxN9YMgQKnFb5VRfFFS/qoIaJ73uD2fuJKqcxAyVRrdLqnSAWSQBgTgunBzdP7xqLAK2qt8DYAQWHkIe9uxFbSXZpdmw,iv:9lq6SFwTFN4GGm6gPiJpUMasMdnHVF6XLGYrsyG3kjU=,tag:428Qf9DOiiHt/Wjb188b8g==,type:str]
secret: ENC[AES256_GCM,data:si7ee6Xfhdgdyzbp6aQpF7pz3TmTBb7iQ82lRPVXNDg9JfHI+lbmgAsSnRLX5qMCA6P9R045sSMosqidL8QwRg==,iv:SrhpZKK8D45yxCEfDb9P3TwtA14+qEI+wcRqcN/a6pw=,tag:PiwV+mOL9xHJgJft6sc61g==,type:str] secret: ENC[AES256_GCM,data:si7ee6Xfhdgdyzbp6aQpF7pz3TmTBb7iQ82lRPVXNDg9JfHI+lbmgAsSnRLX5qMCA6P9R045sSMosqidL8QwRg==,iv:SrhpZKK8D45yxCEfDb9P3TwtA14+qEI+wcRqcN/a6pw=,tag:PiwV+mOL9xHJgJft6sc61g==,type:str]
sops: sops:
lastmodified: "2025-12-01T11:39:17Z" lastmodified: "2025-11-29T14:52:24Z"
mac: ENC[AES256_GCM,data:TwhGOW/V9/IoBifzh1MSwy/ff7ONTnxEmwERD8Yl2E27WG/6dTVz0/nIlZ8KsEKLC6vB2m+sJT+14Q9KCj4Cn/bWV1PmhytktGPxLQpgF55+pZlSK1aLUPLq0hwE93b4MAeOvzoOXtCQguh1dsB2RkinabFoMeZ2xJ7Kc+jHlfA=,iv:Ri8aEA4tssGDv2UuKeza8vs94IovM9GARLIEapb9Ya0=,tag:MDgAffj7ndmMwpw7mBXNRg==,type:str] mac: ENC[AES256_GCM,data:RC18s48jxRFQMtbmu74P7G4uhm2yHk9TB0wN7z4g8SNE3nfkYMvHAJqPr3A3dO+T33zkTFcSRm7fhWItUahTCW3fO10u6kDvWbnyjlSuAy86Tkz2iqeW4iSOzKswDptAgb/B+juAHhEMxDnkG5vpPlIcD0SVP89NlflXftogOqw=,iv:2vN2TJvzePzBJfUeBxvGXwGmRsB5sopqyWm9uUv/rzA=,tag:C6UOWrUxVsRMFncL1y1eTQ==,type:str]
pgp: pgp:
- created_at: "2025-10-03T21:38:48Z" - created_at: "2025-10-03T21:38:48Z"
enc: |- enc: |-

View file

@ -21,6 +21,9 @@ steam:
heisenbridge: heisenbridge:
as-token: ENC[AES256_GCM,data:tXbOeo7nv8I=,iv:wJAKcOXX9nGIw4n38ThOoj29u7dUWhsxSQG/p79JlEw=,tag:rTVaGS2UuWcea1uBa8YX2g==,type:str] as-token: ENC[AES256_GCM,data:tXbOeo7nv8I=,iv:wJAKcOXX9nGIw4n38ThOoj29u7dUWhsxSQG/p79JlEw=,tag:rTVaGS2UuWcea1uBa8YX2g==,type:str]
hs-token: ENC[AES256_GCM,data:VBwvwomv0Xg=,iv:q6INtJ+rg+QiXj8uBdBzQYQZUBBXp+9odxDHwvu8Jxc=,tag:XKhm8nxygAkKaiVPJ2Fcdg==,type:str] hs-token: ENC[AES256_GCM,data:VBwvwomv0Xg=,iv:q6INtJ+rg+QiXj8uBdBzQYQZUBBXp+9odxDHwvu8Jxc=,tag:XKhm8nxygAkKaiVPJ2Fcdg==,type:str]
matrix-hookshot:
as-token: ENC[AES256_GCM,data:uSUOo4f2KqA=,iv:Xb9G8Ecv6m59m51kDw2bOfq3SMJt4g9/6/EdH74R+KM=,tag:K9MSfO2c2Y4rlf0eYrmTnw==,type:str]
hs-token: ENC[AES256_GCM,data:0KsyA06InL4=,iv:zAR0Y1fk8SyodcSLBHlQ8I+BAmttz9Hkd8Q3OREFqs4=,tag:t1Et8N/3seq95DeGoUd7Sw==,type:str]
wireguard: wireguard:
server-key: ENC[AES256_GCM,data:FvY897XdKoa/mckE8JQLCkklsnYD6Wz1wpsu5t3uhEnW3iarnDQxF9msuYU=,iv:jqGXfekM+Vs+J9b5nlZ5Skd1ZKHajoUo2Dc4tMYPm1w=,tag:EehikjI/FCU8wqtpvJRamQ==,type:str] server-key: ENC[AES256_GCM,data:FvY897XdKoa/mckE8JQLCkklsnYD6Wz1wpsu5t3uhEnW3iarnDQxF9msuYU=,iv:jqGXfekM+Vs+J9b5nlZ5Skd1ZKHajoUo2Dc4tMYPm1w=,tag:EehikjI/FCU8wqtpvJRamQ==,type:str]
restic: restic:
@ -30,8 +33,8 @@ turn:
env: ENC[AES256_GCM,data:xjIz/AY109lyiL5N01p5T3HcYco/rM5CJSRTtg==,iv:16bW6OpyOK/QL0QPGQp/Baa9xyT8E3ZsYkwqmjuofk0=,tag:J5re3uKxIykw3YunvQWBgg==,type:str] env: ENC[AES256_GCM,data:xjIz/AY109lyiL5N01p5T3HcYco/rM5CJSRTtg==,iv:16bW6OpyOK/QL0QPGQp/Baa9xyT8E3ZsYkwqmjuofk0=,tag:J5re3uKxIykw3YunvQWBgg==,type:str]
secret: ENC[AES256_GCM,data:eQ7dAocoZtg=,iv:fgzjTPv30WqTKlLy+yMn5MsKQgjhPnwlGFFwYEg3gWs=,tag:1ze33U1NBkgMX/9SiaBNQg==,type:str] secret: ENC[AES256_GCM,data:eQ7dAocoZtg=,iv:fgzjTPv30WqTKlLy+yMn5MsKQgjhPnwlGFFwYEg3gWs=,tag:1ze33U1NBkgMX/9SiaBNQg==,type:str]
sops: sops:
lastmodified: "2025-12-01T11:39:26Z" lastmodified: "2025-11-29T11:54:33Z"
mac: ENC[AES256_GCM,data:11VQAYk8Am0k8OO6BtU17qpuEhcJ8ylRhJWQNHVAsmi5BCFjD1zU3NkWhtSstPrBcqHMenG+9XuEzpNnbccHI2ru0qlILsQvNj5OKo96FnvYtzApYlApoAzOetCx08Lfxa4RGLN/XCUSuccjBIU2PZRWEK+z+Cm1wHUFeqc1xPc=,iv:6y9j55Cld+GoOVGWAqsEgURRna6dHA2mGZwHVA+ZOE8=,tag:bSZi3nYmYrn3nFT2+RBPUQ==,type:str] mac: ENC[AES256_GCM,data:SaTvwxfARVou/ZjrWfdC8J6je8l89Zuumdz7PkmY2Tl2CQVxZmEt4AyV4bWiCtWhJmfH1Qa8m4Q+DyqimjapgYT5cUB1yxlknp233bB/+5C5k3KozU2hmh80KYgR496FtQvI74p0qw/lw00CGCR3WHNcIc0dbTiDzC90HlOpafg=,iv:vxMCAjpgyWvxk18LalmFhwOb5b2ThCDq1KTaX2OPvpM=,tag:QMA+tC4hs/FBnuVDye38Vg==,type:str]
pgp: pgp:
- created_at: "2025-10-03T21:38:26Z" - created_at: "2025-10-03T21:38:26Z"
enc: |- enc: |-

View file

@ -271,7 +271,7 @@ in
# To add completions; sadly need to hand-roll this since # To add completions; sadly need to hand-roll this since
# neither `symlinkJoin` nor `buildEnv` have collision # neither `symlinkJoin` nor `buildEnv` have collision
# handling. # handling.
(pkgs.runCommandLocal "cscli" { } '' (pkgs.runCommandNoCCLocal "cscli" { } ''
mkdir -p $out mkdir -p $out
ln -s ${cscli}/bin $out/bin ln -s ${cscli}/bin $out/bin
ln -s ${cfg.package}/share $out/share ln -s ${cfg.package}/share $out/share

View file

@ -6,7 +6,7 @@
... ...
}: }:
let let
inherit (flake-inputs.self.packages.${pkgs.stdenv.hostPlatform.system}) crowdsec-firewall-bouncer; inherit (flake-inputs.self.packages.${pkgs.system}) crowdsec-firewall-bouncer;
crowdsecCfg = config.security.crowdsec; crowdsecCfg = config.security.crowdsec;
cfg = crowdsecCfg.remediationComponents.firewallBouncer; cfg = crowdsecCfg.remediationComponents.firewallBouncer;

View file

@ -1,5 +1,8 @@
{ pkgs }: { pkgs, flake-inputs }:
let
inherit (flake-inputs.nixpkgs-unstable.legacyPackages.${pkgs.system}) ast-grep;
in
pkgs.lib.packagesFromDirectoryRecursive { pkgs.lib.packagesFromDirectoryRecursive {
inherit (pkgs) callPackage; callPackage = pkgs.lib.callPackageWith (pkgs // { inherit ast-grep; });
directory = ./packages; directory = ./packages;
} }

View file

@ -14,8 +14,8 @@ stdenvNoCC.mkDerivation (drv: {
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "crowdsecurity"; owner = "crowdsecurity";
repo = "hub"; repo = "hub";
rev = "acfabfa095700d52735d0359037e51ea8dd25297"; rev = "b63d9e925cfdd70f818a6a136ea53d5c8ca96d9a";
hash = "sha256-dcPMyMvxLYQV0AFDbzsLW7HLvUUFUSFRTxw1dOy70vk="; hash = "sha256-FMArGnR/pI/QlzsznStp8vzs/LbXooVgLdoTw+eSbec=";
}; };
installPhase = '' installPhase = ''

File diff suppressed because it is too large Load diff

View file

@ -11,6 +11,7 @@ axum = { version = "0.8.7", features = ["macros"], optional = true }
console_error_panic_hook = { version = "0.1.7", optional = true } console_error_panic_hook = { version = "0.1.7", optional = true }
figment = { version = "0.10.19", features = ["toml", "env"] } figment = { version = "0.10.19", features = ["toml", "env"] }
leptos = "0.8.3" leptos = "0.8.3"
leptos-use = "0.16.3"
leptos_axum = { version = "0.8.3", optional = true } leptos_axum = { version = "0.8.3", optional = true }
leptos_meta = "0.8.3" leptos_meta = "0.8.3"
leptos_router = "0.8.3" leptos_router = "0.8.3"
@ -19,9 +20,9 @@ reqwest = "0.12.24"
serde = { version = "1.0.228", features = ["derive"] } serde = { version = "1.0.228", features = ["derive"] }
thiserror = "2.0.17" thiserror = "2.0.17"
tokio = { version = "1.48.0", features = ["rt-multi-thread"], optional = true } tokio = { version = "1.48.0", features = ["rt-multi-thread"], optional = true }
url = { version = "2.5.7", features = ["serde"] } url = "2.5.7"
wasm-bindgen = { version = "=0.2.100", optional = true } wasm-bindgen = { version = "=0.2.100", optional = true }
web-sys = "^0.3.77" web-sys = { version = "^0.3.77", features = ["AnalyserNode", "AudioContext", "AudioDestinationNode", "GainNode", "HtmlMediaElement", "MediaElementAudioSourceNode"] }
[features] [features]
hydrate = [ hydrate = [

View file

@ -237,11 +237,7 @@ rustPlatform.buildRustPackage (drv: {
buildPhase = '' buildPhase = ''
runHook preBuild runHook preBuild
# dart-sass and wasm-pack want a home directory to put cache files
export HOME=$(mktemp -d)
cargo leptos build --release cargo leptos build --release
runHook postBuild runHook postBuild
''; '';
@ -292,7 +288,6 @@ rustPlatform.buildRustPackage (drv: {
(lib.makeBinPath [ (lib.makeBinPath [
ast-grep ast-grep
nix-prefetch-github nix-prefetch-github
cargo
]) ])
]; ];
} ./update.nu; } ./update.nu;

View file

@ -7,10 +7,12 @@ use leptos_router::{
mod homepage; mod homepage;
mod mail; mod mail;
mod music_sample;
use crate::components::Navbar; use crate::components::Navbar;
use homepage::HomePage; use homepage::HomePage;
use mail::Mail; use mail::Mail;
use music_sample::MusicSample;
pub fn shell(options: LeptosOptions) -> impl IntoView { pub fn shell(options: LeptosOptions) -> impl IntoView {
view! { view! {
@ -49,6 +51,7 @@ pub fn App() -> impl IntoView {
<Routes fallback=|| "Page not found.".into_view()> <Routes fallback=|| "Page not found.".into_view()>
<Route path=StaticSegment("") view=HomePage /> <Route path=StaticSegment("") view=HomePage />
<Route path=StaticSegment("mail") view=Mail /> <Route path=StaticSegment("mail") view=Mail />
<Route path=StaticSegment("music_sample") view=MusicSample />
</Routes> </Routes>
</main> </main>
</Router> </Router>

View file

@ -0,0 +1,75 @@
#![allow(dead_code, unused_variables)]
use leptos::{logging, prelude::*};
use leptos_meta::{Meta, Title};
use leptos_use::use_event_listener;
use ssr_safe::{MediaPlayer, MediaPlayerError};
mod ssr_safe;
#[component]
fn Controls() -> impl IntoView {
let player: LocalResource<Result<MediaPlayer, MediaPlayerError>> = expect_context();
Effect::new(move || {
let audio_element = if let Some(Ok(p)) = player.get() {
Some(p.audio_element())
} else {
None
};
use_event_listener(audio_element, ssr_safe::media_events::error, |ev| {
logging::error!("{:?}", ev);
});
});
view! {
<div class="notification">
<Suspense fallback=move || "Initializing audio player...">
<ErrorBoundary fallback=|errors| { "Failed to initialize audio player" }>
<div class="level is-mobile">
<div class="level-left">
// The play/pause/etc button
<div class="level-item">
<button class="button" type="button">
<span class="icon is-medium" />
</button>
</div>
</div>
// The title display
<div class="level-item">
{move || {
Ok::<_, MediaPlayerError>(player.get().transpose()?.map(|p| p.get_title()))
}}
</div>
// The artist display
<div class="level-right">
<div class="level-item">Artist</div>
</div>
</div>
</ErrorBoundary>
</Suspense>
</div>
}
}
#[component]
pub fn MusicSample() -> impl IntoView {
let player = LocalResource::new(MediaPlayer::new);
provide_context(player);
view! {
<Meta name="description" content="tlater.net music visualizer sample" />
<Title text="tlater.net music player" />
<section class="hero is-fullheight-with-navbar">
<div class="hero-body p-0">Body</div>
<div class="hero-foot">
<Controls />
</div>
</section>
}
}

View file

@ -0,0 +1,121 @@
use leptos::{ev::EventDescriptor, logging};
use leptos_use::use_event_listener;
use web_sys::EventTarget;
pub const DEFAULT_MP3: &str = "/Mseq_-_Journey.mp3a";
#[derive(Clone)]
pub struct MediaPlayer {
context: web_sys::AudioContext,
audio_element: web_sys::HtmlAudioElement,
}
impl MediaPlayer {
pub async fn new() -> Result<Self, MediaPlayerError> {
let context = web_sys::AudioContext::new()?;
let audio_element = web_sys::HtmlAudioElement::new_with_src(DEFAULT_MP3)?;
let source_node = context.create_media_element_source(&audio_element)?;
let gain_node = context.create_gain()?;
let analyser_node = context.create_analyser()?;
analyser_node.set_fft_size(2048);
analyser_node.set_smoothing_time_constant(0.8);
source_node.connect_with_audio_node(&analyser_node)?;
source_node.connect_with_audio_node(&gain_node)?;
gain_node.connect_with_audio_node(&context.destination())?;
Ok(Self {
context,
audio_element,
})
}
pub fn set_title(&self, title: &str) {
self.audio_element.set_src(title);
}
pub fn get_title(&self) -> String {
// Hardcoded for now, eventually I'll make this a proper
// player again...
"Journey".to_owned()
}
pub fn context(&self) -> EventTarget {
self.context.clone().into()
}
pub fn audio_element(&self) -> EventTarget {
self.audio_element.clone().into()
}
pub fn use_media_event<Ev, F>(&self, event: Ev, handler: F) -> impl Fn() + Clone + Send + Sync + use<Ev, F>
where
F: FnMut(<Ev as EventDescriptor>::EventType) + 'static,
Ev: EventDescriptor + 'static,
{
use_event_listener(self.audio_element.clone(), event, handler)
}
pub fn use_statechange<F>(&self, handler: F) -> impl Fn() + Clone + Send + Sync
where
F: FnMut(<media_events::statechange as EventDescriptor>::EventType) + 'static,
{
use_event_listener(self.context.clone(), media_events::statechange, handler)
}
}
#[derive(thiserror::Error, Debug, Clone)]
pub enum MediaPlayerError {
#[error("todo")]
Todo,
}
impl From<web_sys::wasm_bindgen::JsValue> for MediaPlayerError {
fn from(value: web_sys::wasm_bindgen::JsValue) -> Self {
logging::error!("Some kind of error");
Self::Todo {}
}
}
pub mod media_events {
use leptos::ev::EventDescriptor;
use std::borrow::Cow;
#[derive(Copy, Clone, Debug)]
#[allow(non_camel_case_types)]
pub struct error;
impl EventDescriptor for error {
type EventType = web_sys::Event;
const BUBBLES: bool = false;
#[inline(always)]
fn name(&self) -> Cow<'static, str> {
"error".into()
}
#[inline(always)]
fn event_delegation_key(&self) -> Cow<'static, str> {
"$$$error".into()
}
}
#[derive(Copy, Clone, Debug)]
#[allow(non_camel_case_types)]
pub struct statechange;
impl EventDescriptor for statechange {
type EventType = web_sys::Event;
const BUBBLES: bool = false;
#[inline(always)]
fn name(&self) -> Cow<'static, str> {
"statechange".into()
}
#[inline(always)]
fn event_delegation_key(&self) -> Cow<'static, str> {
"$$$statechange".into()
}
}
}

View file

@ -46,11 +46,8 @@ iv.$family-monospace: "Hack", iv.$family-monospace;
@forward "bulma/sass/grid/columns"; @forward "bulma/sass/grid/columns";
@forward "bulma/sass/helpers/typography"; @forward "bulma/sass/helpers";
@forward "bulma/sass/helpers/color"; @forward "bulma/sass/layout";
@forward "bulma/sass/layout/container";
@forward "bulma/sass/layout/section";
@forward "bulma/sass/components/navbar" with ( @forward "bulma/sass/components/navbar" with (
$navbar-burger-color: iv.$grey-light, $navbar-burger-color: iv.$grey-light,

View file

@ -5,7 +5,7 @@ let packages_with_updatescript = (
| from json | from json
| $in.packages.x86_64-linux | $in.packages.x86_64-linux
| columns | columns
| where {|p| nix eval $'.#($p)' --apply 'builtins.hasAttr "updateScript"' | $in == 'true' } | filter {|p| nix eval $'.#($p)' --apply 'builtins.hasAttr "updateScript"' | $in == 'true' }
) )
for $package in $packages_with_updatescript { for $package in $packages_with_updatescript {