mirror of
https://codeberg.org/muon/home.git
synced 2026-07-03 23:49:35 +00:00
Add auth2api
This commit is contained in:
parent
b6318d9c4d
commit
28bb03187d
9 changed files with 335 additions and 11 deletions
|
|
@ -1,4 +1,25 @@
|
||||||
{
|
{
|
||||||
|
"auth2api": {
|
||||||
|
"cargoLock": null,
|
||||||
|
"date": "2026-05-10",
|
||||||
|
"extract": null,
|
||||||
|
"name": "auth2api",
|
||||||
|
"passthru": null,
|
||||||
|
"pinned": false,
|
||||||
|
"src": {
|
||||||
|
"deepClone": false,
|
||||||
|
"fetchSubmodules": false,
|
||||||
|
"leaveDotGit": false,
|
||||||
|
"name": null,
|
||||||
|
"owner": "AmazingAng",
|
||||||
|
"repo": "auth2api",
|
||||||
|
"rev": "840fa100e71a3562552cc7d0267f7668db0d7f86",
|
||||||
|
"sha256": "sha256-Pz+V1QKbZZKfoZz2TbC4yLtwAdaUIWdLV8FJh8LohTc=",
|
||||||
|
"sparseCheckout": [],
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"version": "840fa100e71a3562552cc7d0267f7668db0d7f86"
|
||||||
|
},
|
||||||
"dcts-client-shipping": {
|
"dcts-client-shipping": {
|
||||||
"cargoLock": null,
|
"cargoLock": null,
|
||||||
"date": null,
|
"date": null,
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,18 @@
|
||||||
dockerTools,
|
dockerTools,
|
||||||
}:
|
}:
|
||||||
{
|
{
|
||||||
|
auth2api = {
|
||||||
|
pname = "auth2api";
|
||||||
|
version = "840fa100e71a3562552cc7d0267f7668db0d7f86";
|
||||||
|
src = fetchFromGitHub {
|
||||||
|
owner = "AmazingAng";
|
||||||
|
repo = "auth2api";
|
||||||
|
rev = "840fa100e71a3562552cc7d0267f7668db0d7f86";
|
||||||
|
fetchSubmodules = false;
|
||||||
|
sha256 = "sha256-Pz+V1QKbZZKfoZz2TbC4yLtwAdaUIWdLV8FJh8LohTc=";
|
||||||
|
};
|
||||||
|
date = "2026-05-10";
|
||||||
|
};
|
||||||
dcts-client-shipping = {
|
dcts-client-shipping = {
|
||||||
pname = "dcts-client-shipping";
|
pname = "dcts-client-shipping";
|
||||||
version = "v3.3";
|
version = "v3.3";
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,11 @@ in {
|
||||||
mods.docker.enable = true;
|
mods.docker.enable = true;
|
||||||
mods.docker.media.enable = false;
|
mods.docker.media.enable = false;
|
||||||
|
|
||||||
|
mods.server.auth2api.enable = true;
|
||||||
|
# host defaults to 127.0.0.1 (localhost only, perfect for Claude Code)
|
||||||
|
# port defaults to 8317
|
||||||
|
# Run `auth2api --login` once after deploying to authorise your Claude account.
|
||||||
|
|
||||||
mods.server.sync.enable = true;
|
mods.server.sync.enable = true;
|
||||||
mods.tailscale.enable = true;
|
mods.tailscale.enable = true;
|
||||||
mods.openvpn.enable = false;
|
mods.openvpn.enable = false;
|
||||||
|
|
|
||||||
206
modules/nixos/server/auth2api.nix
Normal file
206
modules/nixos/server/auth2api.nix
Normal file
|
|
@ -0,0 +1,206 @@
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
sources,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
let
|
||||||
|
cfg = config.mods.server.auth2api;
|
||||||
|
|
||||||
|
auth2api-pkg = pkgs.callPackage ../../../pkgs/auth2api/package.nix { inherit sources; };
|
||||||
|
|
||||||
|
# Wrap the binary so --config is always implicit, whether run by the
|
||||||
|
# service, by the user for --login, or any other invocation.
|
||||||
|
auth2api-wrapped = pkgs.symlinkJoin {
|
||||||
|
name = "auth2api-wrapped";
|
||||||
|
paths = [ auth2api-pkg ];
|
||||||
|
buildInputs = [ pkgs.makeWrapper ];
|
||||||
|
postBuild = ''
|
||||||
|
wrapProgram $out/bin/auth2api \
|
||||||
|
--add-flags "--config=${cfg.stateDir}/config.yaml" \
|
||||||
|
--run "umask 027"
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# Nix-managed config template — read-only in /nix/store.
|
||||||
|
# systemd-tmpfiles copies it into stateDir on first boot (C rule),
|
||||||
|
# so auth2api can write back to it (e.g. API key auto-generation)
|
||||||
|
# and the user can hand-edit it freely without rebuilds overwriting it.
|
||||||
|
configTemplate = pkgs.writeText "auth2api-config-template.yaml" ''
|
||||||
|
host: "${cfg.host}"
|
||||||
|
port: ${toString cfg.port}
|
||||||
|
|
||||||
|
# Directory where OAuth token files are stored (written by --login).
|
||||||
|
auth-dir: "${cfg.stateDir}/tokens"
|
||||||
|
|
||||||
|
# Empty = unauthenticated mode (safe when binding to 127.0.0.1).
|
||||||
|
api-keys: []
|
||||||
|
|
||||||
|
body-limit: "${cfg.bodyLimit}"
|
||||||
|
|
||||||
|
timeouts:
|
||||||
|
messages-ms: ${toString cfg.timeouts.messagesMs}
|
||||||
|
stream-messages-ms: ${toString cfg.timeouts.streamMessagesMs}
|
||||||
|
count-tokens-ms: ${toString cfg.timeouts.countTokensMs}
|
||||||
|
|
||||||
|
stats:
|
||||||
|
enabled: true
|
||||||
|
|
||||||
|
# off | errors | verbose
|
||||||
|
debug: "${cfg.debug}"
|
||||||
|
|
||||||
|
cloaking:
|
||||||
|
cli-version: "2.1.88"
|
||||||
|
entrypoint: "cli"
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
with lib; {
|
||||||
|
options.mods.server.auth2api = {
|
||||||
|
enable = mkEnableOption "auth2api OAuth-to-API proxy";
|
||||||
|
|
||||||
|
port = mkOption {
|
||||||
|
type = types.port;
|
||||||
|
default = 8317;
|
||||||
|
description = "Port auth2api listens on.";
|
||||||
|
};
|
||||||
|
|
||||||
|
host = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "127.0.0.1";
|
||||||
|
description = "Bind address. Defaults to localhost — safe with no API key.";
|
||||||
|
};
|
||||||
|
|
||||||
|
stateDir = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "/var/lib/auth2api";
|
||||||
|
description = ''
|
||||||
|
State directory. Contains config.yaml (live, writable) and
|
||||||
|
tokens/ (OAuth token files written by --login).
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
bodyLimit = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "200mb";
|
||||||
|
description = "Maximum JSON request body size.";
|
||||||
|
};
|
||||||
|
|
||||||
|
timeouts = {
|
||||||
|
messagesMs = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
default = 120000;
|
||||||
|
description = "Non-streaming /v1/messages timeout (ms).";
|
||||||
|
};
|
||||||
|
streamMessagesMs = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
default = 600000;
|
||||||
|
description = "Streaming /v1/messages timeout (ms).";
|
||||||
|
};
|
||||||
|
countTokensMs = mkOption {
|
||||||
|
type = types.int;
|
||||||
|
default = 30000;
|
||||||
|
description = "/v1/messages/count_tokens timeout (ms).";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
debug = mkOption {
|
||||||
|
type = types.enum [ "off" "errors" "verbose" ];
|
||||||
|
default = "off";
|
||||||
|
description = "Log level: off | errors | verbose.";
|
||||||
|
};
|
||||||
|
|
||||||
|
openFirewall = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Open the firewall for the auth2api port.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
# tmpfiles runs as root before any service starts.
|
||||||
|
# 'd' creates the directory if missing; 'C' copies the template into
|
||||||
|
# config.yaml only if that file doesn't already exist — so hand-edits
|
||||||
|
# and auth2api's own writes are never clobbered on rebuild.
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
# stateDir: world-traversable so any local user can reach files inside
|
||||||
|
"d '${cfg.stateDir}' 0755 auth2api auth2api - -"
|
||||||
|
"z '${cfg.stateDir}' 0755 auth2api auth2api - -"
|
||||||
|
# tokens: group-writable so members of the auth2api group can run --login
|
||||||
|
"d '${cfg.stateDir}/tokens' 0770 auth2api auth2api - -"
|
||||||
|
"z '${cfg.stateDir}/tokens' 0770 auth2api auth2api - -"
|
||||||
|
# config.yaml: contains no secrets (empty api-keys), safe to be world-readable
|
||||||
|
"C '${cfg.stateDir}/config.yaml' 0644 auth2api auth2api - ${configTemplate}"
|
||||||
|
"z '${cfg.stateDir}/config.yaml' 0644 auth2api auth2api - -"
|
||||||
|
];
|
||||||
|
|
||||||
|
# auth2api.path watches the tokens directory and activates auth2api.service
|
||||||
|
# the moment a token file appears (i.e. after --login has been run).
|
||||||
|
# This means the service is never started — and never fails — during
|
||||||
|
# activation on a fresh machine, so nixos-rebuild always succeeds cleanly.
|
||||||
|
systemd.paths.auth2api = {
|
||||||
|
description = "Watch for auth2api OAuth tokens";
|
||||||
|
wantedBy = [ "multi-user.target" ];
|
||||||
|
after = [ "systemd-tmpfiles-setup.service" ];
|
||||||
|
|
||||||
|
pathConfig = {
|
||||||
|
# Trigger when any file is created inside the tokens directory.
|
||||||
|
DirectoryNotEmpty = "${cfg.stateDir}/tokens";
|
||||||
|
# Re-trigger if the service stops (e.g. after a logout / token removal).
|
||||||
|
Unit = "auth2api.service";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.auth2api = {
|
||||||
|
description = "auth2api OAuth-to-API proxy";
|
||||||
|
# Started by the path unit, not directly by multi-user.target.
|
||||||
|
# Must wait for tmpfiles so config.yaml is always present before start.
|
||||||
|
after = [ "network.target" "systemd-tmpfiles-setup.service" ];
|
||||||
|
requires = [ "systemd-tmpfiles-setup.service" ];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "simple";
|
||||||
|
ExecStart = "${auth2api-wrapped}/bin/auth2api";
|
||||||
|
|
||||||
|
Restart = "on-failure";
|
||||||
|
RestartSec = "10s";
|
||||||
|
|
||||||
|
User = "auth2api";
|
||||||
|
Group = "auth2api";
|
||||||
|
|
||||||
|
ReadWritePaths = [ cfg.stateDir ];
|
||||||
|
|
||||||
|
# Hardening
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
PrivateTmp = true;
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
ProtectHome = true;
|
||||||
|
PrivateDevices = true;
|
||||||
|
ProtectKernelTunables = true;
|
||||||
|
ProtectControlGroups = true;
|
||||||
|
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" "AF_UNIX" ];
|
||||||
|
RestrictNamespaces = true;
|
||||||
|
LockPersonality = true;
|
||||||
|
MemoryDenyWriteExecute = false; # Node.js JIT requires W+X pages
|
||||||
|
RestrictRealtime = true;
|
||||||
|
RestrictSUIDSGID = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.auth2api = {
|
||||||
|
isSystemUser = true;
|
||||||
|
group = "auth2api";
|
||||||
|
home = cfg.stateDir;
|
||||||
|
description = "auth2api service user";
|
||||||
|
};
|
||||||
|
users.groups.auth2api = { };
|
||||||
|
|
||||||
|
# Allow the primary user to run --login and read/write tokens.
|
||||||
|
users.users.${config.mods.user.name}.extraGroups = [ "auth2api" ];
|
||||||
|
|
||||||
|
# Make the wrapped binary available in PATH for `auth2api --login` etc.
|
||||||
|
environment.systemPackages = [ auth2api-wrapped ];
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -30,5 +30,6 @@
|
||||||
./audio.nix
|
./audio.nix
|
||||||
./atuin.nix
|
./atuin.nix
|
||||||
./murmur.nix
|
./murmur.nix
|
||||||
|
./auth2api.nix
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,8 +5,7 @@ lemmy-password: ENC[AES256_GCM,data:VVPbhW6l+VYSUfmlySPSwITwonKQHaIY,iv:XcwM7Sz2
|
||||||
sops-key: ENC[AES256_GCM,data:CT2FJnxRV0nVccCS+bofjIDqoVnJKMs63BVdmC4KEXEJAdsiyINTNJ+19aMqIkr2eosvXX1+nvV6oeBvNv1uN9xCrrzu4Qj0yRA=,iv:w9Fp68KK8hnUirlDGOYKSQwlfp3OBWU4XWqliZn/apc=,tag:XZdhC65WpcazSol1mbdp5A==,type:str]
|
sops-key: ENC[AES256_GCM,data:CT2FJnxRV0nVccCS+bofjIDqoVnJKMs63BVdmC4KEXEJAdsiyINTNJ+19aMqIkr2eosvXX1+nvV6oeBvNv1uN9xCrrzu4Qj0yRA=,iv:w9Fp68KK8hnUirlDGOYKSQwlfp3OBWU4XWqliZn/apc=,tag:XZdhC65WpcazSol1mbdp5A==,type:str]
|
||||||
sops:
|
sops:
|
||||||
age:
|
age:
|
||||||
- recipient: age1m97a3eptxwpdd7h5kkqe9gkmhg6rquc64qjmlsfqfhfqv8q72crqrylhgc
|
- enc: |
|
||||||
enc: |
|
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4bUg1Z1JBcmRldDIzN2Zt
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB4bUg1Z1JBcmRldDIzN2Zt
|
||||||
Ky9LOTVBK0IzdE1UUFBXci94R0x1bitjT2hjCjA1NC9wMzNHZkorZllIaFpNMVlm
|
Ky9LOTVBK0IzdE1UUFBXci94R0x1bitjT2hjCjA1NC9wMzNHZkorZllIaFpNMVlm
|
||||||
|
|
@ -14,8 +13,8 @@ sops:
|
||||||
eWlTRmEzYVpQdENiNUMxaWJta0NjcVEKx3togykPGYRNGgJR6fl9cDbJKiLWHjA9
|
eWlTRmEzYVpQdENiNUMxaWJta0NjcVEKx3togykPGYRNGgJR6fl9cDbJKiLWHjA9
|
||||||
XujrttnDTwNCCZENn/E4BABC4XecW8IqSsUmJW6GwZzYJu+4rNTSwA==
|
XujrttnDTwNCCZENn/E4BABC4XecW8IqSsUmJW6GwZzYJu+4rNTSwA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1v4s4hg7u3vjjkarvrk7v6ev7w3wja2r5xm7f4t06culw3fuq7qns8sfju7
|
recipient: age1m97a3eptxwpdd7h5kkqe9gkmhg6rquc64qjmlsfqfhfqv8q72crqrylhgc
|
||||||
enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWWE5tU0ltaTJscUVQSDBy
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBWWE5tU0ltaTJscUVQSDBy
|
||||||
WHRDb2FTRVFtZ2s2eGRjb21ncU1HNkx3RmhRClMwQ0E1cCt1SmtoYi9TWExXdVdX
|
WHRDb2FTRVFtZ2s2eGRjb21ncU1HNkx3RmhRClMwQ0E1cCt1SmtoYi9TWExXdVdX
|
||||||
|
|
@ -23,8 +22,8 @@ sops:
|
||||||
Qm5yVjBNc1l6VFQ4OGJsWXdsWUIyNFkKksIW0x8RxTdaw9YR4y+84VrYnfVZz2js
|
Qm5yVjBNc1l6VFQ4OGJsWXdsWUIyNFkKksIW0x8RxTdaw9YR4y+84VrYnfVZz2js
|
||||||
qz1RG4TXs9NRcm8fGGa/ZYZZN72h/l0WY+fayZ+ZUaHD43tHFisoYg==
|
qz1RG4TXs9NRcm8fGGa/ZYZZN72h/l0WY+fayZ+ZUaHD43tHFisoYg==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1n7qz2w3hkf7fcdv92kxw9k6uef487na2tlc87486rcjwj8lyfuws5q46gn
|
recipient: age1v4s4hg7u3vjjkarvrk7v6ev7w3wja2r5xm7f4t06culw3fuq7qns8sfju7
|
||||||
enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1L29jY3lNeU8xeE03VUFu
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB1L29jY3lNeU8xeE03VUFu
|
||||||
MWJyczNxZFJHSG82c0p3OEtBOThqaE8xTFMwCm9KemZJMjBOQ0I1TU9Qd2IvMGVU
|
MWJyczNxZFJHSG82c0p3OEtBOThqaE8xTFMwCm9KemZJMjBOQ0I1TU9Qd2IvMGVU
|
||||||
|
|
@ -32,8 +31,8 @@ sops:
|
||||||
UXp0a3AwM0hvbG1jeEZIMlViYU9ZWTgKKJ2YL6Q2LyR9x4Oqt5qWiyL7f4wAWrqw
|
UXp0a3AwM0hvbG1jeEZIMlViYU9ZWTgKKJ2YL6Q2LyR9x4Oqt5qWiyL7f4wAWrqw
|
||||||
FTY5r2unI7YdIFtzmbjIAqv/4qqy62Th8EEsqAZUcL/YBcuNIiyg6Q==
|
FTY5r2unI7YdIFtzmbjIAqv/4qqy62Th8EEsqAZUcL/YBcuNIiyg6Q==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
- recipient: age1mgjhkqy9x27gv2t2xvq46dxcajkr9c8zes7rr3dj0ac7md2j6vas43dftp
|
recipient: age1n7qz2w3hkf7fcdv92kxw9k6uef487na2tlc87486rcjwj8lyfuws5q46gn
|
||||||
enc: |
|
- enc: |
|
||||||
-----BEGIN AGE ENCRYPTED FILE-----
|
-----BEGIN AGE ENCRYPTED FILE-----
|
||||||
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJOUhRY0RtaUhNaHZZTHk3
|
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSBJOUhRY0RtaUhNaHZZTHk3
|
||||||
QVF4cXV2Lzc3d1RRM2pzMXBBQU95endLRFFZCkdMVVlkV3VzSnRyRHpROHlReUdJ
|
QVF4cXV2Lzc3d1RRM2pzMXBBQU95endLRFFZCkdMVVlkV3VzSnRyRHpROHlReUdJ
|
||||||
|
|
@ -41,7 +40,8 @@ sops:
|
||||||
VFdIbUg1WjlldFFNbGx3dytQNXBsMDgKuU/86fojKVJ5X8+9OIf3k7ud6bujjyFI
|
VFdIbUg1WjlldFFNbGx3dytQNXBsMDgKuU/86fojKVJ5X8+9OIf3k7ud6bujjyFI
|
||||||
HQoONJgXGoQJtkPsmJbMUuMjo/znK+tdCd/uAwxK1Nk670NVxGmJYA==
|
HQoONJgXGoQJtkPsmJbMUuMjo/znK+tdCd/uAwxK1Nk670NVxGmJYA==
|
||||||
-----END AGE ENCRYPTED FILE-----
|
-----END AGE ENCRYPTED FILE-----
|
||||||
lastmodified: "2025-08-04T09:14:07Z"
|
recipient: age1mgjhkqy9x27gv2t2xvq46dxcajkr9c8zes7rr3dj0ac7md2j6vas43dftp
|
||||||
mac: ENC[AES256_GCM,data:Qu5kuhV2c31S9l01e7IWCrjLKU8eBepK42eR1nEvPpoHqXxbIT3vcDbxJdcn2Ay6Z4pARYqmHctVDOCiilxFyYfzF8mP91u6NhsZC5kHMdP7GI5Pl5FXSCMxQbbBWgXxJXruq/NkrlrLnFTWyzBRLa4wTBZdDMZ2CGo6jLi7G0o=,iv:q3WG536FkLpYEp8AAcW0agYq6rDIhzzt47l7grDvGyo=,tag:T5msy2cSZ/bZ9HvbxTw0Rg==,type:str]
|
lastmodified: "2026-06-01T08:59:15Z"
|
||||||
|
mac: ENC[AES256_GCM,data:DtHxyzG5d+USGmfFg6vYzk5NIfHPSq/5CKTBAXuipW1pD4WCFs85cugzs8fctS0+yaBL1It72YxSTzMw43kGGzSjn9Uy5AGoZnhLAw6E7CO+4D6FCkV4Ui83Ku+UMQ/klMDN3KFl7aL0NpAcsjsvyQeSHwBQvXF7oSXDc/wbc6E=,iv:bpbKsNEu+jrKMz6gIBTEEyH8GuVRKvVSS/vJJ4l/npI=,tag:/bb34cXzy/k957K1X6PLHw==,type:str]
|
||||||
unencrypted_suffix: _unencrypted
|
unencrypted_suffix: _unencrypted
|
||||||
version: 3.10.2
|
version: 3.13.0
|
||||||
|
|
|
||||||
|
|
@ -165,3 +165,8 @@ fetch.github = "mendersoftware/mender-cli"
|
||||||
src.github = "hackthedev/dcts-client-shipping"
|
src.github = "hackthedev/dcts-client-shipping"
|
||||||
fetch.github = "hackthedev/dcts-client-shipping"
|
fetch.github = "hackthedev/dcts-client-shipping"
|
||||||
|
|
||||||
|
["auth2api"]
|
||||||
|
src.git = "https://github.com/AmazingAng/auth2api"
|
||||||
|
src.branch = "main"
|
||||||
|
fetch.github = "AmazingAng/auth2api"
|
||||||
|
|
||||||
|
|
|
||||||
30
pkgs/auth2api/no-auth.patch
Normal file
30
pkgs/auth2api/no-auth.patch
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
--- a/src/config.ts
|
||||||
|
+++ b/src/config.ts
|
||||||
|
@@ -137,15 +137,7 @@
|
||||||
|
|
||||||
|
raw.debug = normalizeDebugMode(raw.debug);
|
||||||
|
|
||||||
|
- // Auto-generate API key if none configured
|
||||||
|
- if (!raw["api-keys"] || raw["api-keys"].length === 0) {
|
||||||
|
- const key = generateApiKey();
|
||||||
|
- raw["api-keys"] = [key];
|
||||||
|
- fs.writeFileSync(filePath, yaml.dump(raw, { lineWidth: -1 }), {
|
||||||
|
- mode: 0o600,
|
||||||
|
- });
|
||||||
|
- console.log(`\nGenerated API key (saved to ${filePath}):\n\n ${key}\n`);
|
||||||
|
- }
|
||||||
|
+ // Empty api-keys = unauthenticated mode (safe when binding to localhost).
|
||||||
|
|
||||||
|
return { ...raw, "api-keys": new Set(raw["api-keys"]) };
|
||||||
|
}
|
||||||
|
--- a/src/server.ts
|
||||||
|
+++ b/src/server.ts
|
||||||
|
@@ -94,6 +94,8 @@
|
||||||
|
// API key auth middleware — accepts both OpenAI style (Authorization: Bearer)
|
||||||
|
// and Anthropic style (x-api-key), so Claude Code and OpenAI clients both work
|
||||||
|
const requireApiKey: express.RequestHandler = (req, res, next) => {
|
||||||
|
+ // No keys configured — unauthenticated mode (localhost-only).
|
||||||
|
+ if (config["api-keys"].size === 0) { next(); return; }
|
||||||
|
const key = extractApiKey(req.headers);
|
||||||
|
if (!key) {
|
||||||
|
res.status(401).json({ error: { message: "Missing API key" } });
|
||||||
44
pkgs/auth2api/package.nix
Normal file
44
pkgs/auth2api/package.nix
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
buildNpmPackage,
|
||||||
|
nodejs_20,
|
||||||
|
makeWrapper,
|
||||||
|
sources,
|
||||||
|
}:
|
||||||
|
|
||||||
|
buildNpmPackage {
|
||||||
|
pname = "auth2api";
|
||||||
|
version = sources.auth2api.version;
|
||||||
|
src = sources.auth2api.src;
|
||||||
|
|
||||||
|
nodejs = nodejs_20;
|
||||||
|
|
||||||
|
npmDepsHash = "sha256-lHwY5MQ0nRoOPcURzmJCiXiUxEx9ZwZJSWKbkD4ZuIA=";
|
||||||
|
|
||||||
|
# Patch to allow running with an empty api-keys list (unauthenticated).
|
||||||
|
# Safe because the service binds to 127.0.0.1 by default.
|
||||||
|
patches = [ ./no-auth.patch ];
|
||||||
|
|
||||||
|
# auth2api's build script is `tsc` (TypeScript compile → dist/)
|
||||||
|
# devDeps (typescript, tsx) are needed for the build; buildNpmPackage
|
||||||
|
# prunes them automatically after the build step completes.
|
||||||
|
npmBuildScript = "build";
|
||||||
|
|
||||||
|
nativeBuildInputs = [ makeWrapper ];
|
||||||
|
|
||||||
|
# buildNpmPackage installs the package under $out/lib/node_modules/auth2api/.
|
||||||
|
# Wire up a $out/bin/auth2api wrapper pointing at the compiled entry-point.
|
||||||
|
postInstall = ''
|
||||||
|
makeWrapper ${nodejs_20}/bin/node $out/bin/auth2api \
|
||||||
|
--add-flags "$out/lib/node_modules/auth2api/dist/index.js"
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "Lightweight Claude OAuth to OpenAI-compatible API proxy";
|
||||||
|
homepage = "https://github.com/AmazingAng/auth2api";
|
||||||
|
license = lib.licenses.mit;
|
||||||
|
maintainers = [ ];
|
||||||
|
platforms = lib.platforms.linux;
|
||||||
|
mainProgram = "auth2api";
|
||||||
|
};
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue