doltsrht: serve /query and the api-meta.json beside it The schema is mounted where /mcp is and for the same three reasons — before web's same-origin group, before the cookie middleware, in a group of its own — plus one of its own: it answers anonymous callers, so it cannot ride core-go's WithSchema, whose router 401s an un-cookied request. It does need the config and database middleware, which is why the group sits below them. The file beside it is not optional. meta.sr.ht fetches api-meta.json from every service it discovers and iterates each one's scopes to build the personal-token page, so a service that mounts its own /query owes the instance this too — and the scope list is what makes "dolt.sr.ht/repos:RO" offerable there at all. The never-null rule that makes a mistake here a 500 for the WHOLE instance lives in ecore's apimeta; a test asserts the name this service advertises is the one authn.RepoScope enforces. README says what the surface serves and what it deliberately does not, and that `hut graphql dolt` needs the patched hut — upstream segfaults on any service outside its hard-coded list.
web: rename a database from its settings page A database's name lives in two places — the metadata row and the on-disk store directory its path names — so a rename has to move both. The order is creation's: the row first, where a name already taken is refused by the unique index before anything on disk changes, then the store, then the handle the remotesapi memoized under the old path. A store that will not move rolls the row back, so the two halves never disagree about where a database lives; only a failed rollback is escalated to a human, because it is the one outcome no later request can repair. storage.MoveStore is one os.Rename behind the containment guard DeleteStore already used, extracted here as containedPath. It refuses an occupied destination outright: os.Rename over an empty directory would succeed and swallow it. No redirect is left behind — the old address simply stops resolving, as on git.sr.ht — so an existing clone needs its remote replaced, and a companion of a git repository is re-provisioned under its old name by the next push to that repository. The README's quickstart now says both.
storage: create databases empty so the first push needs no --force Every automatic creation path wrote an "Initialize data repository" commit through WriteEmptyRepo, and that commit is history. dolt decides fast-forward on the client (actions.CanFastForward over the remotesapi), so the server cannot forgive the collision: pushing a database that has a root commit of its own — a beads tracker, anything grown locally — was rejected as a non-fast-forward and could only land with --force. That is the whole reason the companion-database recipe starts with a forced push. push-to-create already provisioned an empty store for this exact reason. Give the other two paths the same default: /internal/repos, whose caller is git.sr.ht's post-update hook and therefore fires before its user has ever pushed, now always provisions empty, and the web form does unless its new "initialize with an empty commit" checkbox is ticked. The checkbox buys what an empty store cannot offer — a database that can be cloned before anything is pushed to it, since dolt refuses a store with no commits as "contains no Dolt data". Which is also why the overview of a database with no branches now teaches push rather than clone: the clone box there quoted a command that could not work. A store that fails to open is deliberately not treated as empty — an unreadable database must not be advertised as a fresh one.
doltsrht: serve /mcp on the web listener
instconf: take the origin and required-key helpers from ecore Two copies of one function disagreed in this repo: hostFromOrigin returned an error for a malformed origin and web's hostOf answered "localhost", which is a guess that looks like an answer. Both are gone; the caller now names which half it means, and both wanted OriginAuthority — a port is part of a sealed-URL host, a JWT audience and the synthesized commit-author domain alike. The startup checks become one Require, so an operator filling in a fresh config.ini reads every missing key off one boot instead of one per restart. The hook's internal-origin read becomes InternalOrigin, which falls back to the external origin: an instance with only a public address is not misconfigured and used to be refused. And the git-description mirror is wired only when git.sr.ht has an API origin — web.Config already documented a nil Git as no mirroring, but nothing produced one, so an instance without git.sr.ht met config.GetAPI's panic on the first push.
cmd: doltsrht and doltsrht-migrate binaries, module tidy