Git workflows in Muppy¶
Read this first. This page is the shape of the thing; the pages that follow are the detail, and one of them is about your provider.
The problem Muppy is solving¶
Git providers do not universally let you mint a narrow token. GitLab has Deploy Tokens and fine-grained PATs; GitHub's fine-grained tokens are recent and uneven; Codeberg and Forgejo are narrower still. And even where narrow tokens exist, they are per-repository work: a developer touching five repositories would create five tokens before writing a line of code.
So the honest baseline is not "everyone has a perfectly scoped credential". It is: most people have one broad token, because that is what their provider makes easy.
Muppy is built for that reality rather than against it.
The answer: two branches¶
A credential you cannot narrow, you can still stop leaving lying around — and you can take the decision out of the machine that uses it. Those are the two branches, and they do different jobs.
1. Shrink the window¶
The Muppy git credential helper removes the standing credential from the box. Nothing is written to disk; the token is released for an operation and not held between them. And because the release is a decision rather than a file, its duration can follow how dangerous the credential is:
| Your token | How long an approval lasts |
|---|---|
| read-only | 8 hours — one prompt in the morning |
| can write | 15 minutes |
The wider the token, the shorter the window. That is the one lever that still works when narrowing the token is not available to you.
2. Decide somewhere else¶
The approval does not happen on the box. It happens in Muppy, in your browser, by you.
The box can only ask. It holds no token, and it holds no power to give itself one — so a compromised box cannot serve itself in silence. It can request, and requesting is visible: an attempt to use your credential arrives in front of you, as a prompt you did not start.
This is what turns the model from "the exposure is shorter" into "and you find out". The first branch limits what a breach costs; the second is how you learn it happened.
Everything downstream follows from it. Your git push pauses and shows you a link,
rather than succeeding silently. The prompt names the operation — push to acme/app —
so it is something you can evaluate rather than a box to tick. And there is a button
called "Not me — close the session", which exists for exactly one situation: a prompt
you cannot account for. That prompt is the signal; the button is the answer.
A login grants nothing
muppy-git-login says who this box acts as — your name on the commits. It
releases no credential, so logging in is not the moment anything is decided. Each git
operation asks separately.
Why approving a login asks for a code
The screen asks you to type the short code your muppy-git-login command printed. It
is four seconds of typing and it closes an attack the rest of the design cannot.
The command prints it on its own line, without the hyphen, so you can double-click to copy it rather than retype it:
Approve this login in Muppy:
https://<your-muppy>/mpy/git_provider/decide/XB6J-8TC7
Login Code: XB6J8TC7
Both forms are accepted, with or without the hyphen, upper or lower case. The hyphen in the link is there to make the code readable — but it is also what stops a double-click from selecting the whole thing, which is why the copyable line exists.
Anyone who knows your Muppy login can run muppy-git-login <your address> on their
own machine. Muppy then sends you the approval notification — because you are the
person whose identity is being asked for. Nothing is forged: it is a real notification,
about your name, from the system you trust. There is no dodgy link to spot.
What they cannot do is put a code on your terminal. Theirs printed one; yours printed nothing. So when the screen asks, you discover you have no code to give — and that is the whole signal.
It holds even when you do have a code, because your code belongs to your request: typing it on their screen approves nothing, and their attempt expires.
If that happens, press Not me — I did not start this. It refuses the login and records it as an incident rather than as a change of mind, so whoever runs your Muppy can look into it.
What the second branch does not cover, and how to change that
Detection covers what asks. A read served by a read-only, repository-scoped token asks nothing — that is the point of it — so a compromised box could read that one repository without anyone hearing about it.
That is a chosen limit, not an oversight: at worst it is a full read of a single repository, which we judge cheap enough to trade for the fluidity.
If you would rather not trade it, turn off Repo-scoped read-only tokens earn free reads in the settings. Every read then goes through an approval, and nothing at all is served without you seeing it.
OAuth is for talking to your provider, never for your box¶
You will be asked to Connect a Git Provider. That OAuth connection is used by Muppy to ask questions of your provider on your behalf: what are this token's real permissions, is it still alive, which of your tokens is this one.
It is never served to a box. It is an API capability, held only while it is being used and dropped once idle. Tokens are what boxes receive; OAuth is what Muppy uses to keep the truth about those tokens up to date.
That is why Introspect token matters: Muppy trusts what your provider says a token can do, not what you typed into a form.
Your provider decides how much of this Muppy can do for you¶
The two branches above are Muppy's. What Muppy can find out, and what it can cancel, is your provider's — and they differ enough to change what you have to do by hand. This is the one thing to know about yours before anything else:
| GitLab | Forgejo / Codeberg | GitHub | Azure DevOps | |
|---|---|---|---|---|
| Box served with no stored credential | yes | yes | yes | yes |
| Free reads reachable | yes, verified | yes, verified | yes, but declared — a fine-grained token's reach is never confirmed | no — no repository-scoped token exists |
| Muppy can tell a token is dead | yes | yes | yes, on github.com | no |
| Muppy can cancel an authorization | yes | no — you remove it | nothing to cancel — no connection | nothing to cancel — no connection |
| Owner-level access control | no | no | no | no |
| Estate-wide kill switch | no | no | no | no |
| Read-only policy satisfiable | yes | yes | fine-grained tokens only | never — see that page |
Two rows say no for everybody, and they are not oversights
Access is decided token by token, by each developer. There is no screen where the owner of a repository or an organisation can see what their team has granted Muppy, and no single switch that cuts it all off.
That is not a gap in Muppy — it is what these providers expose. A personal access token belongs to the person who made it; nothing in GitLab, GitHub or Forgejo lets a third party enumerate or revoke the tokens their members hold. So the containment Muppy offers is per-developer by construction: short windows, approvals taken away from the box, and credentials that are checked rather than trusted.
Plan around it. If you need to cut off a developer's access, you do it at the provider — remove them from the repository or the organisation — not in Muppy. And when somebody leaves, their tokens are theirs to revoke, or yours to invalidate by removing their account upstream.
---|---| | GitLab | Yes | Yes — classic PATs on their own, fine-grained ones through your connection | | Forgejo / Codeberg | No — you remove it, Muppy reminds you until you have | Yes — through your connection for a repo-limited token | | GitHub | Nothing to cancel — Muppy holds no connection on this provider | Partly — a classic token is corrected; a fine-grained one's reach is your word, permanently | | Azure DevOps | Nothing to cancel — Muppy holds no connection on this provider | No — you declare what the token is, and nothing corrects you |
Where Muppy cannot cancel, you end the authorization — and only when you meant to. If Muppy simply releases its access because the connection sat unused, it says so and asks nothing: your authorization at the provider was never affected. If you press Disconnect, that is you ending the relationship, and Muppy hands you the half it cannot do: the connection stays visible, marked Remove at provider, with a link, until you confirm it is done.
The two tokens to create¶
| Where | Token to create | Why |
|---|---|---|
| Dev boxes | one read-write, account-wide PAT per git host | Created once, covers every repository forever. This alone is a complete, supported setup — clone, pull and push all work. |
| Non-dev boxes (test, staging, production) | one read-only, repository-scoped token per repository | A deploy only reads, and there is no human on that box to approve anything — so the credential itself has to be the containment. |
That asymmetry is the design, not a gap. A dev box has someone in front of it who can answer a prompt; a production box does not, so what it holds must be harmless on its own.
A repo-scoped read-only token on a dev box is a bonus
Add one and reads on that repository stop asking anything at all — no session, no prompt. It is optional, never a prerequisite.
What a day looks like¶
On a dev box, once:
muppy-git-login <your-muppy-username> # approve in Muppy; names your identity
then work. git pull and git push pause the first time and ask you to authorize them;
you approve once for a window and carry on. muppy-git-status tells you what the box can
currently do; muppy-git-logout ends it.
On a non-dev box, nothing. The deploy uses the read-only token you registered, and nobody is prompted.
Where to go next¶
- Git Tokens (Dev & Non-Dev) — which token is served for what, and how Muppy resolves it. Start here after this page.
Then the one page for your provider — each covers the same three things: how that provider's workflow differs, the click-paths for creating tokens, and what Muppy can and cannot do for you there.
- Managing GitLab Tokens
- Managing Forgejo / Codeberg Tokens
- Managing GitHub Tokens
- Managing Azure DevOps Tokens
And, when you need them:
- Git Credential Settings — every tuning knob, with its default and what it controls.
- Read-only token policy — the one switch that makes the read-only requirement mandatory instead of advisory. Read it before turning it on.