This commit is contained in:
암냥 2026-09-10 01:12:27 +09:00
commit 5a58e0e55b
No known key found for this signature in database
8 changed files with 84 additions and 1235 deletions

1116
flake.lock generated

File diff suppressed because it is too large Load diff

View file

@ -36,6 +36,8 @@
nixcord.url = "github:4evy/nixcord";
concord.url = "github:chojs23/concord";
vscode-nigo = {
url = "git+https://git.pari.ng/imnyang/vscode-nigo.git?ref=main";
inputs.nixpkgs.follows = "nixpkgs";

View file

@ -6,6 +6,34 @@
let
nr = import "${inputs.self}/modules/mizukios/features/packages/nr.nix" { inherit pkgs; };
nrr = import "${inputs.self}/modules/mizukios/features/packages/nrr.nix" { inherit pkgs; };
concord = pkgs.writeShellScriptBin "concord" ''
set -eu
runtime_dir="''${XDG_RUNTIME_DIR:-/tmp}"
session_dir="$(mktemp -d "$runtime_dir/concord-session.XXXXXX")"
cleanup() {
rm -rf -- "$session_dir"
}
trap cleanup EXIT
trap 'exit 129' HUP
trap 'exit 130' INT
trap 'exit 143' TERM
config_home="$session_dir/config"
state_home="$session_dir/state"
mkdir -p "$config_home" "$state_home"
cat > "$config_home/config.toml" <<'EOF'
[credentials]
store = "plain"
EOF
XDG_CONFIG_HOME="$config_home" \
XDG_STATE_HOME="$state_home" \
"${inputs.concord.packages.${pkgs.system}.default}/bin/concord" "$@"
'';
in
{
home.stateVersion = "26.05";
@ -13,6 +41,7 @@ in
home.homeDirectory = "/home/imnyang";
home.packages = [
concord
nr
nrr
];

View file

@ -95,8 +95,8 @@ let
'';
};
# These were previously served by *.imnya.ng. Listing them explicitly lets
# stock Caddy obtain an individual certificate without a DNS challenge.
# Keep these explicit hosts for their existing per-host content. The
# wildcard host below handles any other one-level subdomain.
staticHosts = [
"26layer7beginner.imnya.ng"
"alt.imnya.ng"
@ -149,7 +149,20 @@ in
enable = true;
email = "imnyang@pm.me";
openFirewall = true;
package = pkgs.caddy;
package = pkgs.caddy.withPlugins {
plugins = [
"github.com/caddy-dns/cloudflare@v0.2.4"
"github.com/aksdb/caddy-cgi/v2@v2.2.6"
"github.com/shift72/caddy-geo-ip@v0.6.0"
"github.com/lolPants/caddy-requestid@v1.1.2"
"github.com/WeidiDeng/caddy-cloudflare-ip@v0.0.0-20231130002422-f53b62aa13cb"
"github.com/caddyserver/ntlm-transport@v0.1.2"
"github.com/ueffel/caddy-brotli@v1.6.0"
"github.com/RussellLuo/caddy-ext/ratelimit@v0.3.0"
"github.com/neodyme-labs/user_agent_parse@v0.0.1"
];
hash = "sha256-r4MgRk8HpKYx7f8FmA3PKgD/5ZNTIHpQvlE+zRbP0lw=";
};
globalConfig = ''
admin unix//run/caddy/admin.socket
@ -170,6 +183,15 @@ in
}) staticHosts
)
// {
"*.imnya.ng".extraConfig = withErrors ''
tls {
dns cloudflare {env.CLOUDFLARE_API_TOKEN}
}
root * /var/static/{host}
file_server
'';
"api.imnya.ng".extraConfig = reverseProxyWithHeaders "127.0.0.1:10118";
"docs.imnya.ng".extraConfig = reverseProxy "100.70.1.1:3939";
"panel.imnya.ng".extraConfig = reverseProxy "127.0.0.1:32981";
@ -372,6 +394,9 @@ in
requires = [ "mnt-static.mount" ];
after = [ "mnt-static.mount" "systemd-tmpfiles-setup.service" ];
serviceConfig = {
# Keep the manually provisioned token outside NixOS-managed /etc.
# /var/lib/caddy is persistent across system generations.
EnvironmentFile = "/var/lib/caddy/cloudflare.env";
RuntimeDirectory = "caddy";
RuntimeDirectoryMode = "0755";
};

View file

@ -82,7 +82,7 @@
DISABLE_REGISTRATION = false;
ALLOW_ONLY_EXTERNAL_REGISTRATION = true;
ENABLE_CAPTCHA = false;
REQUIRE_SIGNIN_VIEW = false;
REQUIRE_SIGNIN_VIEW = lib.mkForce false;
DEFAULT_KEEP_EMAIL_PRIVATE = false;
DEFAULT_ALLOW_CREATE_ORGANIZATION = true;
DEFAULT_ENABLE_TIMETRACKING = true;

View file

