aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCollin Williams <96917990+bluedragon1221@users.noreply.github.com>2026-03-06 09:34:57 -0600
committerCollin Williams <96917990+bluedragon1221@users.noreply.github.com>2026-03-06 12:00:17 -0600
commit9561853d2c5c18749dd7d8d3eca25a360e2d8dfe (patch)
tree697e4bfc4a1f14d72f082f11c7a7b92fdecd0061
parente4a00e31c5a0a343749963598fa5d7faa49d60c1 (diff)
Add copilot-api package and service
- Package copilot-api (GitHub Copilot API proxy) using bun - Create systemd service module for copilot-api - Enable service on ganymede listening on localhost:4141 - Add SSH port forwarding from mercury to ganymede for copilot-api - Update gitignore to exclude Claude Code artifacts Co-Authored-By: Claude (claude-sonnet-4.5) <noreply@anthropic.com>
-rw-r--r--.gitignore4
-rw-r--r--hosts/ganymede/config.nix10
-rw-r--r--hosts/ganymede/secrets/copilot-token.age11
-rw-r--r--hosts/mercury/hjem.nix2
-rw-r--r--hosts/mercury/nixos.nix2
-rw-r--r--modules/services/nixos/copilot-api.nix62
-rw-r--r--modules/services/nixos/default.nix1
-rw-r--r--modules/services/options.nix19
-rw-r--r--pkgs/copilot-api/default.nix93
-rw-r--r--secrets.nix1
10 files changed, 204 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
index b2be92b..b40218b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1 +1,5 @@
result
+
+# Claude Code artifacts
+CLAUDE.md
+.claude/
diff --git a/hosts/ganymede/config.nix b/hosts/ganymede/config.nix
index b968e3f..70cbfb2 100644
--- a/hosts/ganymede/config.nix
+++ b/hosts/ganymede/config.nix
@@ -22,6 +22,11 @@
file = ./secrets/wireguard-pk.age;
owner = "systemd-network";
};
+
+ "copilot-token" = {
+ file = ./secrets/copilot-token.age;
+ owner = "copilot-api";
+ };
};
terminal = {
@@ -112,6 +117,11 @@
enable = true;
envFile = config.collinux.secrets."caddy-env".path;
};
+
+ copilot-api = {
+ enable = true;
+ githubToken = config.collinux.secrets."copilot-token".path;
+ };
};
};
}
diff --git a/hosts/ganymede/secrets/copilot-token.age b/hosts/ganymede/secrets/copilot-token.age
new file mode 100644
index 0000000..7dd6e51
--- /dev/null
+++ b/hosts/ganymede/secrets/copilot-token.age
@@ -0,0 +1,11 @@
+-----BEGIN AGE ENCRYPTED FILE-----
+YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IEpnNHlXUSBDSlBB
+cXJaNHU0V3VjVnB5czdya0lYMjBTdFFWRlhvWE5lVTRJenBhL3hRCmREcnJwUEo4
+UDVXQTFxdkk3ZXJrVE05cGZWT040U282bldhcm5QVkdSWW8KLT4gUCl4fUBGLWdy
+ZWFzZSB4LHJZRVt7ICVYIjVGdlVbICR6fCBkdAp4T0pZM1hBRm9WcmFFQjJqaW9Y
+Z2x5cE9mQk83U0pDRzdMUXozTkxjUC9FaW5MOUM4NjM3SlhTTnJvVldjK01KCm03
+TTBFdGtEWkZHVmc4RDVMc1QrNndNR2FHOXRreW5FZEhYdG9BdWZpYVBBb0ZSSFl2
+bFN1UUFWCi0tLSBVaUpmVjhibW5jWW9nS2pJV1JXVjJaZ2JYZ0VHQkY0emwrd1NL
+QXFpNmN3Cv69mt8dZSWNQqM8qDledKrYS7PBZDoIXy+FivL42kaUxF0jXjHXefED
+DsjubBCEK0OiAh6nyvuEn9wbSkaALmpqjMARM32QyAOiam2skY0LB8E=
+-----END AGE ENCRYPTED FILE-----
diff --git a/hosts/mercury/hjem.nix b/hosts/mercury/hjem.nix
index d066f69..d76f76f 100644
--- a/hosts/mercury/hjem.nix
+++ b/hosts/mercury/hjem.nix
@@ -8,6 +8,8 @@
prismlauncher
mpv
+ claude-code
+
lagrange
musescore
diff --git a/hosts/mercury/nixos.nix b/hosts/mercury/nixos.nix
index 21907e5..5d8a63f 100644
--- a/hosts/mercury/nixos.nix
+++ b/hosts/mercury/nixos.nix
@@ -25,7 +25,7 @@
name = "ganymede";
user = "collin";
monitoringPort = 20000;
- extraArguments = "-N -D 9090 collin@williamsfam.us.com";
+ extraArguments = "-N -D 9090 -L 4141:localhost:4141 collin@williamsfam.us.com";
}
];
diff --git a/modules/services/nixos/copilot-api.nix b/modules/services/nixos/copilot-api.nix
new file mode 100644
index 0000000..d9d4da3
--- /dev/null
+++ b/modules/services/nixos/copilot-api.nix
@@ -0,0 +1,62 @@
+{
+ config,
+ lib,
+ pkgs,
+ ...
+}: let
+ cfg = config.collinux.services.copilot-api;
+in {
+ config = lib.mkIf cfg.enable {
+ users.groups."copilot-api" = {};
+ users.users."copilot-api" = {
+ isSystemUser = true;
+ group = "copilot-api";
+ home = "/var/lib/copilot-api";
+ createHome = true;
+ };
+
+ systemd.services."copilot-api" = {
+ description = "GitHub Copilot API Proxy";
+ restartIfChanged = true;
+ wants = ["network-online.target"];
+ after = ["network-online.target"];
+ wantedBy = ["multi-user.target"];
+
+ serviceConfig = {
+ User = "copilot-api";
+ Group = "copilot-api";
+ Type = "simple";
+ Restart = "on-failure";
+ RestartSec = "5s";
+
+ # Load environment variables from file (e.g., GH_TOKEN)
+ EnvironmentFile = lib.mkIf (cfg.githubToken != null) cfg.githubToken;
+
+ # Security hardening
+ PrivateTmp = true;
+ ProtectSystem = "strict";
+ ProtectHome = true;
+ NoNewPrivileges = true;
+ PrivateDevices = true;
+ ProtectKernelTunables = true;
+ ProtectControlGroups = true;
+ RestrictSUIDSGID = true;
+
+ # Allow writing to state directory
+ StateDirectory = "copilot-api";
+ WorkingDirectory = "/var/lib/copilot-api";
+
+ ExecStart = let
+ copilot-api = pkgs.callPackage ../../../pkgs/copilot-api {};
+ # Use bash to read token from environment and pass to command
+ startScript = pkgs.writeShellScript "copilot-api-start" ''
+ exec ${copilot-api}/bin/copilot-api start \
+ --port ${toString cfg.port} \
+ --host ${cfg.listenAddr} \
+ ${lib.optionalString (cfg.githubToken != null) "--github-token \"$GH_TOKEN\""}
+ '';
+ in "${startScript}";
+ };
+ };
+ };
+}
diff --git a/modules/services/nixos/default.nix b/modules/services/nixos/default.nix
index d0e170c..3533908 100644
--- a/modules/services/nixos/default.nix
+++ b/modules/services/nixos/default.nix
@@ -15,5 +15,6 @@
./minecraft.nix
./copyparty.nix
./qbittorrent.nix
+ ./copilot-api.nix
];
}
diff --git a/modules/services/options.nix b/modules/services/options.nix
index 5744471..17a4e6b 100644
--- a/modules/services/options.nix
+++ b/modules/services/options.nix
@@ -164,5 +164,24 @@ in {
example = "/run/secrets.d/caddy-env";
};
};
+
+ copilot-api = {
+ enable = mkEnableOption "GitHub Copilot API proxy";
+ listenAddr = mkOption {
+ description = "Address to listen on";
+ type = ipAddr;
+ default = "127.0.0.1";
+ };
+ port = mkOption {
+ description = "Port to listen on";
+ type = types.port;
+ default = 4141;
+ };
+ githubToken = mkOption {
+ description = "Path to file containing GitHub token";
+ type = types.nullOr types.str;
+ default = null;
+ };
+ };
};
}
diff --git a/pkgs/copilot-api/default.nix b/pkgs/copilot-api/default.nix
new file mode 100644
index 0000000..a66fe1a
--- /dev/null
+++ b/pkgs/copilot-api/default.nix
@@ -0,0 +1,93 @@
+{
+ pkgs,
+ lib,
+ ...
+}: let
+ inherit (pkgs) stdenv bun makeBinaryWrapper fetchFromGitHub;
+
+ node_modules = stdenv.mkDerivation {
+ pname = "copilot-api-node_modules";
+ version = "0.7.0";
+
+ src = fetchFromGitHub {
+ owner = "ericc-ch";
+ repo = "copilot-api";
+ tag = "v0.7.0";
+ hash = "sha256-rUUqf9QalVZDN3aw9ze5Uh+y5xvH6zdSgGN6ZLDjkDQ=";
+ };
+
+ nativeBuildInputs = [bun];
+
+ impureEnvVars = lib.fetchers.proxyImpureEnvVars;
+ dontPatchShebangs = true;
+
+ patchPhase = ''
+ # Remove prepare script that tries to setup git hooks
+ sed -i '/"prepare":/d' package.json
+ '';
+
+ buildPhase = ''
+ runHook preBuild
+ export HOME=$TMPDIR
+ bun install --no-progress --frozen-lockfile
+ runHook postBuild
+ '';
+
+ installPhase = ''
+ runHook preInstall
+ mkdir -p $out
+ cp -R ./node_modules $out
+ runHook postInstall
+ '';
+
+ outputHash = "sha256-+AH8eRG3SwwU86Pa/hFSW5nWD0v1po6z7xoLKUzq3qY=";
+ outputHashAlgo = "sha256";
+ outputHashMode = "recursive";
+ };
+in
+ stdenv.mkDerivation {
+ pname = "copilot-api";
+ version = "0.7.0";
+
+ src = fetchFromGitHub {
+ owner = "ericc-ch";
+ repo = "copilot-api";
+ tag = "v0.7.0";
+ hash = "sha256-rUUqf9QalVZDN3aw9ze5Uh+y5xvH6zdSgGN6ZLDjkDQ=";
+ };
+
+ nativeBuildInputs = [makeBinaryWrapper];
+ buildInputs = [bun pkgs.nodejs];
+
+ configurePhase = ''
+ runHook preConfigure
+ # Don't copy node_modules - let bun install them fresh
+ runHook postConfigure
+ '';
+
+ buildPhase = ''
+ runHook preBuild
+ export HOME=$TMPDIR
+ # Use the cached node_modules
+ cp -R ${node_modules}/node_modules .
+ chmod -R +w node_modules
+ # Try using bun's native build with correct entry point
+ mkdir -p dist
+ ${bun}/bin/bun build src/main.ts --outdir dist --target bun --format esm --sourcemap
+ runHook postBuild
+ '';
+
+ installPhase = ''
+ runHook preInstall
+
+ mkdir -p $out/lib/copilot-api
+ cp -r dist node_modules package.json $out/lib/copilot-api/
+
+ mkdir -p $out/bin
+ makeWrapper ${bun}/bin/bun $out/bin/copilot-api \
+ --prefix PATH : ${lib.makeBinPath [bun]} \
+ --add-flags "$out/lib/copilot-api/dist/main.js"
+
+ runHook postInstall
+ '';
+ }
diff --git a/secrets.nix b/secrets.nix
index a46e065..67ab5fd 100644
--- a/secrets.nix
+++ b/secrets.nix
@@ -8,4 +8,5 @@ in {
"hosts/ganymede/secrets/collin-copyparty-password.age".publicKeys = [mercury.host_pubkey ganymede.host_pubkey];
"hosts/ganymede/secrets/collin-forgejo-password.age".publicKeys = [mercury.host_pubkey ganymede.host_pubkey];
"hosts/ganymede/secrets/wireguard-pk.age".publicKeys = [ganymede.host_pubkey];
+ "hosts/ganymede/secrets/copilot-token.age".publicKeys = [ganymede.host_pubkey];
}