diff --git a/Makefile b/Makefile index 5695572df..3d2fa75a4 100644 --- a/Makefile +++ b/Makefile @@ -135,7 +135,7 @@ setup: nix-setup ## Basic Nix setup (alias for nix-setup). setup-dev: nix-setup git-submodule-sync shell-install ## Set up local development environment (Nix + submodules + shell). .PHONY: switch -switch: nix-switch launchctl ## Apply Nix configuration and restart launchd agents. +switch: nix-switch ## Apply Nix configuration and restart launchd agents. .PHONY: test test: neovim-test @@ -320,7 +320,12 @@ nix-switch: ## Activate Nix configuration. elif [ "$(NIX_CONFIG_TYPE)" = "nixosConfigurations" ]; then \ sudo $(NIX_ALLOW_UNFREE) $(NIX_EXEC) run $(NIX_FLAGS) --impure nixpkgs#nixos-rebuild -- switch --flake .#$(NIX_SYSTEM); \ elif [ "$(NIX_CONFIG_TYPE)" = "homeConfigurations" ]; then \ - USER=$(NIX_USERNAME) $(NIX_ALLOW_UNFREE) $(NIX_EXEC) run $(NIX_FLAGS) --impure .#$(NIX_CONFIG_TYPE)."$(NIX_USERNAME)@$(NIX_SYSTEM)".activationPackage; \ + if [ -n "$(HOST)" ]; then \ + echo "Switching named home config: $(HOST)"; \ + USER=$(NIX_USERNAME) $(NIX_ALLOW_UNFREE) $(NIX_EXEC) run $(NIX_FLAGS) --impure .#homeConfigurations.$(HOST).activationPackage; \ + else \ + USER=$(NIX_USERNAME) $(NIX_ALLOW_UNFREE) $(NIX_EXEC) run $(NIX_FLAGS) --impure .#$(NIX_CONFIG_TYPE)."$(NIX_USERNAME)@$(NIX_SYSTEM)".activationPackage; \ + fi; \ else \ echo "Unsupported OS $(OS) for non-CI switch"; \ exit 1; \ diff --git a/bun.lock b/bun.lock index 945a08b7e..2ed968787 100644 --- a/bun.lock +++ b/bun.lock @@ -5,6 +5,7 @@ "": { "name": "dotfiles", "dependencies": { + "@beads/bd": "^0.29.0", "@biomejs/biome": "^2.3.8", "@getgrit/cli": "^0.1.0-alpha.1743007075", "@github/copilot": "^0.0.367", @@ -45,6 +46,8 @@ "@asamuzakjp/css-color": ["@asamuzakjp/css-color@3.2.0", "", { "dependencies": { "@csstools/css-calc": "^2.1.3", "@csstools/css-color-parser": "^3.0.9", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw=="], + "@beads/bd": ["@beads/bd@0.29.0", "", { "os": [ "linux", "win32", "darwin", ], "cpu": [ "x64", "arm64", ], "bin": { "bd": "bin/bd.js" } }, "sha512-O6XF/smRgv5GlnMS4nlL4WSsZGS62hLF2Z5Bp1s25mtFZ0dITyVedbvffuPHaL74XX6s1AlHHPjJyoI7sj7icg=="], + "@biomejs/biome": ["@biomejs/biome@2.3.8", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.3.8", "@biomejs/cli-darwin-x64": "2.3.8", "@biomejs/cli-linux-arm64": "2.3.8", "@biomejs/cli-linux-arm64-musl": "2.3.8", "@biomejs/cli-linux-x64": "2.3.8", "@biomejs/cli-linux-x64-musl": "2.3.8", "@biomejs/cli-win32-arm64": "2.3.8", "@biomejs/cli-win32-x64": "2.3.8" }, "bin": { "biome": "bin/biome" } }, "sha512-Qjsgoe6FEBxWAUzwFGFrB+1+M8y/y5kwmg5CHac+GSVOdmOIqsAiXM5QMVGZJ1eCUCLlPZtq4aFAQ0eawEUuUA=="], "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.3.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-HM4Zg9CGQ3txTPflxD19n8MFPrmUAjaC7PQdLkugeeC0cQ+PiVrd7i09gaBS/11QKsTDBJhVg85CEIK9f50Qww=="], diff --git a/config/claude/settings.json b/config/claude/settings.json index 4cd56995f..96bc38568 100644 --- a/config/claude/settings.json +++ b/config/claude/settings.json @@ -26,6 +26,7 @@ "Bash(git cherry-pick:*)", "Bash(git commit:*)", "Bash(git diff:*)", + "Bash(git fetch:*)", "Bash(git log:*)", "Bash(git merge:*)", "Bash(git pull:*)", diff --git a/config/cliproxyapi/config.yaml b/config/cliproxyapi/config.yaml index 99c36479f..4fde4d2a8 100644 --- a/config/cliproxyapi/config.yaml +++ b/config/cliproxyapi/config.yaml @@ -4,7 +4,7 @@ port: 8317 remote-management: # Whether to allow remote (non-localhost) management access. # When false, only localhost can access management endpoints (a key is still required). - allow-remote: false + allow-remote: true # Management key. If a plaintext value is provided here, it will be hashed on startup. # All management requests (even from localhost) require this key. # Leave empty to disable the Management API entirely (404 for all /v0/management routes). diff --git a/config/cliproxyapi/default.nix b/config/cliproxyapi/default.nix index 4f0d8e3a0..0a27d911d 100644 --- a/config/cliproxyapi/default.nix +++ b/config/cliproxyapi/default.nix @@ -4,4 +4,8 @@ home.file.".cli-proxy-api/config.template.yaml" = { source = config.lib.file.mkOutOfStoreSymlink ./config.yaml; }; + # Example config - required by cliproxyapi's object-backed config bootstrap + home.file.".cli-proxy-api/config.example.yaml" = { + source = config.lib.file.mkOutOfStoreSymlink ./config.yaml; + }; } diff --git a/config/default.nix b/config/default.nix index 7dc66b8d3..6e7eac9ad 100644 --- a/config/default.nix +++ b/config/default.nix @@ -8,6 +8,7 @@ ./factory ./ghostty ./hammerspoon + ./k3s ./karabiner ./opencode ./serena diff --git a/config/k3s/config.yaml b/config/k3s/config.yaml new file mode 100644 index 000000000..f7b60c712 --- /dev/null +++ b/config/k3s/config.yaml @@ -0,0 +1,2 @@ +disable: + - traefik diff --git a/config/k3s/default.nix b/config/k3s/default.nix new file mode 100644 index 000000000..fff6a6c6d --- /dev/null +++ b/config/k3s/default.nix @@ -0,0 +1,22 @@ +{ + config, + lib, + pkgs, + ... +}: +{ + # k3s config file stored in home directory + home.file.".config/k3s/config.yaml" = lib.mkIf pkgs.stdenv.isLinux { + source = config.lib.file.mkOutOfStoreSymlink ./config.yaml; + }; + + # Activation script to sync config to /etc/rancher/k3s/ + home.activation.k3s-config = lib.mkIf pkgs.stdenv.isLinux ( + lib.hm.dag.entryAfter [ "writeBoundary" ] '' + if [ -f "$HOME/.config/k3s/config.yaml" ]; then + $DRY_RUN_CMD /usr/bin/sudo mkdir -p /etc/rancher/k3s + $DRY_RUN_CMD /usr/bin/sudo cp "$HOME/.config/k3s/config.yaml" /etc/rancher/k3s/config.yaml + fi + '' + ); +} diff --git a/flake.nix b/flake.nix index a5896300a..5a9be38d1 100644 --- a/flake.nix +++ b/flake.nix @@ -129,6 +129,11 @@ username = "runner"; system = "aarch64-linux"; }; + kyber = import ./named-hosts/kyber { + inherit inputs; + username = "ubuntu"; + system = "x86_64-linux"; + }; }; }; diff --git a/home-manager/modules/default.nix b/home-manager/modules/default.nix index 256b443a4..a84211003 100644 --- a/home-manager/modules/default.nix +++ b/home-manager/modules/default.nix @@ -1,4 +1,5 @@ [ ./npm-globals + ./tailscale ./yek ] diff --git a/home-manager/modules/tailscale/default.nix b/home-manager/modules/tailscale/default.nix new file mode 100644 index 000000000..f9be42abb --- /dev/null +++ b/home-manager/modules/tailscale/default.nix @@ -0,0 +1,277 @@ +# Tailscale configuration +{ + config, + lib, + pkgs, + ... +}: +with lib; +let + cfg = config.modules.tailscale; + + # Check if user-level services should be enabled + # Only enable if serviceConfig is explicitly set (not for system-level service only) + configEnabled = cfg.serviceConfig != { } && cfg.serviceConfig != null; + + # System service unit file (for non-NixOS Linux) + tailscaledServiceFile = pkgs.writeText "tailscaled.service" '' + [Unit] + Description=Tailscale node agent + Documentation=https://tailscale.com/kb/ + Wants=network-pre.target + After=network-pre.target NetworkManager.service systemd-resolved.service + + [Service] + ExecStartPre=${cfg.tailscaled.package}/bin/tailscaled --cleanup + ExecStart=${cfg.tailscaled.package}/bin/tailscaled --state=/var/lib/tailscale/tailscaled.state --socket=/run/tailscale/tailscaled.sock --port ${toString cfg.port} + ExecStopPost=${cfg.tailscaled.package}/bin/tailscaled --cleanup + Restart=on-failure + RuntimeDirectory=tailscale + RuntimeDirectoryMode=0755 + StateDirectory=tailscale + StateDirectoryMode=0700 + CacheDirectory=tailscale + CacheDirectoryMode=0750 + Type=notify + + [Install] + WantedBy=multi-user.target + ''; +in +{ + options.modules.tailscale = { + enable = mkEnableOption "Tailscale VPN service"; + + # Tailscale daemon configuration + tailscaled = { + package = mkOption { + type = types.package; + default = pkgs.tailscale; + description = "Tailscale package to use"; + }; + + stateDir = mkOption { + type = types.str; + default = "${config.xdg.dataHome}/tailscale"; + description = "Directory for Tailscale state files"; + }; + + socketPath = mkOption { + type = types.str; + default = "${config.home.homeDirectory}/.local/run/tailscale/tailscaled.sock"; + description = "Socket path for Tailscale daemon"; + }; + }; + + # Optional auth key + authKey = mkOption { + type = types.str; + default = ""; + description = "Tailscale auth key (use agenix for secrets)"; + }; + + # Optional auth key file (better for secrets) + authKeyFile = mkOption { + type = types.str; + default = ""; + description = "Path to file containing Tailscale auth key (better for secrets)"; + }; + + # Tailscale up options + acceptRoutes = mkOption { + type = types.bool; + default = false; + description = "Accept routes from other nodes"; + }; + + advertiseExitNode = mkOption { + type = types.bool; + default = false; + description = "Advertise as exit node"; + }; + + useExitNode = mkOption { + type = types.str; + default = ""; + description = "Use specific node as exit node"; + }; + + extraUpArgs = mkOption { + type = types.listOf types.str; + default = [ ]; + description = "Additional arguments to pass to tailscale up"; + }; + + port = mkOption { + type = types.int; + default = 41641; + description = "UDP port for Tailscale traffic"; + }; + + installSystemService = mkOption { + type = types.bool; + default = true; + description = "Install system-level tailscaled service (requires sudo, for non-NixOS Linux)"; + }; + + # Important directories and files + directories = mkOption { + type = types.attrsOf types.anything; + default = { }; + description = "Integration with home-manager's directories option"; + }; + + files = mkOption { + type = types.attrsOf types.anything; + default = { }; + description = "Integration with home-manager's files option"; + }; + + # Service module + serviceConfig = mkOption { + type = types.attrsOf types.anything; + default = { }; + description = "Optional systemd service configuration override"; + }; + }; + + config = mkIf cfg.enable { + home.packages = [ cfg.tailscaled.package ]; + + # Declare directories and files for home-manager + home.file.".local/share/tailscale/tailscaled.state".source = + config.lib.file.mkOutOfStoreSymlink "${config.home.homeDirectory}/.local/state/tailscale/tailscaled.state"; + + # Tailscaled service + systemd.user.services.tailscaled = mkIf configEnabled { + Unit = { + Description = "Tailscale VPN daemon"; + After = [ "network-online.target" ]; + Wants = [ "network-online.target" ]; + }; + + Service = { + ExecStart = "${pkgs.tailscale}/bin/tailscaled --state=${config.xdg.dataHome}/tailscale/tailscaled.state --socket=${cfg.tailscaled.socketPath}"; + Restart = "on-failure"; + RestartSec = 5; + } + // cfg.serviceConfig.service or { }; + + Install.WantedBy = [ "default.target" ]; + }; + + # Tailscale up service (runs once to connect) + systemd.user.services.tailscale-up = mkIf configEnabled { + Unit = { + Description = "Connect Tailscale to network"; + After = [ "tailscaled.service" ]; + Requires = [ "tailscaled.service" ]; + }; + + Service = + let + authKeyArg = + if cfg.authKey != "" then + "--authkey=${cfg.authKey}" + else if cfg.authKeyFile != "" then + "--authkey-file=${cfg.authKeyFile}" + else + ""; + + upArgs = + lib.filter (x: x != "") [ + authKeyArg + (optionalString cfg.acceptRoutes "--accept-routes") + (optionalString cfg.advertiseExitNode "--advertise-exit-node") + (optionalString (cfg.useExitNode != "") "--exit-node=${cfg.useExitNode}") + ] + ++ cfg.extraUpArgs; + + upCommand = "${pkgs.tailscale}/bin/tailscale up ${concatStringsSep " " upArgs}"; + in + { + Type = "oneshot"; + RemainAfterExit = true; + ExecStart = upCommand; + ExecStop = "${pkgs.tailscale}/bin/tailscale down"; + }; + + Install.WantedBy = [ "default.target" ]; + }; + + home.activation.createTailscaleDirs = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + TAILSCALE_STATE_DIR="${config.xdg.dataHome}/tailscale" + TAILSCALE_RUN_DIR="$(dirname "${cfg.tailscaled.socketPath}")" + + # Create directories + $DRY_RUN_CMD mkdir -p "$TAILSCALE_STATE_DIR" + $DRY_RUN_CMD mkdir -p "$TAILSCALE_RUN_DIR" + + # Set proper permissions + $DRY_RUN_CMD chmod 700 "$TAILSCALE_STATE_DIR" + $DRY_RUN_CMD chmod 700 "$TAILSCALE_RUN_DIR" + ''; + + # Install system-level tailscaled service (requires sudo) + # Uses nix-generated service file for full declarative config + home.activation.installTailscaleService = mkIf cfg.installSystemService ( + lib.hm.dag.entryAfter [ "writeBoundary" ] '' + SERVICE_FILE="/etc/systemd/system/tailscaled.service" + NIX_SERVICE="${tailscaledServiceFile}" + SUDOERS_FILE="/etc/sudoers.d/nix-tailscale" + + # Resolve an elevated command helper + SUDO_CMD="" + if command -v sudo >/dev/null 2>&1; then + SUDO_CMD="sudo" + elif [ -x /usr/bin/sudo ]; then + SUDO_CMD="/usr/bin/sudo" + elif command -v doas >/dev/null 2>&1; then + SUDO_CMD="doas" + elif [ -x /usr/bin/doas ]; then + SUDO_CMD="/usr/bin/doas" + elif [ "$(id -u)" -ne 0 ]; then + echo "Tailscale system service installation requires root privileges, but sudo/doas is not available." >&2 + echo "Either install sudo, configure doas, or run home-manager as root." >&2 + exit 1 + fi + + run_root_cmd() { + if [ -n "$SUDO_CMD" ]; then + ''${DRY_RUN_CMD:-} "$SUDO_CMD" "$@" + else + ''${DRY_RUN_CMD:-} "$@" + fi + } + + # Only install if service file differs from nix-generated one + if ! cmp -s "$NIX_SERVICE" "$SERVICE_FILE" 2>/dev/null; then + echo "Installing tailscaled systemd service (requires root)..." + run_root_cmd cp "$NIX_SERVICE" "$SERVICE_FILE" + run_root_cmd systemctl daemon-reload + run_root_cmd systemctl enable tailscaled + echo "Tailscaled service installed." + fi + + # Configure sudo to include Nix profile paths + echo "Configuring sudo PATH for Nix packages..." + SUDOERS_CONTENT="# Added by home-manager for Nix Tailscale + Defaults secure_path=\"${config.home.homeDirectory}/.nix-profile/bin:/nix/var/nix/profiles/default/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\" + " + + # Create temporary file with correct content + TEMP_SUDOERS=$(mktemp) + echo "$SUDOERS_CONTENT" > "$TEMP_SUDOERS" + + # Only update if different or doesn't exist + if ! cmp -s "$TEMP_SUDOERS" "$SUDOERS_FILE" 2>/dev/null; then + run_root_cmd cp "$TEMP_SUDOERS" "$SUDOERS_FILE" + run_root_cmd chmod 0440 "$SUDOERS_FILE" + echo "Sudo PATH configured. You can now use: sudo tailscale login" + fi + + rm -f "$TEMP_SUDOERS" + '' + ); + }; +} diff --git a/home-manager/packages/default.nix b/home-manager/packages/default.nix index 7fe9768bd..ffe8a1fca 100644 --- a/home-manager/packages/default.nix +++ b/home-manager/packages/default.nix @@ -24,6 +24,7 @@ with pkgs; curlie cursor-cli delta + doppler difftastic direnv duf diff --git a/home-manager/programs/fish/default.nix b/home-manager/programs/fish/default.nix index c1378fce1..a75a0cf2f 100644 --- a/home-manager/programs/fish/default.nix +++ b/home-manager/programs/fish/default.nix @@ -40,6 +40,7 @@ shellAliases = { neofetch = "fastfetch"; + cliproxyapi = "cd ~/.cli-proxy-api && /opt/homebrew/bin/cliproxyapi -config config.yaml"; ocd = "bun run ${config.home.homeDirectory}/ghq/github.com/shunkakinoki/open-composer/apps/cli/src/index.ts"; }; shellAbbrs = { @@ -58,6 +59,7 @@ gco = "_gco_function"; grco = "_grco_function"; grcr = "_grcr_function"; + kyber = "_kyber_function"; fch = "_fzf_cmd_history --allow-execute"; fdp = "_fzf_directory_picker --allow-cd --prompt-name Projects ~/"; ffp = "_fzf_file_picker --allow-open-in-editor --prompt-name Files"; @@ -130,6 +132,7 @@ "_grco_function" "_grcr_function" "_hm_load_env_file" + "_kyber_function" "fish_user_key_bindings" ] ); diff --git a/home-manager/programs/fish/functions/_kyber_function.fish b/home-manager/programs/fish/functions/_kyber_function.fish new file mode 100644 index 000000000..a90a09a56 --- /dev/null +++ b/home-manager/programs/fish/functions/_kyber_function.fish @@ -0,0 +1,9 @@ +function _kyber_function --description "SSH to Kyber server" + set -l password (security find-generic-password -s "ssh ubuntu@91.242.214.231" -w 2>/dev/null) + if test -n "$password" + sshpass -p $password ssh ubuntu@$KYBER_IP_ADDR + else + echo "Password not found in Keychain. Run: security add-generic-password -s 'ssh ubuntu@91.242.214.231' -a ubuntu -w" + ssh ubuntu@$KYBER_IP_ADDR + end +end diff --git a/named-hosts/kyber/README.md b/named-hosts/kyber/README.md new file mode 100644 index 000000000..b947c51d4 --- /dev/null +++ b/named-hosts/kyber/README.md @@ -0,0 +1,50 @@ +# Kyber Host Configuration + +Ubuntu Linux server managed via home-manager with Tailscale VPN. + +## Initial Setup + +On the Kyber server, run: + +```bash +# 1. Install Tailscale +curl -fsSL https://tailscale.com/install.sh | sh +sudo systemctl enable --now tailscaled +sudo tailscale up + +# 2. Install Nix +curl -L https://nixos.org/nix/install | sh -s -- --daemon + +# 3. Clone dotfiles +git clone https://github.com/shunkakinoki/dotfiles ~/dotfiles +cd ~/dotfiles + +# 4. Apply configuration +make switch +``` + +## Managing Secrets + +### Get Kyber's SSH public key + +```bash +ssh ubuntu@kyber "cat ~/.ssh/id_ed25519.pub" +``` + +Add this key to `secrets.nix`, then encrypt secrets: + +```bash +# Encrypt Tailscale auth key +make encrypt-key-kyber KEY_FILE=/path/to/tailscale-auth-key.txt + +# Verify +make decrypt-key-kyber KEY_FILE=tailscale-auth +``` + +## SSH Access + +Once Tailscale is set up: + +```bash +kyber # Fish abbreviation that runs: ssh ubuntu@kyber +``` diff --git a/named-hosts/kyber/default.nix b/named-hosts/kyber/default.nix new file mode 100644 index 000000000..7d719f76d --- /dev/null +++ b/named-hosts/kyber/default.nix @@ -0,0 +1,70 @@ +# Kyber - Ubuntu Linux server configuration +{ + inputs, + username ? "ubuntu", + system ? "x86_64-linux", +}: +let + inherit (inputs) home-manager agenix; + overlays = import ../../overlays { inherit inputs; }; + nixpkgsConfig = import ../../lib/nixpkgs-config.nix { + nixpkgsLib = inputs.nixpkgs.lib; + }; + pkgs = import inputs.nixpkgs { + inherit system overlays; + config = nixpkgsConfig; + }; + lib = pkgs.lib; +in +home-manager.lib.homeManagerConfiguration { + inherit pkgs; + extraSpecialArgs = { + inherit inputs username pkgs; + }; + modules = [ + agenix.homeManagerModules.default + ../../home-manager/default.nix + { + home = { + username = username; + homeDirectory = lib.mkForce "/home/${username}"; + activation.backupExistingFiles = lib.mkForce { + before = [ "checkLinkTargets" ]; + after = [ ]; + data = '' + # Backup existing bash configuration files + for file in .bashrc .profile .bash_profile; do + if [ -f "$HOME/$file" ] && [ ! -L "$HOME/$file" ]; then + echo "Backing up existing $file to $file.hm-backup" + mv "$HOME/$file" "$HOME/$file.hm-backup" + fi + done + ''; + }; + }; + + # Agenix configuration + age.identityPaths = [ "/home/${username}/.ssh/id_ed25519" ]; + age.secrets = builtins.mapAttrs (name: value: { file = value.file; }) (import ./secrets.nix); + + programs.home-manager.enable = true; + + # Enable XDG directories + xdg.enable = true; + + # Tailscale configuration + # Using system-level service only (via installSystemService) + # User services are disabled by leaving serviceConfig empty + modules.tailscale = { + enable = true; + installSystemService = true; + # Auth key will be provided via agenix secret + # authKeyFile = config.age.secrets."keys/tailscale-auth.age".path; + extraUpArgs = [ + "--reset" + "--accept-dns=false" + ]; + }; + } + ]; +} diff --git a/named-hosts/kyber/secrets.nix b/named-hosts/kyber/secrets.nix new file mode 100644 index 000000000..980660cf5 --- /dev/null +++ b/named-hosts/kyber/secrets.nix @@ -0,0 +1,22 @@ +# Kyber secrets - managed by agenix +# To add a new secret: +# 1. Add the secret definition here +# 2. Run: make encrypt-key-kyber KEY_FILE=/path/to/secret +{ + # Tailscale auth key - generate from https://login.tailscale.com/admin/settings/keys + # "keys/tailscale-auth.age" = { + # file = ./keys/tailscale-auth.age; + # publicKeys = [ + # # Kyber's SSH public key (run: ssh ubuntu@kyber "cat ~/.ssh/id_ed25519.pub") + # "ssh-ed25519 AAAA..." + # ]; + # }; + + # SSH key sync (your local key encrypted for kyber) + # "keys/id_ed25519.age" = { + # file = ./keys/id_ed25519.age; + # publicKeys = [ + # "ssh-ed25519 AAAA..." + # ]; + # }; +} diff --git a/named-hosts/kyber/setup.sh b/named-hosts/kyber/setup.sh new file mode 100755 index 000000000..37a707edb --- /dev/null +++ b/named-hosts/kyber/setup.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Kyber (Ubuntu) Initial Setup Script +# Run this on the Kyber server to bootstrap the environment + +set -e + +echo "🚀 Setting up Kyber server..." + +# 1. Install Tailscale +echo "📦 Installing Tailscale..." +if ! command -v tailscale &>/dev/null; then + curl -fsSL https://tailscale.com/install.sh | sh +fi + +# 2. Enable and start Tailscale daemon +echo "🔧 Enabling Tailscale daemon..." +sudo systemctl enable --now tailscaled + +# 3. Connect to Tailscale (will prompt for auth) +echo "🔗 Connecting to Tailscale..." +sudo tailscale up + +# 4. Verify Tailscale connection +echo "✅ Tailscale status:" +tailscale status diff --git a/nix-darwin/config/homebrew.nix b/nix-darwin/config/homebrew.nix index df49b1da6..6f4747c73 100644 --- a/nix-darwin/config/homebrew.nix +++ b/nix-darwin/config/homebrew.nix @@ -41,6 +41,7 @@ "protobuf" "reth" "sheldon" + "sshpass" "temporal" "watchexec" ]; diff --git a/package.json b/package.json index 86284a8e4..4de70a7cc 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "license": "ISC", "packageManager": "bun@1.3.0", "dependencies": { + "@beads/bd": "^0.29.0", "@biomejs/biome": "^2.3.8", "@getgrit/cli": "^0.1.0-alpha.1743007075", "@github/copilot": "^0.0.367",