Add projects

This commit is contained in:
Alois 2026-08-24 00:10:41 +02:00
commit 8b607dd700
Signed by: alois
SSH key fingerprint: SHA256:GBzT2DXvAuGV9XIV5W3WrzVpjU54FThmxHXdbz95J24
1802 changed files with 503346 additions and 2 deletions

233
nix/module.nix Normal file
View file

@ -0,0 +1,233 @@
{ self }:
{
config,
lib,
pkgs,
...
}:
let
inherit (lib)
mkEnableOption
mkIf
mkOption
types
;
cfg = config.services.vibe-proxy;
statePath = "/var/lib/${cfg.stateDirectory}";
yaml = pkgs.formats.yaml { };
generatedSettings =
lib.recursiveUpdate
(
{
host = cfg.host;
port = cfg.port;
auth-dir = "${statePath}/auths";
plugins = {
enabled = true;
dir = "${statePath}/plugins";
};
}
// cfg.settings
)
{
host = cfg.host;
port = cfg.port;
};
generatedConfig = yaml.generate "vibe-proxy.yaml" generatedSettings;
runtimeConfig =
if cfg.configFile == null then "${statePath}/config.yaml" else toString cfg.configFile;
in
{
options.services.vibe-proxy = {
enable = mkEnableOption "Vibe Proxy";
package = mkOption {
type = types.package;
default = self.packages.${pkgs.stdenv.hostPlatform.system}.default;
defaultText = lib.literalExpression "inputs.vibe-proxy.packages.${pkgs.stdenv.hostPlatform.system}.default";
description = "Vibe Proxy package to run.";
};
user = mkOption {
type = types.str;
default = "vibe-proxy";
description = "User account under which the service runs.";
};
group = mkOption {
type = types.str;
default = "vibe-proxy";
description = "Group under which the service runs.";
};
stateDirectory = mkOption {
type = types.str;
default = "vibe-proxy";
description = "Directory below /var/lib used for writable service state.";
};
host = mkOption {
type = types.str;
default = "127.0.0.1";
description = "Address on which the API listens.";
};
port = mkOption {
type = types.port;
default = 8317;
description = "TCP port on which the API listens.";
};
openFirewall = mkOption {
type = types.bool;
default = false;
description = "Whether to open the main API port in the firewall.";
};
configFile = mkOption {
type = types.nullOr types.str;
default = null;
description = "Writable external configuration file. When null, settings generate the configuration on every service start.";
};
settings = mkOption {
type = yaml.type;
default = { };
description = "Declarative YAML configuration. Secrets in this option are written to the Nix store; use environmentFiles for secrets.";
};
environment = mkOption {
type = types.attrsOf types.str;
default = { };
description = "Non-secret environment variables for the service.";
};
environmentFiles = mkOption {
type = types.listOf types.path;
default = [ ];
description = "Environment files containing secrets such as MANAGEMENT_PASSWORD.";
};
localModel = mkOption {
type = types.bool;
default = false;
description = "Disable remote model catalog updates.";
};
extraArgs = mkOption {
type = types.listOf types.str;
default = [ ];
description = "Additional command-line arguments passed to the server.";
};
openOAuthCallbackPorts = mkOption {
type = types.bool;
default = false;
description = "Open the built-in OAuth callback ports 1455, 54545, and 51121.";
};
liveMediaRelay = {
openFirewall = mkOption {
type = types.bool;
default = false;
description = "Open the configured UDP range for the live-media relay.";
};
portRange = mkOption {
type = types.submodule {
options = {
from = mkOption {
type = types.port;
default = 50000;
};
to = mkOption {
type = types.port;
default = 50100;
};
};
};
default = { };
description = "UDP firewall range used by the live-media relay.";
};
};
};
config = mkIf cfg.enable {
assertions = [
{
assertion = cfg.liveMediaRelay.portRange.from <= cfg.liveMediaRelay.portRange.to;
message = "services.vibe-proxy.liveMediaRelay.portRange.from must not exceed .to";
}
];
users.groups = mkIf (cfg.group == "vibe-proxy") {
vibe-proxy = { };
};
users.users = mkIf (cfg.user == "vibe-proxy") {
vibe-proxy = {
isSystemUser = true;
inherit (cfg) group;
home = statePath;
};
};
networking.firewall.allowedTCPPorts =
lib.optional cfg.openFirewall cfg.port
++ lib.optionals cfg.openOAuthCallbackPorts [
1455
51121
54545
];
networking.firewall.allowedUDPPortRanges = lib.optional cfg.liveMediaRelay.openFirewall {
inherit (cfg.liveMediaRelay.portRange) from to;
};
systemd.services.vibe-proxy = {
description = "Vibe Proxy API";
wantedBy = [ "multi-user.target" ];
after = [ "network-online.target" ];
wants = [ "network-online.target" ];
environment = {
HOME = statePath;
WRITABLE_PATH = statePath;
}
// cfg.environment;
preStart = ''
ln -sfn ${cfg.package}/share/vibe-proxy/config.example.yaml ${statePath}/config.example.yaml
''
+ lib.optionalString (cfg.configFile == null) ''
install -m 0600 ${generatedConfig} ${runtimeConfig}
'';
serviceConfig = {
User = cfg.user;
Group = cfg.group;
StateDirectory = cfg.stateDirectory;
StateDirectoryMode = "0700";
WorkingDirectory = statePath;
EnvironmentFile = cfg.environmentFiles;
ExecStart = lib.concatStringsSep " " (
[
(lib.getExe cfg.package)
"--config"
(lib.escapeShellArg runtimeConfig)
]
++ lib.optional cfg.localModel "--local-model"
++ map lib.escapeShellArg cfg.extraArgs
);
Restart = "on-failure";
RestartSec = "5s";
UMask = "0077";
NoNewPrivileges = true;
PrivateTmp = true;
ProtectSystem = "strict";
ProtectHome = true;
ReadWritePaths = [
statePath
]
++ lib.optional (cfg.configFile != null) (builtins.dirOf cfg.configFile);
};
};
};
}

