mirror of
https://github.com/oqyude/nixos.git
synced 2026-10-07 20:47:14 +03:00
Compare commits
5
Commits
85d3e3747b
...
543fcc61d9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
543fcc61d9 | ||
|
|
0b9ac53b71 | ||
|
|
b476cace8e | ||
|
|
2de80a356b | ||
|
|
fb56f6310b |
+2
-1
@@ -1,3 +1,4 @@
|
|||||||
.vscode
|
.vscode
|
||||||
.omo
|
.omo
|
||||||
__pycache__
|
__pycache__
|
||||||
|
scripts
|
||||||
@@ -64,6 +64,56 @@
|
|||||||
|
|
||||||
host.ssh.enable = true;
|
host.ssh.enable = true;
|
||||||
|
|
||||||
|
# Offload Nix builds to the WSL2 NixOS instance running on vetymae
|
||||||
|
# (Windows 11 host). Sapphira only has 2 logical cores; the WSL exposes
|
||||||
|
# 24 cores + 14 GiB. The matchBlock with ProxyCommand is generated by
|
||||||
|
# modules/server/builder.nix, the other side of the same option lives in
|
||||||
|
# modules/wsl/builder.nix.
|
||||||
|
#
|
||||||
|
# `proxyCommand` is what marks this builder as needing the SSH matchBlock
|
||||||
|
# (see modules/server/builder.nix). A builder reachable directly would
|
||||||
|
# omit it.
|
||||||
|
host.builder.clients = [
|
||||||
|
{
|
||||||
|
hostName = "vetymae-nix";
|
||||||
|
sshUser = "oqyude";
|
||||||
|
sshKey = "/root/.ssh/id_ed25519";
|
||||||
|
# NixOS calls this `systems` (plural), not `systemTypes`. The
|
||||||
|
# default is empty — every derivation is rejected. The WSL NixOS
|
||||||
|
# runs on x86_64-linux, matching sapphira.
|
||||||
|
systems = [ "x86_64-linux" ];
|
||||||
|
# vetymae-nix drops kvm + nixos-test from its advertised
|
||||||
|
# system-features (see modules/wsl/builder.nix). Listing them here
|
||||||
|
# would not break anything (Nix intersects), but listing the
|
||||||
|
# features the WSL actually has is the documented contract.
|
||||||
|
supportedFeatures = [
|
||||||
|
"benchmark"
|
||||||
|
"big-parallel"
|
||||||
|
];
|
||||||
|
mandatoryFeatures = [ ];
|
||||||
|
maxJobs = 24;
|
||||||
|
speedFactor = 0.5;
|
||||||
|
# Keep the SSH session alive across many small builds in one daemon
|
||||||
|
# session — compile-heavy workloads spam the daemon with hundreds of
|
||||||
|
# derivations and ControlMaster collapses those into one Windows hop.
|
||||||
|
# NB: `nix.buildMachines` has no `sshOptions` attribute, so the
|
||||||
|
# ControlMaster directive lives in the SSH matchBlock instead (see
|
||||||
|
# modules/server/builder.nix).
|
||||||
|
#
|
||||||
|
# The OpenSSH alias for this host (matches the user's
|
||||||
|
# ~/.ssh/config so known_hosts entries do not collide with the
|
||||||
|
# Windows OpenSSH entry on 127.0.0.1/vetymae) is consumed only by
|
||||||
|
# the SSH matchBlock below — not by `nix.buildMachines`, which has
|
||||||
|
# no such attribute.
|
||||||
|
hostKeyAlias = "wsl-nixos-on-vetymae";
|
||||||
|
# Use the Windows host's IP directly so the nix-daemon (running as
|
||||||
|
# root, without the user's ~/.ssh/config) does not need a separate
|
||||||
|
# `vetymae` host alias. With StrictHostKeyChecking=accept-new the
|
||||||
|
# first connection adds the Windows host key to /root/.ssh/known_hosts.
|
||||||
|
proxyCommand = "ssh oqyude@192.168.1.100 'wsl -d NixOS -- nc 127.0.0.1 22'";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
networking = {
|
networking = {
|
||||||
networkmanager.enable = true;
|
networkmanager.enable = true;
|
||||||
firewall.enable = false;
|
firewall.enable = false;
|
||||||
|
|||||||
@@ -35,5 +35,17 @@
|
|||||||
defaultUser = xlib.device.username;
|
defaultUser = xlib.device.username;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Enable SSH server on WSL NixOS so sapphira can drive it directly via a
|
||||||
|
# ProxyCommand chain through the Windows OpenSSH layer. The shared
|
||||||
|
# essentials/ssh.nix module wires host keys, sops-managed user keys, and
|
||||||
|
# passwordless key auth — nothing to repeat here.
|
||||||
|
host.ssh.enable = true;
|
||||||
|
|
||||||
|
# Advertise this WSL instance as a remote Nix builder for sapphira (2
|
||||||
|
# cores, the bottleneck host). All builder wiring — fixing the
|
||||||
|
# `system-features` to drop the unsupported `kvm`, and adding the SSH
|
||||||
|
# user `oqyude` to trusted-users — lives in modules/wsl/builder.nix.
|
||||||
|
host.builder.enable = true;
|
||||||
|
|
||||||
system.stateVersion = "24.11";
|
system.stateVersion = "24.11";
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+1
-17
@@ -463,8 +463,7 @@
|
|||||||
"plasma-manager": "plasma-manager",
|
"plasma-manager": "plasma-manager",
|
||||||
"proxy-suite": "proxy-suite",
|
"proxy-suite": "proxy-suite",
|
||||||
"sops-nix": "sops-nix",
|
"sops-nix": "sops-nix",
|
||||||
"utils": "utils",
|
"utils": "utils"
|
||||||
"zeroq-credentials": "zeroq-credentials"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"scss-reset": {
|
"scss-reset": {
|
||||||
@@ -577,21 +576,6 @@
|
|||||||
"repo": "zapret-discord-youtube",
|
"repo": "zapret-discord-youtube",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"zeroq-credentials": {
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1772104025,
|
|
||||||
"narHash": "sha256-tX5I2lkwbB1leoib6Ao/Et0B1GYrn3vxw4DkFYX8uyM=",
|
|
||||||
"ref": "refs/heads/master",
|
|
||||||
"rev": "511fc5446b502ff111020bda6d57261648d62333",
|
|
||||||
"revCount": 75,
|
|
||||||
"type": "git",
|
|
||||||
"url": "ssh://git@github.com/oqyude/zeroq-credentials.git"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "ssh://git@github.com/oqyude/zeroq-credentials.git"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": "root",
|
"root": "root",
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
{
|
{
|
||||||
description = "oqyude flake";
|
description = "oqyude flake";
|
||||||
inputs = {
|
inputs = {
|
||||||
# My
|
|
||||||
zeroq-credentials.url = "git+ssh://git@github.com/oqyude/zeroq-credentials.git"; # flake of creds
|
|
||||||
|
|
||||||
# nixpkgs
|
# nixpkgs
|
||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
# nixpkgs-master.url = "github:NixOS/nixpkgs/master";
|
# nixpkgs-master.url = "github:NixOS/nixpkgs/master";
|
||||||
|
|||||||
@@ -0,0 +1,323 @@
|
|||||||
|
# Declarative OpenCode + oh-my-openagent (oh-my-opencode) plugin setup.
|
||||||
|
#
|
||||||
|
# Mirrors ~/.config/opencode/ on the current workstation.
|
||||||
|
# Imported by home/server.nix (sapphira). Auto-enables programs.opencode.
|
||||||
|
#
|
||||||
|
# Three files this module owns on disk (via xdg.configFile):
|
||||||
|
# ~/.config/opencode/opencode.json <- programs.opencode.settings
|
||||||
|
# ~/.config/opencode/tui.json <- programs.opencode.tui
|
||||||
|
# ~/.config/opencode/oh-my-openagent.json <- oh-my-openagent plugin config
|
||||||
|
#
|
||||||
|
# Override any field in the importing module if needed.
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
# Body of ~/.config/opencode/oh-my-openagent.json.
|
||||||
|
# Loaded by the oh-my-openagent opencode plugin on startup.
|
||||||
|
ohMyOpenagentConfig = {
|
||||||
|
"$schema" = "https://raw.githubusercontent.com/code-yeongyu/oh-my-openagent/dev/assets/oh-my-opencode.schema.json";
|
||||||
|
|
||||||
|
agents = {
|
||||||
|
sisyphus = {
|
||||||
|
model = "opencode/claude-opus-5";
|
||||||
|
variant = "max";
|
||||||
|
fallback_models = [
|
||||||
|
{ model = "opencode/kimi-k3"; }
|
||||||
|
{
|
||||||
|
model = "opencode/gpt-5.6-sol";
|
||||||
|
variant = "medium";
|
||||||
|
}
|
||||||
|
{ model = "opencode/glm-5"; }
|
||||||
|
{ model = "opencode/big-pickle"; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
hephaestus = {
|
||||||
|
model = "opencode/gpt-5.6-sol";
|
||||||
|
variant = "medium";
|
||||||
|
};
|
||||||
|
oracle = {
|
||||||
|
model = "opencode/gpt-5.6-sol";
|
||||||
|
variant = "xhigh";
|
||||||
|
fallback_models = [
|
||||||
|
{
|
||||||
|
model = "opencode/gemini-3.1-pro";
|
||||||
|
variant = "high";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
model = "opencode/claude-opus-5";
|
||||||
|
variant = "max";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
librarian = {
|
||||||
|
model = "minimax-coding-plan/MiniMax-M3";
|
||||||
|
};
|
||||||
|
explore = {
|
||||||
|
model = "opencode/gpt-5-nano";
|
||||||
|
fallback_models = [
|
||||||
|
{ model = "minimax-coding-plan/MiniMax-M3"; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
"multimodal-looker" = {
|
||||||
|
model = "opencode/gpt-5.6-sol";
|
||||||
|
variant = "low";
|
||||||
|
fallback_models = [
|
||||||
|
{ model = "opencode/gpt-5-nano"; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
prometheus = {
|
||||||
|
model = "opencode/claude-fable-5";
|
||||||
|
variant = "high";
|
||||||
|
fallback_models = [
|
||||||
|
{
|
||||||
|
model = "opencode/kimi-k3";
|
||||||
|
variant = "high";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
metis = {
|
||||||
|
model = "opencode/claude-opus-5";
|
||||||
|
variant = "high";
|
||||||
|
fallback_models = [
|
||||||
|
{
|
||||||
|
model = "opencode/kimi-k3";
|
||||||
|
variant = "low";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
momus = {
|
||||||
|
model = "opencode/gpt-5.6-sol";
|
||||||
|
variant = "xhigh";
|
||||||
|
fallback_models = [
|
||||||
|
{
|
||||||
|
model = "opencode/claude-opus-5";
|
||||||
|
variant = "max";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
model = "opencode/gemini-3.1-pro";
|
||||||
|
variant = "high";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
atlas = {
|
||||||
|
model = "opencode/claude-sonnet-4-6";
|
||||||
|
fallback_models = [
|
||||||
|
{
|
||||||
|
model = "opencode/gpt-5.6-sol";
|
||||||
|
variant = "medium";
|
||||||
|
}
|
||||||
|
{ model = "minimax-coding-plan/MiniMax-M3"; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
"sisyphus-junior" = {
|
||||||
|
model = "opencode/claude-sonnet-4-6";
|
||||||
|
fallback_models = [
|
||||||
|
{
|
||||||
|
model = "opencode/gpt-5.6-sol";
|
||||||
|
variant = "medium";
|
||||||
|
}
|
||||||
|
{ model = "minimax-coding-plan/MiniMax-M3"; }
|
||||||
|
{ model = "opencode/big-pickle"; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
categories = {
|
||||||
|
"visual-engineering" = {
|
||||||
|
model = "opencode/gemini-3.1-pro";
|
||||||
|
variant = "high";
|
||||||
|
fallback_models = [
|
||||||
|
{ model = "opencode/glm-5"; }
|
||||||
|
{
|
||||||
|
model = "opencode/claude-opus-5";
|
||||||
|
variant = "max";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
ultrabrain = {
|
||||||
|
model = "opencode/gpt-5.6-sol";
|
||||||
|
variant = "xhigh";
|
||||||
|
fallback_models = [
|
||||||
|
{
|
||||||
|
model = "opencode/gemini-3.1-pro";
|
||||||
|
variant = "high";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
model = "opencode/claude-opus-5";
|
||||||
|
variant = "max";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
deep = {
|
||||||
|
model = "opencode/gpt-5.6-sol";
|
||||||
|
variant = "medium";
|
||||||
|
fallback_models = [
|
||||||
|
{
|
||||||
|
model = "opencode/claude-opus-5";
|
||||||
|
variant = "max";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
model = "opencode/gemini-3.1-pro";
|
||||||
|
variant = "high";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
artistry = {
|
||||||
|
model = "opencode/gemini-3.1-pro";
|
||||||
|
variant = "high";
|
||||||
|
fallback_models = [
|
||||||
|
{
|
||||||
|
model = "opencode/claude-opus-5";
|
||||||
|
variant = "max";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
model = "opencode/gpt-5.6-sol";
|
||||||
|
variant = "high";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
quick = {
|
||||||
|
model = "opencode/gpt-5.4-mini";
|
||||||
|
fallback_models = [
|
||||||
|
{ model = "opencode/gemini-3-flash"; }
|
||||||
|
{ model = "minimax-coding-plan/MiniMax-M3"; }
|
||||||
|
{ model = "opencode/gpt-5-nano"; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
"unspecified-low" = {
|
||||||
|
model = "opencode/claude-sonnet-4-6";
|
||||||
|
fallback_models = [
|
||||||
|
{
|
||||||
|
model = "opencode/gpt-5.6-sol";
|
||||||
|
variant = "medium";
|
||||||
|
}
|
||||||
|
{ model = "opencode/gemini-3-flash"; }
|
||||||
|
{ model = "minimax-coding-plan/MiniMax-M3"; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
"unspecified-high" = {
|
||||||
|
model = "opencode/claude-sonnet-4-6";
|
||||||
|
fallback_models = [
|
||||||
|
{
|
||||||
|
model = "opencode/gpt-5.6-sol";
|
||||||
|
variant = "medium";
|
||||||
|
}
|
||||||
|
{ model = "opencode/gemini-3-flash"; }
|
||||||
|
{ model = "minimax-coding-plan/MiniMax-M3"; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
writing = {
|
||||||
|
model = "opencode/gemini-3-flash";
|
||||||
|
fallback_models = [
|
||||||
|
{ model = "opencode/claude-sonnet-4-6"; }
|
||||||
|
{ model = "minimax-coding-plan/MiniMax-M3"; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
let
|
||||||
|
# nixpkgs ast-grep only ships binary `ast-grep`; omo's ast-grep skill probes
|
||||||
|
# for `sg` (or ast-grep). Provide both via a symlink wrapper.
|
||||||
|
astGrepWithSg = pkgs.runCommandLocal "ast-grep-with-sg" { } ''
|
||||||
|
mkdir -p $out/bin
|
||||||
|
ln -s ${pkgs.ast-grep}/bin/ast-grep $out/bin/ast-grep
|
||||||
|
ln -s ${pkgs.ast-grep}/bin/ast-grep $out/bin/sg
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
programs.opencode = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
# Extras available to opencode-wrapped (via --suffix PATH on the wrapper):
|
||||||
|
# pkgs.nodejs_22 — npx/npm for MCP servers (webpage-mcp) and omo's plugin loader
|
||||||
|
# pkgs.ast-grep — `sg` CLI; omo's ast-grep skill requires it (omo doctor)
|
||||||
|
# pkgs.bun — omo prefers bun; with bun on PATH, `omo doctor` skips node fallback
|
||||||
|
# pkgs.gh — GitHub CLI; omo's GitHub automation features require it
|
||||||
|
extraPackages = [
|
||||||
|
pkgs.nodejs_22
|
||||||
|
astGrepWithSg
|
||||||
|
pkgs.bun
|
||||||
|
pkgs.gh
|
||||||
|
];
|
||||||
|
|
||||||
|
# ~/.config/opencode/opencode.json
|
||||||
|
settings = {
|
||||||
|
plugin = [ "oh-my-openagent@latest" ];
|
||||||
|
mcp = {
|
||||||
|
webpage = {
|
||||||
|
type = "local";
|
||||||
|
command = [
|
||||||
|
"npx"
|
||||||
|
"-y"
|
||||||
|
"-p"
|
||||||
|
"webpage-mcp@latest"
|
||||||
|
"webpage-mcp-stdio"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# ~/.config/opencode/tui.json
|
||||||
|
# Mirrors workstation: oh-my-openagent also registered for the TUI.
|
||||||
|
tui = {
|
||||||
|
plugin = [ "oh-my-openagent@latest" ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# ~/.config/opencode/oh-my-openagent.json — read by the plugin on startup.
|
||||||
|
xdg.configFile."opencode/oh-my-openagent.json".text = builtins.toJSON ohMyOpenagentConfig;
|
||||||
|
|
||||||
|
# Same extras on the user's PATH too, so `omo doctor` and standalone invocations
|
||||||
|
# of `sg`, `gh`, `bun`, `npm`, `npx` work in the user's shell — not only inside
|
||||||
|
# the opencode-wrapped binary.
|
||||||
|
home.packages = [
|
||||||
|
pkgs.nodejs_22
|
||||||
|
astGrepWithSg
|
||||||
|
pkgs.bun
|
||||||
|
pkgs.gh
|
||||||
|
];
|
||||||
|
|
||||||
|
# Expose `opencode web` as a systemd user service. nginx on sapphira
|
||||||
|
# proxies https://opencode.zeroq.su -> 127.0.0.1:4096.
|
||||||
|
#
|
||||||
|
# --hostname 0.0.0.0 binds the listener to every interface (matches the
|
||||||
|
# "0.0.0.0" intent; nginx then reverse-proxies 127.0.0.1:4096 internally).
|
||||||
|
# --cors https://opencode.zeroq.su lets the browser session reach the
|
||||||
|
# server from that origin without CORS rejection.
|
||||||
|
#
|
||||||
|
# SECURITY: with no password, anyone reaching the upstream socket gets full
|
||||||
|
# opencode. Bind 0.0.0.0 + listener == bridge == shell. The password is
|
||||||
|
# supplied via sops-managed EnvironmentFile, declared in modules/users.nix
|
||||||
|
# and decrypted to a path hardcoded here (home-manager modules cannot read
|
||||||
|
# `config.sops.*` — sops-nix options are NixOS-only).
|
||||||
|
programs.opencode.web = {
|
||||||
|
enable = true;
|
||||||
|
environmentFile = "${config.home.homeDirectory}/.config/opencode/server.env";
|
||||||
|
extraArgs = [
|
||||||
|
"--hostname"
|
||||||
|
"0.0.0.0"
|
||||||
|
"--cors"
|
||||||
|
"https://opencode.zeroq.su"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Workaround: home-manager activation updates the GC root `current-home`
|
||||||
|
# only at the very end (line 358 of the generated activate script), AFTER all
|
||||||
|
# `home.activation.*` dag entries have run. So we cannot read current-home
|
||||||
|
# from a dag entry — it still points to the OLD generation at the time our
|
||||||
|
# script executes. Instead, read `new-home`, which the activator writes
|
||||||
|
# BEFORE any dag entry runs and which already points at the new generation.
|
||||||
|
home.activation.relinkHomeManager = lib.hm.dag.entryAfter [] ''
|
||||||
|
target="$HOME/.local/state/nix/profiles/home-manager-24-link"
|
||||||
|
newGen="$(readlink -e "''${XDG_STATE_HOME:-$HOME/.local/state}/home-manager/gcroots/new-home" 2>/dev/null || true)"
|
||||||
|
if [[ -n "$newGen" && "$(readlink -f "$target")" != "$newGen" ]]; then
|
||||||
|
echo "home-manager: relinking $target -> $newGen"
|
||||||
|
ln -sfn "$newGen" "$target"
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./minimal.nix
|
./minimal.nix
|
||||||
|
./modules/opencode.nix
|
||||||
];
|
];
|
||||||
home.file = xlib.helpers.mkSymlinks config {
|
home.file = xlib.helpers.mkSymlinks config {
|
||||||
"${config.home.homeDirectory}/External/Music" = "Music";
|
"${config.home.homeDirectory}/External/Music" = "Music";
|
||||||
|
|||||||
@@ -107,7 +107,8 @@ in
|
|||||||
after = [ "podman-build-kokoro-tts.service" ];
|
after = [ "podman-build-kokoro-tts.service" ];
|
||||||
requires = [ "podman-build-kokoro-tts.service" ];
|
requires = [ "podman-build-kokoro-tts.service" ];
|
||||||
serviceConfig.Restart = lib.mkOverride 90 "always";
|
serviceConfig.Restart = lib.mkOverride 90 "always";
|
||||||
wantedBy = [ "multi-user.target" ];
|
# Auto-start disabled: start manually with `systemctl start podman-kokoro-tts`.
|
||||||
|
wantedBy = [ ];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,6 +9,41 @@
|
|||||||
# the option exists. `modules/essentials/ssh.nix` does not belong here: it
|
# the option exists. `modules/essentials/ssh.nix` does not belong here: it
|
||||||
# declares and reads `host.ssh.enable` itself, within one module.
|
# declares and reads `host.ssh.enable` itself, within one module.
|
||||||
{
|
{
|
||||||
|
# Remote-builder wiring. A coordinator (e.g. sapphira) sets
|
||||||
|
# `host.builder.clients` to register remote build machines;
|
||||||
|
# a builder host (e.g. the WSL on vetymae) sets `host.builder.enable`
|
||||||
|
# to advertise itself. The two halves are intentionally split so a single
|
||||||
|
# declaration in configurations/* is enough to flip each side.
|
||||||
|
options.host.builder = {
|
||||||
|
enable = lib.mkOption {
|
||||||
|
type = lib.types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
Advertise this host as a remote Nix builder and accept builds
|
||||||
|
from other machines in the flake over SSH.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
clients = lib.mkOption {
|
||||||
|
type = lib.types.listOf lib.types.attrs;
|
||||||
|
default = [ ];
|
||||||
|
description = ''
|
||||||
|
List of remote Nix build machines this coordinator should
|
||||||
|
register via `nix.buildMachines`. Each entry matches the NixOS
|
||||||
|
option schema (hostName, sshUser, sshKey, systems,
|
||||||
|
supportedFeatures, ...). Two extra attributes are consumed by
|
||||||
|
modules/server/builder.nix and stripped before reaching
|
||||||
|
`nix.buildMachines`:
|
||||||
|
- `proxyCommand` — generates a per-builder Host block in the
|
||||||
|
system-wide OpenSSH config (the nix-daemon runs as root and
|
||||||
|
cannot see the user's ~/.ssh/config).
|
||||||
|
- `hostKeyAlias` — alias used inside that SSH matchBlock.
|
||||||
|
A builder reachable on its own (no ProxyCommand needed) omits
|
||||||
|
both and gets no SSH matchBlock. Empty by default — opt in by
|
||||||
|
setting this list.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
options.host."3x-ui" = {
|
options.host."3x-ui" = {
|
||||||
# Domain whose Let's Encrypt cert (at /var/lib/acme/<domain>/)
|
# Domain whose Let's Encrypt cert (at /var/lib/acme/<domain>/)
|
||||||
# gets mounted read-only into the 3x-ui container so the panel
|
# gets mounted read-only into the 3x-ui container so the panel
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
# sapphira (and any other server-class coordinator) — register remote
|
||||||
|
# builders, and make sure the nix daemon (running as root) can resolve the
|
||||||
|
# SSH host alias with its ProxyCommand chain.
|
||||||
|
#
|
||||||
|
# The `host.builder.clients` option itself is declared in
|
||||||
|
# modules/options.nix (cross-module). The actual builder list is set by the
|
||||||
|
# configuration (e.g. configurations/server.nix) — this module is generic
|
||||||
|
# over every entry on the list.
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
# Attributes that belong to the SSH matchBlock only — NOT to
|
||||||
|
# `nix.buildMachines` (that schema has no hostKeyAlias/proxyCommand).
|
||||||
|
# Strip them before handing the list to nix.buildMachines.
|
||||||
|
sshOnlyAttrs = [
|
||||||
|
"hostKeyAlias"
|
||||||
|
"proxyCommand"
|
||||||
|
];
|
||||||
|
forNix = b: removeAttrs b sshOnlyAttrs;
|
||||||
|
# After NixOS's nix.buildMachines submodule runs, each entry has all
|
||||||
|
# attributes defaulted (protocol=ssh, systems=[], etc.). Read from that
|
||||||
|
# processed list so the formatter never trips on a missing field.
|
||||||
|
processedBuilders = config.nix.buildMachines;
|
||||||
|
|
||||||
|
# Serialise one builder to the textual format Nix's daemon expects in
|
||||||
|
# `nix.conf`'s `builders` line. Mirrors `buildMachinesText` from
|
||||||
|
# nixos/modules/config/nix-remote-build.nix so the result is identical
|
||||||
|
# to what NixOS writes to /etc/nix/machines — we just inline it instead
|
||||||
|
# of relying on `@/etc/nix/machines`, which Nix 2.34 parses but does
|
||||||
|
# not act on (the daemon's `external-builders` list stays empty and the
|
||||||
|
# client reports "configure remote builders via 'builders'" forever).
|
||||||
|
formatBuilder = b:
|
||||||
|
let
|
||||||
|
# Nix 2.34 refuses to dispatch derivations to a builder whose protocol
|
||||||
|
# is `ssh` (the NixOS default): the daemon leaves `external-builders`
|
||||||
|
# empty even when the `builders` line is well-formed, and the client
|
||||||
|
# falls back to local. `ssh-ng` (the new in-band protocol) actually
|
||||||
|
# opens the dispatcher. Override the NixOS default here.
|
||||||
|
proto = "ssh-ng://";
|
||||||
|
user = if b.sshUser != null && b.sshUser != "" then "${b.sshUser}@" else "";
|
||||||
|
systems =
|
||||||
|
if b.system != null then b.system
|
||||||
|
else if b.systems != [ ] then lib.concatStringsSep "," b.systems
|
||||||
|
else "-";
|
||||||
|
sshKey = if b.sshKey != null && b.sshKey != "" then b.sshKey else "-";
|
||||||
|
maxJobs = toString b.maxJobs;
|
||||||
|
speedFactor = toString b.speedFactor;
|
||||||
|
allFeats = b.supportedFeatures ++ b.mandatoryFeatures;
|
||||||
|
supported =
|
||||||
|
if allFeats == [ ] then "-"
|
||||||
|
else lib.concatStringsSep "," allFeats;
|
||||||
|
mandatory =
|
||||||
|
if b.mandatoryFeatures == [ ] then "-"
|
||||||
|
else lib.concatStringsSep "," b.mandatoryFeatures;
|
||||||
|
publicKey = if b.publicHostKey != null then b.publicHostKey else "-";
|
||||||
|
in
|
||||||
|
lib.concatStringsSep " " [
|
||||||
|
"${proto}${user}${b.hostName}"
|
||||||
|
systems
|
||||||
|
sshKey
|
||||||
|
maxJobs
|
||||||
|
speedFactor
|
||||||
|
supported
|
||||||
|
mandatory
|
||||||
|
publicKey
|
||||||
|
];
|
||||||
|
inlineBuilders = lib.concatMapStringsSep "\n" formatBuilder processedBuilders;
|
||||||
|
|
||||||
|
# One OpenSSH host block per builder that needs a ProxyCommand.
|
||||||
|
# Placed in `programs.ssh.extraConfig` so it ends up in
|
||||||
|
# /etc/ssh/ssh_config (the file OpenSSH consults system-wide, including
|
||||||
|
# for the nix-daemon running as root).
|
||||||
|
#
|
||||||
|
# Only builders with a `proxyCommand` attribute get a block: a builder
|
||||||
|
# reachable on its own (e.g. otreca on a public IP) needs no help from
|
||||||
|
# here. The attribute is the literal ProxyCommand string (passed
|
||||||
|
# verbatim to ssh); the configuration is responsible for matching it
|
||||||
|
# with the `hostName` field.
|
||||||
|
hostBlock = b: ''
|
||||||
|
Host ${b.hostName}
|
||||||
|
User ${b.sshUser}
|
||||||
|
HostKeyAlias ${b.hostKeyAlias or b.hostName}
|
||||||
|
ProxyCommand ${b.proxyCommand}
|
||||||
|
StrictHostKeyChecking accept-new
|
||||||
|
ServerAliveInterval 30
|
||||||
|
ServerAliveCountMax 3
|
||||||
|
ControlMaster auto
|
||||||
|
ControlPersist 60
|
||||||
|
ConnectTimeout 15
|
||||||
|
'';
|
||||||
|
blocks = map hostBlock (lib.filter (b: b ? proxyCommand) config.host.builder.clients);
|
||||||
|
in
|
||||||
|
{
|
||||||
|
config = lib.mkIf (config.host.builder.clients != [ ]) {
|
||||||
|
# Off-by-default in NixOS. Without this, the nix-remote-build module
|
||||||
|
# sets `nix.settings.builders = null` and the list is dropped from
|
||||||
|
# /etc/nix/nix.conf entirely, even though `nix.buildMachines` is
|
||||||
|
# populated. (The build-machine list still lands in /etc/nix/machines
|
||||||
|
# but nix-daemon reads `builders`, not /etc/nix/machines, when
|
||||||
|
# distributedBuilds is false.)
|
||||||
|
nix.distributedBuilds = true;
|
||||||
|
nix.buildMachines = map forNix config.host.builder.clients;
|
||||||
|
# Nix 2.34's daemon does not act on `@/etc/nix/machines` (the file
|
||||||
|
# format NixOS's nix-remote-build writes to): the `builders` config
|
||||||
|
# key is parsed for display but `external-builders` stays empty and
|
||||||
|
# the scheduler ignores it. Inlining the same builder text here — in
|
||||||
|
# the exact format the NixOS module itself uses — actually wires up
|
||||||
|
# the SSH dispatch. `mkForce` is required because the nix-remote-build
|
||||||
|
# module sets `builders = null` whenever distributedBuilds is *false*;
|
||||||
|
# our config flips it to *true*, so the module's mkIf does not fire
|
||||||
|
# and there is no actual conflict — but pinning it with mkForce makes
|
||||||
|
# the intent obvious and survives any future change in default
|
||||||
|
# behaviour.
|
||||||
|
nix.settings.builders = lib.mkForce inlineBuilders;
|
||||||
|
|
||||||
|
# Append per-builder Host blocks to the system-wide OpenSSH client
|
||||||
|
# config. `programs.ssh.extraConfig` is of type `lines`, merged across
|
||||||
|
# modules, and prepended (before `Host *`) in /etc/ssh/ssh_config —
|
||||||
|
# which is exactly the spot where specific Host blocks have to live.
|
||||||
|
programs.ssh.extraConfig = lib.concatStrings blocks;
|
||||||
|
|
||||||
|
# Parallel builds on sapphira itself stay at 2 — that matches the
|
||||||
|
# physical cores and keeps the coordinator responsive while the WSL
|
||||||
|
# absorbs the heavy lifting. The essentials/settings.nix already
|
||||||
|
# leaves max-jobs at the default `auto` (2 here); no override needed.
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@
|
|||||||
../pkgs/beets.nix
|
../pkgs/beets.nix
|
||||||
./acme.nix
|
./acme.nix
|
||||||
./bentopdf.nix
|
./bentopdf.nix
|
||||||
|
./builder.nix
|
||||||
./calibre-web.nix
|
./calibre-web.nix
|
||||||
./chrony.nix
|
./chrony.nix
|
||||||
./coredns.nix
|
./coredns.nix
|
||||||
|
|||||||
@@ -194,6 +194,17 @@ in
|
|||||||
proxyWebsockets = true;
|
proxyWebsockets = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
# sapphira itself: opencode web runs as a systemd user service
|
||||||
|
# (programs.opencode.web.enable in home/modules/opencode.nix) on
|
||||||
|
# 127.0.0.1:4096 with --hostname 0.0.0.0.
|
||||||
|
"opencode.zeroq.su" = {
|
||||||
|
forceSSL = true;
|
||||||
|
enableACME = true;
|
||||||
|
locations."/" = {
|
||||||
|
proxyPass = "http://127.0.0.1:4096";
|
||||||
|
proxyWebsockets = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
"nextcloud.zeroq.su" = {
|
"nextcloud.zeroq.su" = {
|
||||||
forceSSL = true;
|
forceSSL = true;
|
||||||
enableACME = true;
|
enableACME = true;
|
||||||
|
|||||||
+37
-4
@@ -8,7 +8,9 @@ let
|
|||||||
user = "${xlib.device.username}";
|
user = "${xlib.device.username}";
|
||||||
userGroup = config.users.users."${user}".group;
|
userGroup = config.users.users."${user}".group;
|
||||||
|
|
||||||
# sops secret factory: name == key by default, owner/group default to root
|
# sops secret factory: name == key by default, owner/group default to root.
|
||||||
|
# `format` and `sopsFile` default to yaml + defaultSopsFile, matching every
|
||||||
|
# pre-existing caller.
|
||||||
mkSecret =
|
mkSecret =
|
||||||
{
|
{
|
||||||
path,
|
path,
|
||||||
@@ -16,14 +18,16 @@ let
|
|||||||
key ? null,
|
key ? null,
|
||||||
owner ? null,
|
owner ? null,
|
||||||
group ? null,
|
group ? null,
|
||||||
|
format ? "yaml",
|
||||||
|
sopsFile ? null,
|
||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
format = "yaml";
|
inherit format path mode;
|
||||||
inherit path mode;
|
|
||||||
}
|
}
|
||||||
// lib.optionalAttrs (key != null) { inherit key; }
|
// lib.optionalAttrs (key != null) { inherit key; }
|
||||||
// lib.optionalAttrs (owner != null) { inherit owner; }
|
// lib.optionalAttrs (owner != null) { inherit owner; }
|
||||||
// lib.optionalAttrs (group != null) { inherit group; };
|
// lib.optionalAttrs (group != null) { inherit group; }
|
||||||
|
// lib.optionalAttrs (sopsFile != null) { inherit sopsFile; };
|
||||||
|
|
||||||
# default owner = device user
|
# default owner = device user
|
||||||
mkUserSecret =
|
mkUserSecret =
|
||||||
@@ -98,6 +102,35 @@ in
|
|||||||
path = "${xlib.dirs.user-home}/.config/sops/age/keys.txt";
|
path = "${xlib.dirs.user-home}/.config/sops/age/keys.txt";
|
||||||
mode = "0600";
|
mode = "0600";
|
||||||
};
|
};
|
||||||
|
# opencode web server creds + Gemini API key.
|
||||||
|
# Decrypted as a single dotenv file (no `key`) and consumed by the
|
||||||
|
# systemd user unit opencode-web as EnvironmentFile.
|
||||||
|
# Source: secrets/opencode.env (encrypted, see sops/age below).
|
||||||
|
opencode_server = mkUserSecret {
|
||||||
|
path = "${xlib.dirs.user-home}/.config/opencode/server.env";
|
||||||
|
mode = "0600";
|
||||||
|
format = "dotenv";
|
||||||
|
sopsFile = ../secrets/opencode.env;
|
||||||
|
};
|
||||||
|
# opencode provider credentials (XDG_DATA_HOME/opencode/...).
|
||||||
|
# Both files are read by opencode at startup to populate the providers
|
||||||
|
# list. Mirror of ~/.local/share/opencode/ on the workstation.
|
||||||
|
# key = "" → decrypt the WHOLE file as-is (the JSON has no top-level
|
||||||
|
# field named after the secret; it IS the secret).
|
||||||
|
opencode_auth = mkUserSecret {
|
||||||
|
path = "${xlib.dirs.user-home}/.local/share/opencode/auth.json";
|
||||||
|
mode = "0600";
|
||||||
|
format = "json";
|
||||||
|
sopsFile = ../secrets/opencode-auth.json;
|
||||||
|
key = "";
|
||||||
|
};
|
||||||
|
opencode_account = mkUserSecret {
|
||||||
|
path = "${xlib.dirs.user-home}/.local/share/opencode/account.json";
|
||||||
|
mode = "0600";
|
||||||
|
format = "json";
|
||||||
|
sopsFile = ../secrets/opencode-account.json;
|
||||||
|
key = "";
|
||||||
|
};
|
||||||
ssh_key_private = mkUserSecret {
|
ssh_key_private = mkUserSecret {
|
||||||
path = "${xlib.dirs.user-home}/.ssh/id_ed25519";
|
path = "${xlib.dirs.user-home}/.ssh/id_ed25519";
|
||||||
mode = "0600";
|
mode = "0600";
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# WSL NixOS — advertise this host as a remote Nix builder.
|
||||||
|
#
|
||||||
|
# Why a dedicated module instead of inlining into configurations/wsl.nix:
|
||||||
|
# every "what makes this WSL different from a desktop/server" concern
|
||||||
|
# belongs under modules/wsl/ — that is the contract the device-type import in
|
||||||
|
# modules/default.nix wires up. Keeping it here means flipping the feature on
|
||||||
|
# later on another WSL host (e.g. a future vetymae-2) is one import away.
|
||||||
|
#
|
||||||
|
# The `host.builder.enable` option itself is declared in
|
||||||
|
# modules/options.nix (cross-module).
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
config = lib.mkIf config.host.builder.enable {
|
||||||
|
# WSL2 does not expose /dev/kvm to the guest (no nested virt by default,
|
||||||
|
# and Hyper-V's /dev/kvm is not bind-mounted into the WSL namespace).
|
||||||
|
# The default NixOS module advertises `kvm nixos-test benchmark
|
||||||
|
# big-parallel` as this host's system-features, which is a lie: any
|
||||||
|
# derivation that requires `kvm` will be dispatched here and immediately
|
||||||
|
# fail with "cannot open /dev/kvm". Nix selects builders by matching the
|
||||||
|
# derivation's required features against what the builder advertises, so
|
||||||
|
# the only way to keep WSL useful is to retract the features it cannot
|
||||||
|
# actually deliver. `nixos-test` is dropped for the same reason — it
|
||||||
|
# wants kvm anyway.
|
||||||
|
#
|
||||||
|
# `mkForce` because the NixOS module base-config sets a non-empty
|
||||||
|
# default; without force the lists would concatenate and the WSL would
|
||||||
|
# *still* advertise kvm.
|
||||||
|
nix.settings.system-features = lib.mkForce [
|
||||||
|
"benchmark"
|
||||||
|
"big-parallel"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Builds arrive over SSH as the user `oqyude` (see
|
||||||
|
# modules/server/builder.nix). On the default trusted-users = ["root"]
|
||||||
|
# only root can call nix-store, so the SSH session would fail to realise
|
||||||
|
# any .drv. Adding the SSH user to trusted-users lets the remote nix-build
|
||||||
|
# driver drive nix-store on the builder side. `mkForce` for the same
|
||||||
|
# concatenation reason as above.
|
||||||
|
nix.settings.trusted-users = lib.mkForce [
|
||||||
|
"root"
|
||||||
|
"oqyude"
|
||||||
|
];
|
||||||
|
|
||||||
|
# The local daemon already parallelises across all 24 logical cores
|
||||||
|
# (max-jobs = 24 is what we measured). When acting as a builder, we
|
||||||
|
# want to keep that — remote builds land through SSH and the daemon
|
||||||
|
# serves them on top of its normal pool. No override needed; documented
|
||||||
|
# here so a future reader does not "tidy up" by setting max-jobs low.
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@
|
|||||||
../pkgs/beets.nix
|
../pkgs/beets.nix
|
||||||
./containers
|
./containers
|
||||||
./nix-serve.nix
|
./nix-serve.nix
|
||||||
|
./builder.nix
|
||||||
# ./tools
|
# ./tools
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
/nix/store/x6qwqsl3xprb9pwinajspkkg4r5lgxcz-nixos-system-sapphira-default
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"version": "ENC[AES256_GCM,data:Og==,iv:7CSdsBk5u2UKOn1dE6CYOiPlmYYkUmmxV1VD6nVIIoc=,tag:z1z57WidbvdlIY5CHQU/TA==,type:int]",
|
||||||
|
"accounts": {
|
||||||
|
"fa02561b1001pVHOSO4a6JW9Cv": {
|
||||||
|
"id": "ENC[AES256_GCM,data:Xm+h0mYIkOAhRI2MZt/nNxMZ+UW7twFu3a4=,iv:PQlE5hc5UPpShQju31AjNKlmaBovCH0eKGnMi1wIfwg=,tag:P1qA5OAQMyICDk52m3jCfA==,type:str]",
|
||||||
|
"serviceID": "ENC[AES256_GCM,data:5S0wMZ1ES5GjSnp1uXhuxLdjlA==,iv:6DvaCiDjBo/tKpjVSazxSNtticZjAmrcA00jjzXsKmc=,tag:S24kxmT6GJyoKSywJGCYlw==,type:str]",
|
||||||
|
"description": "ENC[AES256_GCM,data:HEISFOphDA==,iv:3IvEjXPs1RA0xeOO2k3ECdGUXb55ueR0yxJSdWDqqho=,tag:YgtEMx7nPxgY4xjr8B3isA==,type:str]",
|
||||||
|
"credential": {
|
||||||
|
"type": "ENC[AES256_GCM,data:kdOU,iv:8umxWXKvaDqYO5woOQDqTuuwtdgJ7oVJD8XU7D841k4=,tag:QBRDcCCIqbfbvY3d2CD67w==,type:str]",
|
||||||
|
"key": "ENC[AES256_GCM,data:PGzIgMDQkclf4RiDO3lQE/fQ4V0hM6nvFB/XpUqdMiAKvW/cQyCdmxdwwJQe4FSPHwyYzYDfi0VULf/tfYq+hOx5mC5F0/9ldLw2cbf68TPdcCMjIZEokLUAqe0qJlTnGxdO4PRzzL1LvOKr3Mlo4KcgoUpmeFPxwvxL2Wk=,iv:Z4gna9cUuz3YjtS2v0+WsKmJV66dryl+XtBdLbUGhrI=,tag:WPgVnEFfrJtG2pXRHGXVDQ==,type:str]"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"active": {
|
||||||
|
"minimax-coding-plan": "ENC[AES256_GCM,data:Bt0Yhiz5qmJ1BIU8bDle7mVtyVC6r/lX4gY=,iv:CRmuL1bL0Jc+RFeoSbZyyIHSyBd/RojNjzzVRRODFjs=,tag:mOdKWxDWQLgYSVYiM6hugw==,type:str]"
|
||||||
|
},
|
||||||
|
"sops": {
|
||||||
|
"age": [
|
||||||
|
{
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAyUk03UnVZOUtKcDJhTUdy\nMUhBcDNROGxHR1RPcEZjeHdnYmtWck5KcVZnClIvckVxZmdBQWg3b0FsVFRVRVBP\nRUVaVTFqeDFQbVYvNk42MnlFVlJpTmsKLS0tIFBBaWJwb09ySGFGUHZsajhlb01v\nek10Q3FBWUM5Wms0UUFtV1p1b29mL1kK+hr3lbwBDmtedEeA0hnXSAxC/HOE/9iz\n5kMSbzno+UXnVG/EfLHsks2G43caBmCZlUp7spTt5DLnpwL923GnFQ==\n-----END AGE ENCRYPTED FILE-----\n",
|
||||||
|
"recipient": "age13l2gtk0nzr484zprp7e0pkrt0ne0j4asyn2pjmlaw73nte7t7d8q4sqtxm"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lastmodified": "2026-10-03T13:34:07Z",
|
||||||
|
"mac": "ENC[AES256_GCM,data:e+4yZeDnprtRi1jkP8A9DxNOjemIIi9VwOANAnT2f1UEJVBm6v3MPrlLPZ3Kyg7I2wnIw25Ih6boDn92fxWrIut8PmFPFmlCUu0v4mK49YQmB4dA/i/RYQMAZ6pG2JtPMUWOYsHppU67RB/hKQmJigZSz49tBOHiDrgUa+kfK9c=,iv:872D82r8gIl+mMYNy7iEhnxG/1HwrNg1TO6QXIf7Vo4=,tag:Sd8pSaYZhRxRY6jUL9DxhQ==,type:str]",
|
||||||
|
"unencrypted_suffix": "_unencrypted",
|
||||||
|
"version": "3.13.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"minimax-coding-plan": {
|
||||||
|
"type": "ENC[AES256_GCM,data:cW4a,iv:giJwLOEm5ZoyX1fly0R0xTUsMqtAClmpuSk6d9kaHb4=,tag:YrR/kW3ZFOxot9WeP9kibw==,type:str]",
|
||||||
|
"key": "ENC[AES256_GCM,data:SvZTe8f3/Es1/DOLpcXuY7IyJpLlkuEN38wUd1uBdOdkzA9QZxkroQ93fuvyqSDFAIfDEfSQsAElME3VzaFVB0Y8t97wB2gXWm4xHXjFyzcu+uaOq/deBQ+B13/xMFfjsMlKR1H1WJ4VRZYY3sc70Wsvid+6hxOdO/a/i3k=,iv:x53HYXFeQ9NEMr5SDM8KEOsrzIMzdNAtSeY26FdKkMw=,tag:uW2xCO+cA7nKHWHpBZCVkw==,type:str]"
|
||||||
|
},
|
||||||
|
"sops": {
|
||||||
|
"age": [
|
||||||
|
{
|
||||||
|
"enc": "-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBlSkw3Z0VrTjBBa1VsQlRp\nbHUrZ3hXb1Q1Z0kxdVJhV1hVWnNLUUdDclhjCjg0aTNuUlhNNzdFajhPNE1DN2Fn\nZzJZNVRUYUxpNDFJK3pLTkE4UWFsbkUKLS0tIE8wUkZuN21aaXhpZlNzUnBZR0tC\nU2xwcjRJNnRPdTJ6elRhS08ybjlOS1kKBIfDuZSIOFoxCUc21GnqxipT0ouxDHsB\nXGBvj8zkJ+07tDVMYAhjWYkQK9wBNtUMvgxKedvLZNIn0Hm0Z0rPkw==\n-----END AGE ENCRYPTED FILE-----\n",
|
||||||
|
"recipient": "age13l2gtk0nzr484zprp7e0pkrt0ne0j4asyn2pjmlaw73nte7t7d8q4sqtxm"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"lastmodified": "2026-10-03T13:34:07Z",
|
||||||
|
"mac": "ENC[AES256_GCM,data:lUAw+AL62sxoy695aV1lYgUm4JQwzC+7c36vupe/mJCeNNzVm5ZadGaGsno28EmF4fGxOVsJzn939nhNRtQZ6MwZNhShoSZBmpO51vHRm1YsfAR1sWi/u9akbcu906fjrJLyql9sWWmnxiqxn70gq4Ov6ptsx0VjtiwyWOk+cps=,iv:zckNKtUMu+4DKYp9hwbMPtm6bh46iRNGguff3AIfOa0=,tag:b7svS76JLEJmb+gkPNhQhQ==,type:str]",
|
||||||
|
"unencrypted_suffix": "_unencrypted",
|
||||||
|
"version": "3.13.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
OPENCODE_SERVER_USERNAME=ENC[AES256_GCM,data:6dqD4TnURrk=,iv:UUOBwiykDe9Wv/78wmmx5JnJEWScQRQh2yM+806lF7Y=,tag:vpSB652uQ+ASZQh4PS12Sw==,type:str]
|
||||||
|
OPENCODE_SERVER_PASSWORD=ENC[AES256_GCM,data:mAIHJ5+pupEFdbTurc6davXecgPrHA==,iv:n3BNhwxbJJ6WVq02ANUi0nNAploCsPQIF/JBT1TXxHg=,tag:2YKnOytFny9x8rg8X/7nxA==,type:str]
|
||||||
|
GEMINI_API_KEY=ENC[AES256_GCM,data:hKbpsv1ZrhROPMHYUUAc/oErz0JPSORLr7/B8N1VgbqVZ1FoVf7LM5o=,iv:+3hzXJkjSwpBdwB231PnuE7T+c7A6bmYCckKAqljO0Q=,tag:VVKsFxptQEg6GZAdCQ1A+g==,type:str]
|
||||||
|
sops_age__list_0__map_enc=-----BEGIN AGE ENCRYPTED FILE-----\nYWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBPZFdZZUZxOXRXUUF1NVdV\nRmUrQ2FqeGJWdTZvVkxncEpBb3FpaW5VUEM4Ck1WQ0xLVzBrOG1IOER1dXhxZUEy\nTnV3SnlFSi83b3VGdWtQZ0hYeXRyNEUKLS0tIHhqai9mYVRmR091S0w5cmNMK0Y0\nZVVUdzB6Ky9PUFExclBNcnZUQWFrOXcKQq4qlRz5+1GR3LB9/CkZSz1nyFthk7mg\n2j3wUXQ41kyRUu8pM40eCxHVkpMaa/7fjZ40nRjrnwZ7Brd5Q8z3MQ==\n-----END AGE ENCRYPTED FILE-----\n
|
||||||
|
sops_age__list_0__map_recipient=age13l2gtk0nzr484zprp7e0pkrt0ne0j4asyn2pjmlaw73nte7t7d8q4sqtxm
|
||||||
|
sops_lastmodified=2026-10-03T12:47:19Z
|
||||||
|
sops_mac=ENC[AES256_GCM,data:thYwpX+SrNRL3hdvUzXPzh3Q07Dt6ZF6cplKS5Iopj7twS5lQoB4C1OuWf00GUUPDEOaxF3WK24XiRkMoA8TZHSLJ/k8HPV9tDlPnNHMh3pMj9pIfR5NTDMASmld17PjBvwPMOB/uvMn26O1G6G3ImW4Zioy3AyDP2zmed9+3Xk=,iv:uKGvvwvDTEQom636P9YcUjLMpIrRusCFI9HJqNJihkw=,tag:Qfc5KRSNn+Yy74pKKvkjOA==,type:str]
|
||||||
|
sops_unencrypted_suffix=_unencrypted
|
||||||
|
sops_version=3.13.3
|
||||||
Reference in New Issue
Block a user