@ -2,6 +2,7 @@
let
inherit (config.virtualisation.quadlet) containers networks;
authentikVersion = "2026.5.6";
authentikEnv = config.sops.secrets."containers/authentik.env".path;
outpostEnv = config.sops.secrets."containers/authentik-outpost.env".path;
in
@ -53,7 +54,7 @@ in
authentik-server = {
containerConfig = {
image = "ghcr.io/goauthentik/server:2026.5.0";
image = "ghcr.io/goauthentik/server:${authentikVersion}";
exec = "server";
networks = [ networks.authentik.ref ];
publishPorts = [
@ -65,6 +66,11 @@ in
"/home/imnyang/Docker/authentik/custom-templates:/templates:Z"
];
environmentFiles = [ authentikEnv ];
environments = {
# Authentik 2026.5 defaults to [::], but hako is reached over IPv4.
AUTHENTIK_LISTEN__HTTP = "0.0.0.0:9000";
AUTHENTIK_LISTEN__HTTPS = "0.0.0.0:9443";
};
};
unitConfig = {
@ -77,7 +83,7 @@ in
authentik-worker = {
containerConfig = {
image = "ghcr.io/goauthentik/server:2026.5.0";
image = "ghcr.io/goauthentik/server:${authentikVersion}";
exec = "worker";
user = "root";
networks = [ networks.authentik.ref ];
@ -85,9 +91,11 @@ in
"/home/imnyang/Docker/authentik/certs:/certs:Z"
"/home/imnyang/Docker/authentik/media:/media:Z"
"/home/imnyang/Docker/authentik/custom-templates:/templates:Z"
"/var/run/docker.sock:/var/run/docker.sock"
];
environmentFiles = [ authentikEnv ];
environments = {
AUTHENTIK_LISTEN__HTTP = "0.0.0.0:9000";
};
};
unitConfig = {
@ -100,7 +108,7 @@ in
authentik-outpost-ldap = {
containerConfig = {
image = "ghcr.io/goauthentik/ldap:2026.5.0";
image = "ghcr.io/goauthentik/ldap:${authentikVersion}";
entrypoint = [ "/ldap" ];
user = "1000";
networks = [ networks.authentik.ref ];
@ -109,6 +117,15 @@ in
"636:6636"
];
environmentFiles = [ outpostEnv ];
environments = {
# Keep the outpost-to-core API traffic on the private Podman network.
# The public hostname resolves through Cloudflare and hairpins back
# through the reverse proxy, which currently returns 502 from hako.
AUTHENTIK_HOST = "http://authentik-server:9000";
AUTHENTIK_INSECURE = "true";
AUTHENTIK_LISTEN__LDAP = "0.0.0.0:3389";
AUTHENTIK_LISTEN__LDAPS = "0.0.0.0:6636";
};
};
unitConfig = {

View file

@ -1,95 +1,4 @@
{ pkgs, ... }:
let
streamPlayer = pkgs.writeTextDir "index.html" ''
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Mizuki Stream</title>
<style>
html,
body {
margin: 0;
min-height: 100%;
background: #0d0f12;
color: #f5f3ee;
font-family: system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}
main {
min-height: 100vh;
display: grid;
grid-template-rows: 1fr auto;
gap: 12px;
padding: 12px;
}
video {
width: 100%;
max-height: calc(100vh - 72px);
aspect-ratio: 16 / 9;
align-self: center;
background: #000;
border: 1px solid #2f343c;
border-radius: 8px;
}
footer {
display: flex;
justify-content: space-between;
gap: 12px;
color: #a9acb3;
font-size: 14px;
}
code {
color: #f5f3ee;
}
@media (max-width: 720px) {
footer {
flex-direction: column;
}
}
</style>
</head>
<body>
<main>
<video id="video" controls autoplay playsinline></video>
<footer>
<span id="stream"></span>
<code>rtmp://stream.mizuki.guru:1935/live</code>
</footer>
</main>
<script src="https://cdn.jsdelivr.net/npm/hls.js@latest"></script>
<script>
var video = document.getElementById("video");
var label = document.getElementById("stream");
var match = window.location.pathname.match(/^\/play\/([^/]+)\/?$/);
var name = match ? decodeURIComponent(match[1]) : "stream";
var source = "https://stream.mizuki.guru/live/" + name + "/" + name + ".m3u8";
label.textContent = source;
if (video.canPlayType("application/vnd.apple.mpegurl")) {
video.src = source;
video.play();
} else if (Hls.isSupported()) {
var hls = new Hls();
hls.loadSource(source);
hls.attachMedia(video);
hls.on(Hls.Events.MANIFEST_PARSED, function () {
video.play();
});
}
</script>
</body>
</html>
'';
in
{
services.caddy = {
enable = true;
@ -182,25 +91,8 @@ in
reverse_proxy 127.0.0.1:31774
'';
virtualHosts."stream.mizuki.guru".extraConfig = ''
handle /live/* {
reverse_proxy 127.0.0.1:10023
}
handle /play/* {
root * ${streamPlayer}
rewrite * /index.html
file_server
}
handle / {
root * ${streamPlayer}
file_server
}
handle {
respond "Not Found" 404
}
virtualHosts."pass.imnya.ng".extraConfig = ''
reverse_proxy 127.0.0.1:10302
'';
virtualHosts."netbird.mizuki.guru".extraConfig = ''

View file

@ -31,7 +31,7 @@ in
src = builtins.fetchTarball {
url = "https://code.visualstudio.com/sha/download?build=insider&os=linux-x64";
sha256 = "0b1127828nk7hr1j66vwi98ip6zwzwrnacqpsa5klnw1xagz52aj";
sha256 = "1d2my418b5np4m5pch3s06gfq6b8676j983b43rqxqsck8j7fzyy";
};
buildInputs = oldAttrs.buildInputs ++ [