Skip to content

feat(bin): ship the agent credential shims + lint ~/.local/bin - #13

Closed
natevick wants to merge 2 commits into
mainfrom
norm/agent-credential-shims
Closed

feat(bin): ship the agent credential shims + lint ~/.local/bin#13
natevick wants to merge 2 commits into
mainfrom
norm/agent-credential-shims

Conversation

@natevick

@natevick natevick commented Aug 1, 2026

Copy link
Copy Markdown
Owner

Follow-up to #12, which shipped a gitconfig pointing at a credential helper this repo did not contain — so a rebuilt agent host got config with nothing behind it. This adds the three shims.

shim what it does
gh injects the per-org PAT. Owner resolved from -R/--repo, GH_REPO, an gh api path, a positional owner/repo, or the cwd origin. No default-org fallback — a repo-less call runs unauthenticated rather than quietly carrying an org credential.
git refuses force-push and branch-delete, audits, notifies.
git-credential-github-org one PAT per org, keyed on the remote URL owner.

The git guard is an accident catcher, not a control. Anything running as the agent can call /usr/bin/git, use the API, or set NORM_ALLOW_FORCE=1 (which is itself audited and notified). It exists because the broker's hard-deny on force-push does not reach the ambient PAT path. It fails open to real git by design — breaking every git call would be worse than missing one force-push. Non-push subcommands are untouched, so clean -fd / checkout -f / tag -f / branch -d still work.

  • Gated in .chezmoiignore on .agent: gh and git shadow real binaries, so on a host with no ~/.config/github-pats they would break github.com auth outright. Both renderings verified with --override-data.
  • Audit log is created 0600 before first write; the ambient umask would otherwise leave it world-readable. The token value is never logged.
  • home/dot_local/bin/* is now covered by the shellcheck job — these shadow real binaries and were outside the linter entirely. All four files pass clean (shellcheck 0.11.0).

Verified on the agent host: 51 behavioural tests across the three shims incl. sabotage passes, a real blocked force-push (nothing reached the remote), and a real push+delete cycle on a throwaway branch.

Norm added 2 commits August 1, 2026 08:14
Agent accounts now hold one fine-grained PAT per GitHub org under
~/.config/github-pats/<owner>, selected by lowercasing the owner from the remote
URL. Scope is enforced server-side, so the tokens are narrow enough to be
ambient — a routine fetch or `gh pr view` no longer costs an approval
round-trip.

Guarded on .agent: on a machine without the helper installed this block would
break github.com auth outright, so it must never render on the Mac. Verified
both ways with `chezmoi execute-template --override-data`.

Two details that are load-bearing and easy to lose:
- `helper =` resets the list first. For multi-valued keys git APPENDS, so
  without the reset the `!gh auth git-credential` helper above still gets
  consulted first.
- useHttpPath=true, or git omits `path=` from the credential request and the
  helper has no owner to key a token on.
PR #12 added a gitconfig pointing at a credential helper the repo did not
contain, so a rebuilt agent host got config with nothing behind it. Ship the
three shims it depends on:

  gh                        injects the per-org PAT, resolving owner from
                            -R/--repo, GH_REPO, an `gh api` path, a POSITIONAL
                            owner/repo, or the cwd's origin. No default-org
                            fallback: a repo-less call runs unauthenticated
                            rather than quietly carrying an org credential.
  git                       refuses force-push and branch-delete, audits, and
                            notifies. An ACCIDENT CATCHER, not a control —
                            anything running as the agent can call /usr/bin/git
                            or set NORM_ALLOW_FORCE=1. It fails OPEN to real git
                            by design; breaking every git call would be worse
                            than missing one force-push.
  git-credential-github-org one PAT per org, keyed on the remote URL's owner.

All three append to ~/.local/state/norm-gh-audit.jsonl (0600, created before
first write — the ambient umask would otherwise leave it world-readable). The
token value is never logged.

Gated in .chezmoiignore on .agent: `gh` and `git` SHADOW the real binaries, so
on a host without ~/.config/github-pats they would break github.com auth
outright. Verified both renderings with --override-data.

Also wire home/dot_local/bin/* into the shellcheck job. Those files shadow real
binaries and were outside the linter's reach; all four pass clean (0.11.0).
@natevick

natevick commented Aug 1, 2026

Copy link
Copy Markdown
Owner Author

Superseded by #14 — this branch had a stale merge base after #12 was squash-merged, so it re-added the already-landed gitconfig block. #14 is cherry-picked onto current main and contains only the shims.

@natevick natevick closed this Aug 1, 2026
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