~bigbes/sr-ht-dolt

ref: ecfc6bb21417a3997754a5399a2cd2a73a542037 sr-ht-dolt/authn d---------
57adcc0a — Eugene Blikh 5 days ago
doltsrht: serve /mcp on the web listener
0f6d50a6 — Eugene Blikh 5 days ago
authn: gofmt the test files
6288fc1e — Eugene Blikh 5 days ago
authn: accept tokens.sr.ht working tokens and PATs as bearer

The /mcp surface is bearer-only, and none of the three credential planes
this service has fits an agent. ResolveBearer adds the fourth: it decodes
the presented token once, locally, and routes on the ClientID — the only
thing that tells a tokens.sr.ht working token from a meta.sr.ht PAT, since
both are sealed with the same instance key.

A working token goes through sr-ht-ecore's validator (one copy of that
check for the whole instance) and carries its grants out on the result, so
the surface can ask Authorize where the action is known. A PAT reuses
ResolveBasic with the token's own username as the identity — there is no
presented username to compare against in a bearer header — and the same
TokenGrantsAllow read gate the clone path applies.

An instance with no [tokens.sr.ht] section passes a nil validator: meta
PATs and anonymity keep working, a working token is refused, because a
machine credential this instance cannot verify is refused rather than
guessed at. A failed credential is always a refusal and never a downgrade
to anonymous, and backend.go's two error classes are preserved so a caller
can still answer 401 against 503.

core.GrantRead is the vocabulary tokens.sr.ht deliberately does not know.
There is no dolt:write: nothing on that surface writes, and a grant nobody
checks is a promise to an operator that no code keeps.
e04928c9 — Eugene Blikh 9 days ago
login: take the unified-login cookie decode from ecore

authn's CookieName, the fernet decrypt, the auth.AuthCookie unmarshal and the
empty-name check were one of six copies of the same decode on this instance.
They are now sr-ht-ecore/login.UsernameFromRequest; what stays here is the half
that is ours, turning that name into a row in our user table.

Two things the local copy did not do. It passed the cookie's name through with
a leading '~' still on it, which meta answers for nobody, and it validated
nothing at all — a name went from an attacker-supplied cookie straight into a
GraphQL query and a log line. Both are now login's, and the middleware's own
rule is unchanged: every failure is anonymity, so public browsing and public
clones keep working.
27823bb6 — Eugene Blikh 9 days ago
log: replace logrus with slog behind auxilia's scribe handler

Every logger field this service owned was a *logrus.Entry threaded
through a constructor, which is what logrus costs for want of a usable
default. They are slog.Default().With("component", ...) now, and the
threading is gone with them; the shared middleware's panic reports land
in the same handler, which is why the daemon sets the default before
anything that can fail.

The handler is scribe's tint handler: level from [dolt.sr.ht]log-level,
source positions, and masks keyed on the attribute path for the three
credentials this service handles — the unified-login cookie, the
Internal fernet token and the Authorization header the remotesapi reads
a PAT or a keypair JWT out of. Errors go through scribe.Err, so a culpa
error's hint reaches the operator on its own line.

logrus stays in go.mod: dolt's remotesrv.ServerArgs takes a
*logrus.Entry and nothing else. It is now confined to Config.DoltLogger,
which is the only place this service names it.

dolt-git-hook is deliberately untouched: what it writes to stderr is the
notice a pushing user reads through git, not a log.
84c33df2 — Eugene Blikh 9 days ago
test: build the fixture config and the keyset with ecoretest

The hand-built ini in web_test.go, the random fernet key in authn's
TestMain and the same seeding copied into the git-hook test are one call
to ecoretest now. The keys are fixed rather than generated on purpose:
they secure nothing inside a test process, and a constant keyset is what
lets two packages of this service initialise without the second rotating
what the first sealed with.

The synthetic instance runs in production mode, so the environment
banner is off in tests unless one asks for it.
2dfab043 — Eugene Blikh 30 days ago
rename module to sourcecraft.dev/bigbes/sr-ht-dolt; depend on sourcecraft sr-ht-core
2b781a27 — Eugene Blikh 30 days ago
remoteapi: authz interceptors, remotesrv assembly, credentials service
5e555bac — Eugene Blikh 30 days ago
authn: cookie, PAT, and dolt-JWT caller resolution

Add the authn package resolving the SourceHut caller across dolt.sr.ht's
three auth flows, producing core-go *auth.AuthContext values mapped onto the
pure core.Caller domain type:

- ctx.go: WithCaller/CallerFromContext (nil for anonymous, never panics) and
  AsCoreCaller (maps UserType, derives Suspended).
- cookie.go: OptionalCookieMiddleware, the never-rejecting unified-login
  cookie reader (fernet decrypt -> {name} -> LookupUser); any failure degrades
  to anonymous so public browsing keeps working.
- token.go: ResolveBasic, the meta personal-access-token trio (offline
  DecodeBearerToken -> username match -> LookupUser + revocation) with a 60s
  positive cache keyed by sha512(password); TokenGrantsAllow gates
  dolt.sr.ht/repos RO/RW grants (empty grants pass).
- jwt.go: ResolveDoltJWT, EdDSA JWS verification for dolt keypair auth (kid ->
  KeyStore pubkey, alg=EdDSA, aud/exp/sub checked, iss ignored), reusing
  dolt's creds.PubKeyToKIDStr for the kid<->pubkey integrity check.
- grpc.go: ResolveGRPCAuth dispatching Basic/Bearer/anonymous.

Meta lookup + revocation sit behind the MetaBackend interface and JWT keys
behind the KeyStore interface (implemented later by db/), so tests forge
cookies (fernet), PATs (BearerToken.Encode) and real Ed25519 JWTs against
in-memory stubs with no network or Postgres.