Skip to content

feat: automatic git auth and signing for sprout agents#528

Merged
tlongwell-block merged 2 commits into
mainfrom
feat/multicall-git-auth-signing
May 11, 2026
Merged

feat: automatic git auth and signing for sprout agents#528
tlongwell-block merged 2 commits into
mainfrom
feat/multicall-git-auth-signing

Conversation

@tlongwell-block

Copy link
Copy Markdown
Collaborator

Summary

Integrate git-credential-nostr and git-sign-nostr into sprout-dev-mcp as multicall personalities. Agents using sprout-dev-mcp get automatic NIP-98 git auth and NIP-GS commit signing when NOSTR_PRIVATE_KEY is set — zero persistent config mutation.

How it works

sprout-dev-mcp (single binary, sync main)

argv[0] dispatch (before tokio, before tracing):
  rg / tree / git-credential-nostr / git-sign-nostr

async (tokio runtime built here):
  sprout (CLI) / default (MCP server)

Shim dir (0700 tempdir, prepended to PATH):
  symlinks → self for all personalities
  .nostr-key (0600, OpenOptions::mode at creation) — private key

At startup, the dev-mcp:

  1. Reads NOSTR_PRIVATE_KEY, then remove_var() unconditionally
  2. Writes key to 0600 file (created with create_new + mode(0o600))
  3. Derives the public key for user.signingkey
  4. Zeroizes the in-memory key string
  5. Builds ephemeral GIT_CONFIG_* env vars for shell children

Shell children get automatic nostr credential auth + commit signing. The credential helper is additive — it silently declines non-Sprout remotes (no Nostr WWW-Authenticate challenge = exit 0, no credential), so git falls through to system helpers for GitHub/GitLab/etc.

What agents can do

  • git push to Sprout relays — automatic NIP-98 auth
  • git commit — automatic NIP-GS signing
  • git clone git@github.com:... — SSH works (SSH_AUTH_SOCK passed through)
  • git push to GitHub/GitLab — system credential helpers still work
  • sprout channels list — relay CLI works (SPROUT_PRIVATE_KEY inherited)

Security model

  • NOSTR_PRIVATE_KEY removed from process env after keyfile write
  • Keyfile is 0600 inside 0700 tempdir, cleaned up on drop
  • Shell children cannot read the key from env
  • Git helpers read from keyfile via nostr.keyfile git config
  • SPROUT_PRIVATE_KEY intentionally inherited (sprout CLI needs it)

Changes

  • git-credential-nostr: extract lib.rs with pub fn run() -> i32, remove unused anyhow dep
  • git-sign-nostr: extract lib.rs with pub fn run() -> i32
  • sprout-dev-mcp: multicall dispatch (sync, before tokio/tracing), shim symlinks, keyfile, ephemeral git config, secret scoping
  • sprout-agent: widen PASSTHROUGH_ENV (SSH_AUTH_SOCK, GIT_ASKPASS, GIT_SSH_COMMAND, SPROUT_PRIVATE_KEY, SPROUT_RELAY_URL)
  • scripts/build-agent-release.sh: tar packaging with cross-compile support

Standalone binaries for both git helpers continue to work unchanged (lib + bin pattern).

Release artifact

./scripts/build-agent-release.sh 0.1.0
# → dist/sprout-agent-v0.1.0-aarch64-apple-darwin.tar.gz (9.7 MB)
#   Contains: sprout-agent (8.8 MB) + sprout-dev-mcp (14 MB)

Testing

  • 144 tests pass across all affected packages
  • Existing integration tests for git-credential-nostr and git-sign-nostr pass
  • Pre-existing flaky test cancel_kills_inflight_tool_via_mcp_notification unrelated

@tlongwell-block tlongwell-block force-pushed the feat/multicall-git-auth-signing branch 2 times, most recently from 94e2d7c to 08f8c80 Compare May 11, 2026 13:49
Integrate git-credential-nostr and git-sign-nostr into sprout-dev-mcp as
multicall personalities. Agents using sprout-dev-mcp get automatic NIP-98
git auth and NIP-GS commit signing when NOSTR_PRIVATE_KEY is set — zero
persistent config mutation.

## How it works

sprout-dev-mcp is already a multicall binary (rg, tree, sprout CLI via
argv[0] dispatch). This adds git-credential-nostr and git-sign-nostr as
two more personalities, with a shim directory that puts them on PATH.

At startup, the dev-mcp:
1. Reads NOSTR_PRIVATE_KEY, then removes it from the process env
2. Writes the key to a 0600 file (created with O_CREAT|O_EXCL + mode)
3. Derives the public key for user.signingkey
4. Zeroizes the in-memory key string
5. Builds ephemeral GIT_CONFIG_* env vars for shell children

Shell children get automatic nostr credential auth + commit signing via
the ephemeral git config. The credential helper is additive — it silently
declines non-Sprout remotes (no Nostr WWW-Authenticate challenge = exit 0,
no credential), so git falls through to system helpers for GitHub/GitLab.

## Changes

- git-credential-nostr: extract lib.rs with `pub fn run() -> i32`
- git-sign-nostr: extract lib.rs with `pub fn run() -> i32`
- sprout-dev-mcp: multicall dispatch (sync, before tokio/tracing),
  shim symlinks, keyfile, ephemeral git config, secret scoping
- sprout-agent: widen PASSTHROUGH_ENV (SSH_AUTH_SOCK, GIT_ASKPASS,
  GIT_SSH_COMMAND, SPROUT_PRIVATE_KEY, SPROUT_RELAY_URL, etc.)
- scripts/build-agent-release.sh: tar packaging with cross-compile support
- Remove unused anyhow dep from git-credential-nostr

Standalone binaries for both git helpers continue to work unchanged.
@tlongwell-block tlongwell-block force-pushed the feat/multicall-git-auth-signing branch from 08f8c80 to a7963ad Compare May 11, 2026 13:58
Ensures the agent + dev-mcp (with git-credential-nostr and git-sign-nostr
multicall integration) build successfully for linux-musl targets on every PR.

Also adds git-sign-nostr to the cross-compile list (previously only
git-credential-nostr was included).
@tlongwell-block tlongwell-block merged commit 70a6915 into main May 11, 2026
15 checks passed
@tlongwell-block tlongwell-block deleted the feat/multicall-git-auth-signing branch May 11, 2026 14:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant