first commit
This commit is contained in:
34
nix/devshells/default.nix
Normal file
34
nix/devshells/default.nix
Normal file
@@ -0,0 +1,34 @@
|
||||
{pkgs}:
|
||||
pkgs.mkShellNoCC {
|
||||
name = "bifrost-nix-dev-shell";
|
||||
|
||||
packages = with pkgs; [
|
||||
go_1_26
|
||||
gopls
|
||||
gofumpt
|
||||
air
|
||||
delve
|
||||
go-tools
|
||||
|
||||
nodejs_24
|
||||
];
|
||||
|
||||
env = {};
|
||||
|
||||
shellHook = ''
|
||||
CACHE_ROOT="''${XDG_CACHE_HOME:-$HOME/.cache}/bifrost"
|
||||
mkdir -p "$CACHE_ROOT"
|
||||
|
||||
export GOPATH="$CACHE_ROOT/go"
|
||||
export GOBIN="$GOPATH/bin"
|
||||
export GOMODCACHE="$GOPATH/pkg/mod"
|
||||
export GOCACHE="$CACHE_ROOT/gocache"
|
||||
|
||||
mkdir -p "$GOBIN" "$GOMODCACHE" "$GOCACHE"
|
||||
export PATH="$PATH:$GOBIN"
|
||||
|
||||
export npm_config_prefix="$CACHE_ROOT/npm-global"
|
||||
mkdir -p "$npm_config_prefix/bin"
|
||||
export PATH="$PATH:$npm_config_prefix/bin"
|
||||
'';
|
||||
}
|
||||
216
nix/modules/bifrost.nix
Normal file
216
nix/modules/bifrost.nix
Normal file
@@ -0,0 +1,216 @@
|
||||
{
|
||||
config,
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
let
|
||||
inherit (lib) types;
|
||||
|
||||
cfg = config.services.bifrost;
|
||||
settingsFormat = pkgs.formats.json { };
|
||||
in
|
||||
{
|
||||
options = {
|
||||
services.bifrost = {
|
||||
enable = lib.mkEnableOption "Bifrost HTTP gateway";
|
||||
|
||||
package = lib.mkPackageOption pkgs "bifrost-http" { };
|
||||
|
||||
stateDir = lib.mkOption {
|
||||
type = types.path;
|
||||
default = "/var/lib/bifrost";
|
||||
example = "/var/lib/bifrost";
|
||||
description = "Application data directory (contains config.json and logs).";
|
||||
};
|
||||
|
||||
host = lib.mkOption {
|
||||
type = types.str;
|
||||
default = "127.0.0.1";
|
||||
example = "0.0.0.0";
|
||||
description = "The host address which the Bifrost HTTP server listens to.";
|
||||
};
|
||||
|
||||
port = lib.mkOption {
|
||||
type = types.port;
|
||||
default = 8080;
|
||||
example = 11111;
|
||||
description = "Which port the Bifrost HTTP server listens to.";
|
||||
};
|
||||
|
||||
logLevel = lib.mkOption {
|
||||
type = types.enum [
|
||||
"debug"
|
||||
"info"
|
||||
"warn"
|
||||
"error"
|
||||
];
|
||||
default = "info";
|
||||
description = "Logger level.";
|
||||
};
|
||||
|
||||
logStyle = lib.mkOption {
|
||||
type = types.enum [
|
||||
"json"
|
||||
"pretty"
|
||||
];
|
||||
default = "json";
|
||||
description = "Logger output style.";
|
||||
};
|
||||
|
||||
settings = lib.mkOption {
|
||||
type = types.nullOr settingsFormat.type;
|
||||
default = null;
|
||||
description = ''
|
||||
Optional content for `config.json` under `services.bifrost.stateDir`.
|
||||
|
||||
If set, the file will be written on service start (overwriting any existing config.json).
|
||||
If null, Bifrost will use an existing config.json (or bootstrap from environment).
|
||||
'';
|
||||
example = {
|
||||
providers = [
|
||||
{
|
||||
name = "openai";
|
||||
enabled = true;
|
||||
keys = [
|
||||
{
|
||||
name = "default";
|
||||
value = "env.OPENAI_API_KEY";
|
||||
}
|
||||
];
|
||||
}
|
||||
];
|
||||
};
|
||||
};
|
||||
|
||||
environment = lib.mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = { };
|
||||
description = "Extra environment variables for Bifrost.";
|
||||
example = {
|
||||
OPENAI_API_KEY = "...";
|
||||
};
|
||||
};
|
||||
|
||||
environmentFile = lib.mkOption {
|
||||
description = ''
|
||||
Environment file to be passed to the systemd service.
|
||||
Useful for passing secrets to the service to prevent them from being
|
||||
world-readable in the Nix store.
|
||||
'';
|
||||
type = lib.types.nullOr lib.types.path;
|
||||
default = null;
|
||||
example = "/var/lib/secrets/bifrost.env";
|
||||
};
|
||||
|
||||
openFirewall = lib.mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to open the firewall for Bifrost.
|
||||
This adds `services.bifrost.port` to `networking.firewall.allowedTCPPorts`.
|
||||
'';
|
||||
};
|
||||
|
||||
extraArgs = lib.mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
description = "Extra CLI arguments passed to bifrost-http.";
|
||||
example = [
|
||||
"-some-flag"
|
||||
"value"
|
||||
];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
config = lib.mkIf cfg.enable {
|
||||
assertions =
|
||||
let
|
||||
stateDirStr = toString cfg.stateDir;
|
||||
in
|
||||
[
|
||||
{
|
||||
assertion = builtins.dirOf stateDirStr == "/var/lib";
|
||||
message = "services.bifrost.stateDir must be a direct child of /var/lib (e.g. /var/lib/bifrost) to work with systemd StateDirectory and DynamicUser.";
|
||||
}
|
||||
];
|
||||
|
||||
systemd.services.bifrost =
|
||||
let
|
||||
stateDirStr = toString cfg.stateDir;
|
||||
stateDirName = builtins.baseNameOf stateDirStr;
|
||||
configFile =
|
||||
if cfg.settings == null then null else settingsFormat.generate "bifrost-config.json" cfg.settings;
|
||||
in
|
||||
{
|
||||
description = "Bifrost AI Gateway (bifrost-http)";
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
|
||||
environment = cfg.environment;
|
||||
|
||||
preStart = lib.optionalString (configFile != null) ''
|
||||
install -Dm600 "${configFile}" "${stateDirStr}/config.json"
|
||||
'';
|
||||
|
||||
serviceConfig = {
|
||||
ExecStart = lib.concatStringsSep " " (
|
||||
[
|
||||
(lib.getExe cfg.package)
|
||||
"-host"
|
||||
cfg.host
|
||||
"-port"
|
||||
(toString cfg.port)
|
||||
"-app-dir"
|
||||
stateDirStr
|
||||
"-log-level"
|
||||
cfg.logLevel
|
||||
"-log-style"
|
||||
cfg.logStyle
|
||||
]
|
||||
++ cfg.extraArgs
|
||||
);
|
||||
|
||||
EnvironmentFile = lib.optional (cfg.environmentFile != null) cfg.environmentFile;
|
||||
|
||||
WorkingDirectory = cfg.stateDir;
|
||||
StateDirectory = stateDirName;
|
||||
RuntimeDirectory = "bifrost";
|
||||
RuntimeDirectoryMode = "0755";
|
||||
|
||||
PrivateTmp = true;
|
||||
DynamicUser = true;
|
||||
DevicePolicy = "closed";
|
||||
LockPersonality = true;
|
||||
PrivateUsers = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectControlGroups = true;
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
SystemCallArchitectures = "native";
|
||||
UMask = "0077";
|
||||
RestrictAddressFamilies = [
|
||||
"AF_INET"
|
||||
"AF_INET6"
|
||||
"AF_UNIX"
|
||||
];
|
||||
ProtectClock = true;
|
||||
ProtectProc = "invisible";
|
||||
};
|
||||
};
|
||||
|
||||
networking.firewall = lib.mkIf cfg.openFirewall { allowedTCPPorts = [ cfg.port ]; };
|
||||
};
|
||||
|
||||
meta.maintainers = [
|
||||
{
|
||||
name = "ReStranger";
|
||||
github = "ReStranger";
|
||||
}
|
||||
];
|
||||
}
|
||||
83
nix/packages/bifrost-http.nix
Normal file
83
nix/packages/bifrost-http.nix
Normal file
@@ -0,0 +1,83 @@
|
||||
{
|
||||
pkgs,
|
||||
inputs,
|
||||
src,
|
||||
version,
|
||||
bifrost-ui,
|
||||
}:
|
||||
let
|
||||
lib = pkgs.lib;
|
||||
|
||||
# Bifrost requires Go 1.26 (go.mod/go.work). Force Go 1.26 for buildGoModule.
|
||||
buildGoModule = pkgs.callPackage "${inputs.nixpkgs}/pkgs/build-support/go/module.nix" {
|
||||
go = pkgs.go_1_26 or pkgs.go;
|
||||
};
|
||||
|
||||
transportsLocalReplaces = ''
|
||||
if [ -f transports/go.mod ]; then
|
||||
cat >> transports/go.mod <<'EOF'
|
||||
|
||||
replace github.com/maximhq/bifrost/core => ../core
|
||||
replace github.com/maximhq/bifrost/framework => ../framework
|
||||
replace github.com/maximhq/bifrost/plugins/governance => ../plugins/governance
|
||||
replace github.com/maximhq/bifrost/plugins/compat => ../plugins/compat
|
||||
replace github.com/maximhq/bifrost/plugins/logging => ../plugins/logging
|
||||
replace github.com/maximhq/bifrost/plugins/maxim => ../plugins/maxim
|
||||
replace github.com/maximhq/bifrost/plugins/otel => ../plugins/otel
|
||||
replace github.com/maximhq/bifrost/plugins/semanticcache => ../plugins/semanticcache
|
||||
replace github.com/maximhq/bifrost/plugins/telemetry => ../plugins/telemetry
|
||||
EOF
|
||||
fi
|
||||
'';
|
||||
in
|
||||
buildGoModule {
|
||||
pname = "bifrost-http";
|
||||
inherit version;
|
||||
inherit src;
|
||||
|
||||
modRoot = "transports";
|
||||
subPackages = [ "bifrost-http" ];
|
||||
vendorHash = "sha256-Ck1cwv/DYI9EXmp7U2ZSNXlU+Qok8BFn5bcN1Pv7Nmc=";
|
||||
|
||||
doCheck = false;
|
||||
|
||||
overrideModAttrs = final: prev: {
|
||||
postPatch = (prev.postPatch or "") + transportsLocalReplaces;
|
||||
};
|
||||
|
||||
env = {
|
||||
CGO_ENABLED = "1";
|
||||
};
|
||||
|
||||
nativeBuildInputs = with pkgs; [
|
||||
pkg-config
|
||||
gcc
|
||||
];
|
||||
buildInputs = [ pkgs.sqlite ];
|
||||
|
||||
postPatch = transportsLocalReplaces;
|
||||
|
||||
preBuild = ''
|
||||
# Provide UI assets for //go:embed all:ui
|
||||
rm -rf bifrost-http/ui
|
||||
mkdir -p bifrost-http/ui
|
||||
if [ -d "${bifrost-ui}/ui" ]; then
|
||||
cp -R --no-preserve=mode,ownership,timestamps "${bifrost-ui}/ui/." bifrost-http/ui/
|
||||
else
|
||||
printf '%s\n' '<!doctype html><meta charset="utf-8"><title>Bifrost</title>' > bifrost-http/ui/index.html
|
||||
fi
|
||||
'';
|
||||
|
||||
ldflags = [
|
||||
"-s"
|
||||
"-w"
|
||||
"-X main.Version=${version}"
|
||||
];
|
||||
|
||||
meta = {
|
||||
mainProgram = "bifrost-http";
|
||||
description = "Bifrost HTTP gateway";
|
||||
homepage = "https://github.com/maximhq/bifrost";
|
||||
license = lib.licenses.asl20;
|
||||
};
|
||||
}
|
||||
48
nix/packages/bifrost-ui.nix
Normal file
48
nix/packages/bifrost-ui.nix
Normal file
@@ -0,0 +1,48 @@
|
||||
{
|
||||
pkgs,
|
||||
src,
|
||||
version,
|
||||
}:
|
||||
pkgs.buildNpmPackage {
|
||||
pname = "bifrost-ui";
|
||||
inherit version;
|
||||
inherit src;
|
||||
sourceRoot = "source/ui";
|
||||
|
||||
npmDepsHash = "sha256-+tI2NUJtpHwvI9sAYMXO7r00Y3Pb1E62ms1ZSd3O0hM=";
|
||||
|
||||
# Next's `next/font/google` requires network access at build time.
|
||||
# Nix builds are sandboxed (no network), so patch the layout to avoid
|
||||
# fetching Google Fonts.
|
||||
postPatch = ''
|
||||
cat > app/layout.tsx <<'EOF'
|
||||
import "./globals.css"
|
||||
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en" suppressHydrationWarning>
|
||||
<head>
|
||||
<link rel="dns-prefetch" href="https://getbifrost.ai" />
|
||||
<link rel="preconnect" href="https://getbifrost.ai" />
|
||||
</head>
|
||||
<body className="font-sans antialiased">{children}</body>
|
||||
</html>
|
||||
)
|
||||
}
|
||||
EOF
|
||||
'';
|
||||
|
||||
# Avoid the upstream build script's copy step (writes outside $PWD).
|
||||
npmBuildScript = "build-enterprise";
|
||||
env.NEXT_TELEMETRY_DISABLED = "1";
|
||||
env.NEXT_DISABLE_ESLINT = "1";
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
mkdir -p "$out/ui"
|
||||
cp -R --no-preserve=mode,ownership,timestamps out/. "$out/ui/"
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
}
|
||||
Reference in New Issue
Block a user