126
nix/package.nix Normal file
View file

@ -0,0 +1,126 @@
{
lib,
buildGo126Module,
fetchPnpmDeps,
nodejs_24,
pnpm,
pnpmConfigHook,
stdenvNoCC,
}:
let
version = "dev";
frontendSource = lib.fileset.toSource {
root = ../.;
fileset = lib.fileset.unions [
../package.json
../pnpm-lock.yaml
../pnpm-workspace.yaml
../frontend/package.json
../frontend/index.html
../frontend/eslint.config.js
../frontend/tsconfig.json
../frontend/tsconfig.node.json
../frontend/vite.config.ts
../frontend/src
];
};
frontend = stdenvNoCC.mkDerivation (finalAttrs: {
pname = "vibe-proxy-frontend";
inherit version;
src = frontendSource;
pnpmDeps = fetchPnpmDeps {
inherit (finalAttrs) pname version src;
fetcherVersion = 4;
hash = "sha256-vpiGYrLY5B+irYJf4lAs3SM9RrmEb9t+BJHBVXQGP+I=";
};
nativeBuildInputs = [
nodejs_24
pnpm
pnpmConfigHook
];
buildPhase = ''
runHook preBuild
pnpm --dir frontend exec tsc
pnpm --dir frontend exec vite build
runHook postBuild
'';
installPhase = ''
runHook preInstall
mkdir -p $out/share/vibe-proxy/frontend
cp -r frontend/dist/. $out/share/vibe-proxy/frontend/
runHook postInstall
'';
meta = {
description = "Vibe Proxy management frontend";
license = lib.licenses.mit;
platforms = lib.platforms.all;
};
});
backendRoot = toString ../backend;
backendSource = lib.cleanSourceWith {
src = ../backend;
filter =
path: type:
let
relative = lib.removePrefix "${backendRoot}/" (toString path);
in
lib.cleanSourceFilter path type
&& relative != "cli-proxy-api"
&& relative != ".dev"
&& !lib.hasPrefix ".dev/" relative
&& !lib.hasPrefix "internal/managementasset/dist/" relative;
};
buildBackend =
{ embedFrontend }:
buildGo126Module {
pname = if embedFrontend then "vibe-proxy" else "vibe-proxy-backend";
inherit version;
src = backendSource;
vendorHash = "sha256-CrDp7MOr+AwJUhTovklXx3F1yaktQlvD7VYhYSY6VvY=";
subPackages = [ "cmd/server" ];
tags = lib.optional embedFrontend "frontend";
env.CGO_ENABLED = "1";
preBuild = lib.optionalString embedFrontend ''
rm -rf internal/managementasset/dist
mkdir -p internal/managementasset/dist
cp -r ${frontend}/share/vibe-proxy/frontend/. internal/managementasset/dist/
'';
ldflags = [
"-s"
"-w"
"-X main.Version=${version}"
"-X main.Commit=nix"
"-X main.BuildDate=reproducible"
];
postInstall = ''
mv $out/bin/server $out/bin/vibe-proxy
mkdir -p $out/share/vibe-proxy
cp config.example.yaml $out/share/vibe-proxy/config.example.yaml
'';
meta = {
description = "OpenAI-compatible proxy server${lib.optionalString embedFrontend " with an embedded management UI"}";
homepage = "https://github.com/router-for-me/CLIProxyAPI";
license = lib.licenses.mit;
mainProgram = "vibe-proxy";
platforms = lib.platforms.linux;
};
};
in
{
inherit frontend;
backend = buildBackend { embedFrontend = false; };
combined = buildBackend { embedFrontend = true; };
}