gitcaskDocsGitHub

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.

ModeNeedsCaller
noneserver.listen on loopback; refused otherwiseEveryone is anon with write and admin
jwt[auth.jwt]: one of public_key or jwks_url, plus issuerThe token's sub and scopes
introspect[auth.introspect]: url and secret_envThe issuer's principal and scopes
forwardedA trusted proxy; GITCASK_FORWARD_SECRET is optional and must match when setX-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 startupOne identity per request: proxy or token, never both
gitcask.toml
[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 skew

Tokens 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.

git
Basic, password = $TOKEN
API client
Bearer $TOKEN
git sends the token in the Basic password; API clients send it as Authorization: Bearer.

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.audience is set.
nbf
Honoured when present.

Introspection

For opaque tokens, gitcask asks your issuer and caches the answer per instance.

Basic password or Bearer
POST url {"token":"…"}
active, principal, scopes, ttl
200 answer
{"active":true,"principal":"user:42","scopes":["acme/*:read"],"ttl":30}
Key or limitDefaultBound
cache_ttl30s≤ 10m; 0s disables positive caching
negative_cache_ttl3s0s disables negative caching
timeout2s> 0s and ≤ 10s
Cached answers10,000 per instance, FIFOKeyed by SHA-256, never raw tokens
Answer body64 KiBLarger 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-SecretAuthentication and precedence
AbsentIntrospect 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 wrongReturn 401; never retry with Authorization, even if it contains a valid token.
Present once and validCompare 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

SituationAnswer
Missing or invalid credentials401 with WWW-Authenticate: Basic realm="gitcask"
Token lacks a scope for the repository404
Forwarded principal missing or empty401
Forwarded request without X-Gitcask-Write: 1 or X-Gitcask-Admin: 1 it needs403
Introspection service failure503 with Retry-After: 5, no challenge
PathCredentials
/healthz, /readyzOpen
/docs, /openapi.json, /api/v1/docs, /api/v1/openapi.jsonOpen while server.public_docs = true (default); required when false
/metricsAlways required
Everything elseRequired

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