Authentication
gitcask verifies a credential on every request and checks repository scopes; issuing tokens and storing users stay with your platform.
Five modes
Pick one with server.auth_mode. Startup fails closed when a mode's settings are missing.
| Mode | Needs | Caller |
|---|---|---|
none | server.listen on loopback; refused otherwise | Everyone is anon with write and admin |
jwt | [auth.jwt]: one of public_key or jwks_url, plus issuer | The token's sub and scopes |
introspect | [auth.introspect]: url and secret_env | The issuer's principal and scopes |
forwarded | A trusted proxy; GITCASK_FORWARD_SECRET is optional and must match when set | X-Gitcask-Principal, with X-Gitcask-Write: 1 and X-Gitcask-Admin: 1 as grants |
introspect_forwarded | [auth.introspect] and GITCASK_FORWARD_SECRET, both required at startup | One identity per request: proxy or token, never both |
[server]
auth_mode = "jwt"
[auth.jwt]
public_key = "/etc/gitcask/public.pem" # or jwks_url = "https://issuer.example/.well-known/jwks.json"
issuer = "https://issuer.example" # exact iss; required in jwt mode
# audience = "gitcask" # optional exact member of aud
leeway = "60s" # exp/iat/nbf clock skewTokens and scopes
Git sends the token as the Basic-auth password and ignores the username. The API takes the same token as a Bearer header.
JWTs are EdDSA (Ed25519) only. gitcask holds the public key; the issuer keeps the private key.
- sub
- Opaque principal. gitcask stores nothing else about the caller.
- scopes
<owner>/<repo>:read|write|admin.*is allowed only in the repository segment; admin implies write implies read.- exp, iat, jti
- Required.
- iss
- Must equal
auth.jwt.issuer. - aud
- Checked when
auth.jwt.audienceis set. - nbf
- Honoured when present.
Introspection
For opaque tokens, gitcask asks your issuer and caches the answer per instance.
{"active":true,"principal":"user:42","scopes":["acme/*:read"],"ttl":30}| Key or limit | Default | Bound |
|---|---|---|
cache_ttl | 30s | ≤ 10m; 0s disables positive caching |
negative_cache_ttl | 3s | 0s disables negative caching |
timeout | 2s | > 0s and ≤ 10s |
| Cached answers | 10,000 per instance, FIFO | Keyed by SHA-256, never raw tokens |
| Answer body | 64 KiB | Larger answers are a service failure: 503 |
A positive answer lives min(ttl, cache_ttl), which is also the longest a revocation can take to reach an instance.
Trusted proxies
In introspect_forwarded, the X-Gitcask-Forward-Secret header decides who the caller is.
| X-Gitcask-Forward-Secret | Authentication and precedence |
|---|---|
| Absent | Introspect the Basic password or Bearer token using the existing bounded client/cache. Ignore all forwarded identity/write/admin headers. |
| Present but empty, malformed, repeated, or wrong | Return 401; never retry with Authorization, even if it contains a valid token. |
| Present once and valid | Compare in constant time, then require a non-empty X-Gitcask-Principal. Use only forwarded identity/grants; ignore Authorization, even if valid, invalid or more privileged. A missing/empty principal returns 401. |
Errors and open paths
| Situation | Answer |
|---|---|
| Missing or invalid credentials | 401 with WWW-Authenticate: Basic realm="gitcask" |
| Token lacks a scope for the repository | 404 |
| Forwarded principal missing or empty | 401 |
Forwarded request without X-Gitcask-Write: 1 or X-Gitcask-Admin: 1 it needs | 403 |
| Introspection service failure | 503 with Retry-After: 5, no challenge |
| Path | Credentials |
|---|---|
/healthz, /readyz | Open |
/docs, /openapi.json, /api/v1/docs, /api/v1/openapi.json | Open while server.public_docs = true (default); required when false |
/metrics | Always required |
| Everything else | Required |
Keys for self-hosters
Without an issuer, generate a key pair and mint tokens offline. gitcask has no endpoint that issues tokens.
# once: an Ed25519 key pair (never overwrites existing files)
gitcask --config gitcask.toml token keygen \
--private-key gitcask-private.pem --public-key gitcask-public.pem
# per token: issuer and audience come from [auth.jwt] in the config
gitcask --config gitcask.toml token mint \
--key gitcask-private.pem --principal ci --scope acme/web:write --ttl 1h