Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions home-manager/programs/ssh/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,14 @@
extraOptions = {
IgnoreUnknown = "UseKeychain";
UseKeyChain = "yes";
# Enable post-quantum key exchange algorithms
# sntrup761x25519 is a hybrid post-quantum algorithm combining
# Streamlined NTRU Prime (sntrup761) with X25519
KexAlgorithms = "sntrup761x25519-sha512@openssh.com,curve25519-sha256,curve25519-sha256@libssh.org,diffie-hellman-group-exchange-sha256";
# Prefer modern host key algorithms
HostKeyAlgorithms = "ssh-ed25519-cert-v01@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256";
# Prefer modern public key algorithms
PubkeyAcceptedAlgorithms = "ssh-ed25519-cert-v01@openssh.com,ssh-ed25519,rsa-sha2-512-cert-v01@openssh.com,rsa-sha2-512,rsa-sha2-256-cert-v01@openssh.com,rsa-sha2-256";
};
};
"localhost" = {
Expand All @@ -30,6 +38,7 @@
};
"github.com" = {
serverAliveInterval = 0;
identityFile = "~/.ssh/id_ed25519_github";
extraOptions = {
ControlMaster = "auto";
ControlPath = "~/.ssh/github.sock";
Expand Down
21 changes: 15 additions & 6 deletions named-hosts/galactica/secrets.nix
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
let
# Galactica's SSH public key
galactica = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEKze2jlpV7SyTKA2ezqbumpCiDn+5Sj4z5SxrqfzesX shunkakinoki@gmail.com";
# Kyber's SSH public key
kyber = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO0IZtP3KSzY6GVSZ+R+VQYYfu3sEOVaQGDblQxAtwNM ubuntu@kyber";
# All machines that can decrypt shared secrets
allMachines = [
galactica
kyber
];
in
Comment on lines +1 to +11

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This let block defining public keys is also present in named-hosts/kyber/secrets.nix. To improve maintainability and avoid having to update keys in multiple places, consider moving the public key definitions to a shared file (e.g., named-hosts/public-keys.nix) and importing it where needed.

{
# Shared SSH key for GitHub authentication (accessible on all machines)
"keys/id_ed25519.age" = {
file = ./keys/id_ed25519.age;
publicKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEKze2jlpV7SyTKA2ezqbumpCiDn+5Sj4z5SxrqfzesX shunkakinoki@gmail.com"
];
publicKeys = allMachines;
};
# GPG key (galactica only)
"keys/gpg.age" = {
file = ./keys/gpg.age;
publicKeys = [
"ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEKze2jlpV7SyTKA2ezqbumpCiDn+5Sj4z5SxrqfzesX shunkakinoki@gmail.com"
];
publicKeys = [ galactica ];
};
}
28 changes: 28 additions & 0 deletions named-hosts/kyber/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,3 +48,31 @@ Once Tailscale is set up:
```bash
kyber # Fish abbreviation that runs: ssh ubuntu@kyber
```

## Syncing SSH Keys from Galactica

To sync the GitHub SSH key from galactica to kyber:

### On Galactica

```bash
cd ~/dotfiles
git pull
make rekey-galactica
git add named-hosts/galactica/keys/
git commit -m "chore(agenix): rekey secrets for kyber access"
git push
```

### On Kyber

```bash
cd ~/dotfiles
git pull
make switch

# Test GitHub access
ssh -T git@github.com
```

The SSH key will be automatically decrypted and deployed to `~/.ssh/id_ed25519_github`.
17 changes: 16 additions & 1 deletion named-hosts/kyber/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,22 @@ home-manager.lib.homeManagerConfiguration {

# Agenix configuration
age.identityPaths = [ "/home/${username}/.ssh/id_ed25519" ];
age.secrets = builtins.mapAttrs (name: value: { file = value.file; }) (import ./secrets.nix);
age.secrets = builtins.mapAttrs (
name: value:
{
file = value.file;
# Deploy GitHub SSH key to ~/.ssh/ with correct permissions
}
// (
if name == "keys/id_ed25519.age" then
{
path = "/home/${username}/.ssh/id_ed25519_github";
mode = "0600";
}
else
{ }
)
) (import ./secrets.nix);
Comment on lines +48 to +63

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This logic for conditionally setting the path and mode for the GitHub SSH key can be simplified using lib.optionalAttrs from nixpkgs.lib. This makes the code more concise and idiomatic.

The comment explaining the deployment of the SSH key is also better placed next to the conditional logic.

      age.secrets = builtins.mapAttrs (
        name: value: { file = value.file; } //
          # Deploy GitHub SSH key to ~/.ssh/ with correct permissions
          (lib.optionalAttrs (name == "keys/id_ed25519.age") {
            path = "/home/${username}/.ssh/id_ed25519_github";
            mode = "0600";
          })
      ) (import ./secrets.nix);


programs.home-manager.enable = true;

Expand Down
38 changes: 38 additions & 0 deletions named-hosts/kyber/rekey-galactica.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env bash
# Remotely rekey galactica secrets to include kyber's public key
# This script should be run from kyber to trigger the rekey on galactica

set -e

Copilot AI Dec 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The script uses 'set -e' but should use 'set -euo pipefail' for more robust error handling. This ensures that:

  • 'u' treats unset variables as errors
  • 'o pipefail' ensures failures in pipes are caught

The reference script at scripts/update-gitalias.sh uses 'set -euo pipefail', which should be the standard for bash scripts in this codebase.

Suggested change
set -e
set -euo pipefail

Copilot uses AI. Check for mistakes.

echo "🔑 Rekeying galactica secrets to include kyber..."
echo ""
echo "This will:"
echo "1. Connect to galactica via Tailscale SSH"
echo "2. Run the rekey command to re-encrypt secrets with both keys"
echo "3. Commit and push the changes"
echo ""

# Try Tailscale SSH first
if tailscale ssh shunkakinoki@galactica "cd ~/dotfiles && make rekey-galactica" 2>/dev/null; then

@cubic-dev-ai cubic-dev-ai Bot Dec 13, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: The success path is missing git commit and push operations on galactica. After make rekey-galactica completes successfully, the re-encrypted secrets need to be committed and pushed before git pull on kyber can receive them. Currently, git pull will fetch nothing because galactica never pushed the changes.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At named-hosts/kyber/rekey-galactica.sh, line 16:

<comment>The success path is missing git commit and push operations on galactica. After `make rekey-galactica` completes successfully, the re-encrypted secrets need to be committed and pushed before `git pull` on kyber can receive them. Currently, `git pull` will fetch nothing because galactica never pushed the changes.</comment>

<file context>
@@ -0,0 +1,38 @@
+echo &quot;&quot;
+
+# Try Tailscale SSH first
+if tailscale ssh shunkakinoki@galactica &quot;cd ~/dotfiles &amp;&amp; make rekey-galactica&quot; 2&gt;/dev/null; then
+  echo &quot;✅ Rekey completed via Tailscale SSH&quot;
+else
</file context>
Fix with Cubic

Copilot AI Dec 13, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stderr redirection to /dev/null can hide important error messages. If the Tailscale SSH connection fails, the user won't see the actual error message that could help diagnose the issue. Consider either:

  1. Capturing and displaying the error message on failure
  2. Removing the redirection to let errors flow naturally to the user

The fallback error handling already provides instructions, but seeing the actual Tailscale error would be more helpful for debugging.

Suggested change
if tailscale ssh shunkakinoki@galactica "cd ~/dotfiles && make rekey-galactica" 2>/dev/null; then
if tailscale ssh shunkakinoki@galactica "cd ~/dotfiles && make rekey-galactica"; then

Copilot uses AI. Check for mistakes.
echo "✅ Rekey completed via Tailscale SSH"
else
echo "❌ Tailscale SSH failed. Please run this manually on galactica:"
echo ""
echo " cd ~/dotfiles"
echo " git pull"
echo " make rekey-galactica"
echo " git add named-hosts/galactica/keys/"
echo " git commit -m 'chore(agenix): rekey secrets for kyber access'"
echo " git push"
echo ""
exit 1
fi

# Pull the changes
echo ""
echo "📥 Pulling re-encrypted secrets..."
cd ~/dotfiles
git pull

echo ""
echo "✅ Done! Now run: make switch"
Comment on lines +1 to +38

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

git ls-files | grep -E "rekey-galactica|Makefile"

Repository: shunkakinoki/dotfiles

Length of output: 110


🏁 Script executed:

cat -n named-hosts/kyber/rekey-galactica.sh

Repository: shunkakinoki/dotfiles

Length of output: 1378


🏁 Script executed:

grep -A 20 "rekey-galactica" Makefile

Repository: shunkakinoki/dotfiles

Length of output: 975


🏁 Script executed:

grep -B 5 -A 15 "^rekey:" Makefile

Repository: shunkakinoki/dotfiles

Length of output: 743


Script safety and workflow mismatch: banner claims commit/push but remote command only re-encrypts

The banner (line 12) states the script will "Commit and push the changes", but the remote command (line 16) only runs make rekey-galactica, which executes agenix --rekey without committing or pushing. The manual fallback (lines 21–26) explicitly shows git add/commit/push steps, confirming these are not handled automatically. This messaging mismatch creates confusion about whether changes will be persisted to the repository.

Additionally, follow shellcheck-style safety defaults: use set -euo pipefail instead of set -e, remove stderr silencing (2>/dev/null) so failures are debuggable, and add prerequisite checks for tailscale and git.

-#!/usr/bin/env bash
+#!/usr/bin/env bash
 # Remotely rekey galactica secrets to include kyber's public key
 # This script should be run from kyber to trigger the rekey on galactica
 
-set -e
+set -euo pipefail
+
+command -v tailscale >/dev/null || { echo "tailscale is required"; exit 1; }
+command -v git >/dev/null || { echo "git is required"; exit 1; }
 
 echo "🔑 Rekeying galactica secrets to include kyber..."
 echo ""
 echo "This will:"
 echo "1. Connect to galactica via Tailscale SSH"
 echo "2. Run the rekey command to re-encrypt secrets with both keys"
-echo "3. Commit and push the changes"
+echo "3. (If needed) commit and push the changes from galactica"
 echo ""
 
 # Try Tailscale SSH first
-if tailscale ssh shunkakinoki@galactica "cd ~/dotfiles && make rekey-galactica" 2>/dev/null; then
+if tailscale ssh shunkakinoki@galactica "cd ~/dotfiles && make rekey-galactica"; then
   echo "✅ Rekey completed via Tailscale SSH"
 else
   echo "❌ Tailscale SSH failed. Please run this manually on galactica:"
🤖 Prompt for AI Agents
In named-hosts/kyber/rekey-galactica.sh around lines 1 to 38, the banner claims
it will "Commit and push the changes" but the remote command only runs make
rekey-galactica (which re-encrypts but does not git add/commit/push), and the
script uses unsafe shell settings and silences stderr; update the script to
either (A) make the banner accurate (remove commit/push claim) or (B) perform
the git add/commit/push on the remote after rekey (e.g., run a remote sequence
that runs make rekey-galactica && git add ... && git commit -m ... && git push),
remove the stderr redirection (do not use 2>/dev/null), replace set -e with set
-euo pipefail, and add prerequisite checks at the top to ensure tailscale and
git are available before attempting the SSH/pull steps.

34 changes: 20 additions & 14 deletions named-hosts/kyber/secrets.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,27 @@
# To add a new secret:
# 1. Add the secret definition here
# 2. Run: make encrypt-key-kyber KEY_FILE=/path/to/secret
let
# Galactica's SSH public key
galactica = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEKze2jlpV7SyTKA2ezqbumpCiDn+5Sj4z5SxrqfzesX shunkakinoki@gmail.com";
# Kyber's SSH public key
kyber = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO0IZtP3KSzY6GVSZ+R+VQYYfu3sEOVaQGDblQxAtwNM ubuntu@kyber";
# All machines that can decrypt shared secrets
allMachines = [
galactica
kyber
];
in
Comment on lines +5 to +15

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

This let block defining public keys is duplicated in named-hosts/galactica/secrets.nix. To improve maintainability and avoid having to update keys in multiple places, consider moving the public key definitions to a shared file.

For example, you could create a named-hosts/public-keys.nix file:

# named-hosts/public-keys.nix
{
  galactica = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEKze2jlpV7SyTKA2ezqbumpCiDn+5Sj4z5SxrqfzesX shunkakinoki@gmail.com";
  kyber = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIO0IZtP3KSzY6GVSZ+R+VQYYfu3sEOVaQGDblQxAtwNM ubuntu@kyber";
}

Then, you could import it in both secrets.nix files:

# named-hosts/kyber/secrets.nix
let
  keys = import ../public-keys.nix;
  allMachines = with keys; [ galactica kyber ];
in {
  ...
  "keys/tailscale-auth.age".publicKeys = [ keys.kyber ];
  "keys/id_ed25519.age".publicKeys = allMachines;
}

{
# 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..."
# ];
# };
"keys/tailscale-auth.age" = {
file = ./keys/tailscale-auth.age;

@cubic-dev-ai cubic-dev-ai Bot Dec 13, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1: This secret references a non-existent file ./keys/tailscale-auth.age. The keys/ directory doesn't exist under named-hosts/kyber/, which will cause Nix evaluation to fail. Either create the directory and encrypted file first, or keep this configuration commented out until the file exists.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At named-hosts/kyber/secrets.nix, line 16:

<comment>This secret references a non-existent file `./keys/tailscale-auth.age`. The `keys/` directory doesn&#39;t exist under `named-hosts/kyber/`, which will cause Nix evaluation to fail. Either create the directory and encrypted file first, or keep this configuration commented out until the file exists.</comment>

<file context>
@@ -2,21 +2,24 @@
-  #   ];
-  # };
+  &quot;keys/tailscale-auth.age&quot; = {
+    file = ./keys/tailscale-auth.age;
+    publicKeys = [ kyber ];
+  };
</file context>
Fix with Cubic

publicKeys = [ kyber ];
};

# SSH key sync (your local key encrypted for kyber)
# "keys/id_ed25519.age" = {
# file = ./keys/id_ed25519.age;
# publicKeys = [
# "ssh-ed25519 AAAA..."
# ];
# };
# Shared SSH key for GitHub authentication (synced from galactica)
"keys/id_ed25519.age" = {
file = ../galactica/keys/id_ed25519.age;
publicKeys = allMachines;
};
}
Loading