Adds an opt-in per-agent Gitea identity so a fleet agent can push/commit/open
PRs under its own token instead of the single shared account, giving
cryptographic author-!=-reviewer separation (Gate-16).
Resolution priority (both tools): MOSAIC_GIT_IDENTITY env > git config
mosaic.gitIdentity (per-worktree, persists on disk) > git-supplied username
(git-credential-mosaic only). If the resolved identity has a token file at
~/.config/mosaic/secrets/gitea-tokens/gitea-{usc,mosaicstack}-<id>.token, that
identity is used; otherwise both tools fall through to the existing
shared-account path unchanged, so this is a no-op on any host without
per-slot tokens configured.
- tools/git/git-credential-mosaic: new git credential helper (get verb).
- tools/git/detect-platform.sh: get_gitea_token() gains the same identity
resolution, prepended ahead of the existing shared-token logic, so API
tooling (pr-create.sh, issue-create.sh, ...) authors under the same
identity as git push/fetch.
- install.sh: explicit chmod +x for git-credential-mosaic (it ships without
a .sh suffix, so the existing *.sh glob does not cover it); tools/** is
already framework-owned so the file syncs automatically.
- tools/git/README.md: documents the feature, the one-time
`git config credential.helper` registration step (deliberately not
auto-wired — see README for why), and the PowerShell-parity decision
(detect-platform.ps1 authenticates via tea logins, not a raw-token
function, so there is nothing to port there).
- tools/git/test-git-credential-mosaic.sh,
tools/git/test-gitea-token-identity.sh: new regression harnesses (red
verified against the pre-patch code) covering identity-resolution
priority, per-host token path selection, and shared-account fallback.
Wired into package.json's test:framework-shell.
Upstreams Mos host-local tooling-patch kit (2026-07-23), Patches 1+2.
Closes #873
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
70 lines
3.1 KiB
Bash
70 lines
3.1 KiB
Bash
#!/bin/bash
|
|
# git-credential-mosaic — git credential helper — resolves Gitea tokens from
|
|
# the Mosaic credential store at runtime so remote URLs never embed secrets.
|
|
#
|
|
# Install (one-time, per clone or globally):
|
|
# git config credential.helper "$HOME/.config/mosaic/tools/git/git-credential-mosaic"
|
|
# # or, fleet-wide: git config --global credential.helper "$HOME/.config/mosaic/tools/git/git-credential-mosaic"
|
|
#
|
|
# Per-agent Gate-16 identity (author != reviewer separation):
|
|
# git config mosaic.gitIdentity <agent-id> # per-worktree, persists on disk
|
|
# # or: export MOSAIC_GIT_IDENTITY=<agent-id>
|
|
#
|
|
# Resolution priority: MOSAIC_GIT_IDENTITY env > git config mosaic.gitIdentity
|
|
# (per-worktree, survives across non-persistent shells) > git-supplied username
|
|
# (credential.username / URL). When the resolved identity has a matching
|
|
# per-agent token file, use it instead of the shared account. Backward
|
|
# compatible: nothing resolvable -> shared token (unchanged behavior).
|
|
[ "$1" = "get" ] || exit 0
|
|
host=""; username_in=""
|
|
while IFS= read -r line; do
|
|
[ -z "$line" ] && break
|
|
case "$line" in
|
|
host=*) host=${line#host=};;
|
|
username=*) username_in=${line#username=};;
|
|
esac
|
|
done
|
|
# Per-agent identity resolution (Gate-16 author≠reviewer separation).
|
|
# Priority: MOSAIC_GIT_IDENTITY env > git config mosaic.gitIdentity (per-worktree,
|
|
# survives across non-persistent shells) > git-supplied username (credential.username
|
|
# / URL). When the resolved identity has a matching per-agent token, use it instead of
|
|
# the shared account. Backward-compatible: nothing resolvable → shared token.
|
|
ident="$MOSAIC_GIT_IDENTITY"
|
|
[ -z "$ident" ] && ident=$(git config --get mosaic.gitIdentity 2>/dev/null)
|
|
[ -z "$ident" ] && ident="$username_in"
|
|
if [ -n "$ident" ]; then
|
|
case "$host" in
|
|
git.uscllc.com) idpfx=gitea-usc;;
|
|
git.mosaicstack.dev) idpfx=gitea-mosaicstack;;
|
|
*) idpfx="";;
|
|
esac
|
|
if [ -n "$idpfx" ]; then
|
|
idtok="$HOME/.config/mosaic/secrets/gitea-tokens/${idpfx}-${ident}.token"
|
|
if [ -r "$idtok" ]; then
|
|
echo "username=${ident}"
|
|
echo "password=$(cat "$idtok")"
|
|
exit 0
|
|
fi
|
|
fi
|
|
fi
|
|
case "$host" in
|
|
git.uscllc.com) svc=gitea-usc;;
|
|
git.mosaicstack.dev) svc=gitea-mosaicstack;;
|
|
*) exit 0;;
|
|
esac
|
|
# Script-relative (not $HOME-absolute) so this resolves correctly regardless
|
|
# of where the framework installer places tools/ under $HOME — mirrors
|
|
# detect-platform.sh's own cred_loader resolution in this same directory.
|
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
# shellcheck source=../_lib/credentials.sh
|
|
source "$script_dir/../_lib/credentials.sh"
|
|
load_credentials "$svc" >/dev/null 2>&1 || exit 0
|
|
# GITEA_USER is not populated by load_credentials (it only exports
|
|
# GITEA_URL/GITEA_TOKEN for gitea-*), so this fallback is normally taken. Gitea's
|
|
# git-over-HTTP auth authenticates from the token itself (the password field),
|
|
# not from the username string, so any non-empty placeholder works here — this
|
|
# is deliberately NOT a real account name (framework files must stay
|
|
# operator-agnostic; see tools/quality/scripts/verify-sanitized.sh).
|
|
echo "username=${GITEA_USER:-git}"
|
|
echo "password=$GITEA_TOKEN"
|