Consuming approved skills¶
The facade is an ordinary read-only git remote, so clients need no modification — only a credential and a URL.
The wizard does steps 1–4 for you
Every section of a marketplace's page in the portal offers Connect a
client in its header, which composes the exact commands below for that marketplace — token
creation (show-once, as always), the credential line, the claude plugin
marketplace add command, and the clone URL. The rest of this guide is the
same procedure by hand. If the header says the marketplace is not
served, the commands are correct but will be answered 404 until a
snapshot is approved. See
the portal reference.
1. Get a credential¶
Git clients authenticate with a token, not with your portal session cookie. There are two kinds, and for a human at a keyboard the first is the better default.
If your gateway has identity-provider bearer tokens switched on, there is a third option in which you mint nothing at all — skip to it.
A session credential (recommended for people)¶
If you are already logged in to the portal, mint a credential from that session instead of creating a standing token:
$ curl -X POST localhost:8080/api/v1/tokens/session \
-H 'Content-Type: application/json' -d '{"name":"my-laptop"}'
{"id":1,"name":"my-laptop","token":"sgw_...","expiresAt":"2026-08-23T17:00:00Z",
"sessionDerived":true,"pushScopes":[]}
The gateway sets the lifetime —
skills-gateway.tokens.session-ttl,
8 hours by default — and you cannot ask for longer. That is the entire
difference between this and a personal access token: a credential whose life
the holder chooses is a standing credential wearing a different name. It also
carries no publication authority, and it is marked sessionDerived wherever it
appears, so the audit ledger distinguishes
"a fetch by a credential minted moments after an SSO login" from "a fetch by a
token provisioned months ago".
The timer is the control, not your session
A session credential is not revoked when you log out or close the browser — the gateway does not track session lifetime. It dies when its expiry passes, and until then it is a bearer token like any other. What it buys is a bounded window and an attributable origin, not immunity.
A personal access token (for CI, and anything without a browser)¶
Git clients authenticate with PATs, not with your portal session.
Shown exactly once
Only a SHA-256 digest is stored. A lost token cannot be recovered — revoke it and create another.
Tokens are scoped to the creating principal: you only ever see and revoke your own.
Scope it, expire it, rotate it
A token can be limited to named marketplaces ("scopes":["acme","catalog"])
and given an expiry ("expiresAt":"...") — a leaked CI token limited to one
marketplace is an incident contained to that marketplace, and one that dies
on its own bounds the window a leak matters. Suspect exposure without
wanting to reconfigure anything? POST /api/v1/tokens/{id}/rotate issues a
fresh secret with the identical grant and kills the old one in the same
act. See Access tokens.
No credential at all: SSO in the credential helper¶
If the gateway sets
skills-gateway.facade.idp-bearer.enabled,
a git client that speaks generic OAuth can authenticate straight against your
organisation's SSO. Nothing is minted, nothing is stored on the gateway, and the
fetch is still attributed to you — the gateway takes your identity from the same
claim the portal does.
Git Credential Manager
is configured entirely through git config. Four settings, all keyed on the
gateway's URL:
$ git config --global credential.https://skills.corp.example.oauthClientId corp-skills-gateway
$ git config --global credential.https://skills.corp.example.oauthAuthorizeEndpoint https://idp.corp.example/oauth2/v2.0/authorize
$ git config --global credential.https://skills.corp.example.oauthTokenEndpoint https://idp.corp.example/oauth2/v2.0/token
$ git config --global credential.https://skills.corp.example.oauthScopes openid
Then clone with no credential in the URL at all:
The first request is answered 401, the helper opens a browser, you complete
the usual SSO, and the token it receives is sent as
Authorization: Bearer … on the retry.
The repository's end-to-end suite runs a mock OIDC provider on
localhost:9090 and the gateway on localhost:18081. Against that pair, with
the gateway started with SKILLSGATEWAY_FACADE_IDPBEARER_ENABLED=true and
SKILLSGATEWAY_OIDC_ISSUER=http://localhost:9090/default:
$ git config --global credential.http://localhost:18081.oauthClientId e2e-client
$ git config --global credential.http://localhost:18081.oauthAuthorizeEndpoint http://localhost:9090/default/authorize
$ git config --global credential.http://localhost:18081.oauthTokenEndpoint http://localhost:9090/default/token
$ git config --global credential.http://localhost:18081.oauthScopes openid
$ git clone http://localhost:18081/git/acme
The values are real — they are the ones
e2e/run-e2e.sh
exports — but the credential-helper half of this is not automated: the
suite drives a browser, not a credential manager. What the suite does verify
end to end is the gateway's half, with a real git clone carrying a real
bearer token in a header.
Any client that can set a header works, which is also how to test the gateway's side directly:
$ git -c http.extraHeader="Authorization: Bearer ${SGW_ACCESS_TOKEN}" \
clone https://skills.corp.example/git/acme
Do not put this in a shell history or a CI log — it is a bearer credential like any other.
Short-lived, and the gateway cannot revoke it
A bearer token dies when the identity provider says so, usually within the hour, and that is the whole control: the gateway has no kill switch for one. If you need a credential you can revoke centrally — a CI pipeline's, most of all — use a personal access token. That is why both exist.
Your marketplace scopes
A bearer token has no scope list, so it reaches every marketplace the gateway
serves — the same as a personal access token created without scopes. If you
need a credential limited to one marketplace, create a scoped token.
2. Verify the remote¶
The username is ignored; the token goes in the password field.
A 404 here means the marketplace has never had a snapshot approved — there is nothing to serve. A 401 means the token is wrong, expired or revoked. The portal says the same thing on the marketplace's own page when nothing is being served yet, so the two are not confused for each other.
3. Store the credential¶
So the client never prompts:
$ printf 'protocol=https\nhost=skills.corp.example\nusername=token\npassword=sgw_...\n' \
| git credential approve
Or configure the username and let your credential helper hold the secret:
4. Point your agent at it¶
One URL for everything
Instead of adding each marketplace separately, add the
virtual catalog — /git/catalog — which aggregates
the currently approved snapshot of every governed marketplace, with plugin
names prefixed by their marketplace.
Point the tool at the same URL as an ordinary skills repository — the facade serves plain git, and the open Agent Skills format needs nothing more.
What clients see¶
Exactly one branch, main, at the approved SHA. Unapproved snapshots do not
exist on this remote, and pushing here is impossible — receive-pack is disabled
by construction on the facade, so git push gets a "service not enabled"
rejection. (Publishing to a marketplace the gateway hosts is a different
endpoint and a different credential; see
Publishing first-party skills.)
The served SHA changes only when a reviewer approves a new snapshot. Upstream movement alone never changes what you receive.
Every fetch is recorded¶
Each git fetch appends entries to the audit ledger with your principal, the
marketplace, the ref and the SHA. Check the portal's Audit log page.
This is what makes "which identities ever received this exact content" answerable — see Snapshots and the audit ledger.
Making the gateway the only door¶
Governance that relies on developers choosing the right URL is documentation, not control. Two mechanisms carry the load:
Fleet-managed client settings. Claude Code's managed settings support
strictKnownMarketplaces (managed-only), which restricts users to an explicit
marketplace allowlist. Combined with extraKnownMarketplaces and
enabledPlugins you can pre-register the gateway and force-install the approved
set. Distribute via MDM. Copilot and Cursor have no equivalent hard switch
today.
Network egress policy. Block — or at minimum alert on — direct access from developer machines and CI to upstream marketplace hosts. Blocked attempts are themselves a useful signal.
And the carrot. The gateway is faster (LAN-local), simpler (one URL, a pre-approved catalog, no security tickets) and works in restricted networks. Making the paved road genuinely better is half of enforcement.