feat: use terminal Git credentials with a one-year memory cache

This commit is contained in:
OpenAI Coding Assistant
2026-09-06 13:01:07 -05:00
parent 2311330dbf
commit 9c32b3f877
4 changed files with 123 additions and 2 deletions
+23 -1
View File
@@ -20,7 +20,7 @@ Flat, explicit NixOS modules with locked inputs. Required setup belongs here—n
| `neovim.nix`, `neovim-test.lua` | Unmodified upstream editor deployment and opt-in native runtime audit |
| `updates.nix`, `update-system.sh`, `update-test.py` | Shared dev-owned checkouts, daily boot-staged updates and failure/concurrency regression tests |
| `switch-system.sh`, `switch-test.py` | Same installed manual apply/preview command on both hosts, with host identity supplied by Nix |
| `physical-test.nix`, `tools-test.nix` | Physical/AWS safety, shared-policy assertions and bounded offline tool/help smoke tests |
| `physical-test.nix`, `tools-test.nix`, `git-credentials-test.nix` | Physical/AWS safety, shared-policy assertions, offline tool/help smoke tests and disposable Git credential-cache checks |
| `desktop-test.nix`, `desktop-test.py`, `audit-desktop.sh` | Disposable graphical/PAM/audio/scaling audit |
| `workstation.nix`, `nvidia.nix` | Shared local hardware/SDDM integration for laptop and VM; separate opt-in NVIDIA support |
| [DESKTOP.md](DESKTOP.md) | Live audit, wallpaper provenance, JaKooLit comparison and explicit feature-completion plan |
@@ -91,6 +91,28 @@ See [DESKTOP.md](DESKTOP.md) for the screenshot-led audit, functional coverage,
These are system-owned executables from Nix, not unmanaged `npm -g`, `pip install --user` or `cargo install` bootstraps. Project dependencies may still be downloaded by their ordinary package managers. `nix develop` / `.envrc` remain appropriate for project-specific versions; this is not a promise that every language project uses the same global toolchain.
### Git credentials: terminal, not a GUI
Git HTTPS authentication uses terminal username/token prompts and Git's native
**in-memory cache with a 365-day timeout** (`31536000` seconds). Inherited helpers
are reset, and Git/SSH graphical askpass fallback is disabled for normal terminal
Git invocations. No credential-manager GUI or plaintext `credential-store` is used.
Credentials are scoped to the repository path as well as the host.
This is cache retention, **not a new token expiry**: rebooting, stopping the cache
daemon or rejecting a credential clears it, and the provider can expire/revoke a
token sooner. A successful re-approval refreshes its cache timeout. Choose a
one-year token expiry at your Git provider if it supports it; no real credentials
or provider settings are changed by this configuration. SSH keys/agents and
KeePassXC's storage for other applications remain unchanged. IDEs or repositories
that explicitly override Git helpers/askpass can override these user defaults.
To forget all cached Git HTTPS credentials immediately:
```sh
git credential-cache exit
```
### Per-project DeepSeek Harness
[AGENT-VM.md](AGENT-VM.md) documents the project template and `nix run .#agent`.
+4
View File
@@ -63,6 +63,10 @@
in
{
desktop = import ./desktop-test.nix { inherit inputs pkgs; };
git-credentials = import ./git-credentials-test.nix {
inherit pkgs;
config = inputs.self.nixosConfigurations.nixos.config;
};
tools = import ./tools-test.nix {
inherit pkgs;
config = inputs.self.nixosConfigurations.nixos.config;
+80
View File
@@ -0,0 +1,80 @@
# Offline protocol checks using dummy credentials in a disposable HOME only.
{ config, pkgs }:
let
hm = config.home-manager.users.dev;
in
assert
hm.programs.git.settings.credential.helper == [
""
"cache --timeout=31536000"
];
assert hm.programs.git.settings.credential.useHttpPath;
assert hm.programs.git.settings.core.askPass == "";
assert hm.home.sessionVariables.GIT_ASKPASS == "";
assert hm.home.sessionVariables.GIT_TERMINAL_PROMPT == "1";
pkgs.runCommand "git-terminal-credentials-check"
{
nativeBuildInputs = [
config.programs.git.package
pkgs.coreutils
pkgs.gnugrep
pkgs.expect
];
}
''
export HOME="$TMPDIR/home" XDG_CONFIG_HOME="$TMPDIR/home/.config" XDG_CACHE_HOME="$TMPDIR/cache"
export GIT_CONFIG_NOSYSTEM=1 GIT_ASKPASS="" GIT_TERMINAL_PROMPT=0
mkdir -p "$XDG_CONFIG_HOME/git"
cp ${hm.xdg.configFile."git/config".source} "$XDG_CONFIG_HOME/git/config"
test "$(git config --get core.askPass)" = ""
git config --get-all credential.helper | grep -qx 'cache --timeout=31536000'
trap 'git credential-cache exit' EXIT
printf 'protocol=https\nhost=git.example.invalid\npath=project.git\nusername=test\npassword=offline-test-token\n\n' |
git credential approve
printf 'protocol=https\nhost=git.example.invalid\npath=project.git\n\n' |
git credential fill > "$TMPDIR/retrieved"
grep -qx 'password=offline-test-token' "$TMPDIR/retrieved"
test -S "$XDG_CACHE_HOME/git/credential/socket"
test ! -e "$HOME/.git-credentials"
# Even with a GUI fallback in the environment, a cache miss must not invoke it.
printf '#!${pkgs.runtimeShell}\ntouch "$TMPDIR/gui-was-used"\necho unwanted\n' > "$TMPDIR/gui-askpass"
chmod +x "$TMPDIR/gui-askpass"
export SSH_ASKPASS="$TMPDIR/gui-askpass"
if printf 'protocol=https\nhost=git.example.invalid\npath=other.git\n\n' | git credential fill; then
echo 'Credentials leaked across repository paths' >&2; exit 1
fi
test ! -e "$TMPDIR/gui-was-used"
printf 'protocol=https\nhost=git.example.invalid\npath=project.git\n\n' | git credential reject
if printf 'protocol=https\nhost=git.example.invalid\npath=project.git\n\n' | git credential fill; then
echo 'Rejected credentials remained cached' >&2; exit 1
fi
# Exercise genuine /dev/tty entry too, without contacting a Git server.
export GIT_TERMINAL_PROMPT=1
expect <<'EXPECT'
set timeout 10
spawn -noecho git credential fill
send -- "protocol=https\rhost=terminal.example.invalid\rpath=project.git\r\r"
expect {
-exact "Username for 'https://terminal.example.invalid/project.git': " { send -- "terminal-user\r" }
timeout { exit 1 }
eof { exit 1 }
}
expect {
-exact "Password for 'https://terminal-user@terminal.example.invalid/project.git': " { send -- "offline-tty-token\r" }
timeout { exit 1 }
eof { exit 1 }
}
expect {
-exact "password=offline-tty-token" { }
timeout { exit 1 }
eof { exit 1 }
}
expect eof
lassign [wait] pid spawnid os_error status
exit $status
EXPECT
test ! -e "$TMPDIR/gui-was-used"
touch "$out"
''
+16 -1
View File
@@ -235,7 +235,22 @@ in
programs.git = {
enable = true;
package = null; # The system module supplies Git.
settings.user.useConfigOnly = true;
settings = {
user.useConfigOnly = true;
core.askPass = ""; # Use /dev/tty, never fall back to SSH's GUI askpass.
credential = {
# Reset inherited helpers; keep secrets in memory, never plaintext files.
helper = [
""
"cache --timeout=31536000"
]; # 365 days; cleared on reboot.
useHttpPath = true; # Don't reuse a repository token for unrelated paths.
};
};
};
home.sessionVariables = {
GIT_ASKPASS = "";
GIT_TERMINAL_PROMPT = "1";
};
programs.delta = {