feat: use terminal Git credentials with a one-year memory cache
This commit is contained in:
@@ -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 |
|
| `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 |
|
| `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 |
|
| `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 |
|
| `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 |
|
| `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 |
|
| [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.
|
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
|
### Per-project DeepSeek Harness
|
||||||
|
|
||||||
[AGENT-VM.md](AGENT-VM.md) documents the project template and `nix run .#agent`.
|
[AGENT-VM.md](AGENT-VM.md) documents the project template and `nix run .#agent`.
|
||||||
|
|||||||
@@ -63,6 +63,10 @@
|
|||||||
in
|
in
|
||||||
{
|
{
|
||||||
desktop = import ./desktop-test.nix { inherit inputs pkgs; };
|
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 {
|
tools = import ./tools-test.nix {
|
||||||
inherit pkgs;
|
inherit pkgs;
|
||||||
config = inputs.self.nixosConfigurations.nixos.config;
|
config = inputs.self.nixosConfigurations.nixos.config;
|
||||||
|
|||||||
@@ -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"
|
||||||
|
''
|
||||||
@@ -235,7 +235,22 @@ in
|
|||||||
programs.git = {
|
programs.git = {
|
||||||
enable = true;
|
enable = true;
|
||||||
package = null; # The system module supplies Git.
|
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 = {
|
programs.delta = {
|
||||||
|
|||||||
Reference in New Issue
Block a user