The module named github.com/rodent-software/vole, a GitHub repository that does
not exist. Moving it to the forge alone would not have been enough: go get
resolves an import path by fetching https://<import-path>?go-get=1 and reading a
<meta name="go-import"> tag, and the forge served only pages. So this change
does both halves — the discovery tag first, then the path that depends on it.
What changed
Go module discovery. A new goModuleMeta on the web layer answers the
?go-get=1 probe from the catch-all route. The first path segment is the
repository, any segments after it are packages, and the tag names the repository
prefix and its Git transport:
<meta name="go-import" content="rodent.software/vole git https://rodent.software/git/vole.git">
It lives on the catch-all rather than a new route because a module path is not a
page the forge serves, and the catch-all already sees exactly the paths no other
route claimed — no new top-level route can shadow or be shadowed by /r/, /u/,
/static/ and the rest. Decisions inside it:
- Public repositories only. The tag is fetched without credentials — that is
the discovery protocol — so answering for a private repository would confirm
its existence to anyone who guessed the name. A private repository answers 404,
as everywhere else.
docs/go-modules.mdsays how to consume one instead (local checkout plusreplace). - The host comes from
public_url, never the request’sHostheader, for the same reason the clone URL does: the import prefix is a claim about which module path this instance owns, and a header an attacker chose must not make it. - The prefix is the host name alone, because a module path cannot carry a port. An instance reachable only on a nonstandard port still serves pages; its repositories are not importable paths, which the page states.
- The link in the tag’s explanation is
/r/<repository>, the page a reader already knows.
The module path. module rodent.software/vole in go.mod, and every import
in 253 files follows it. The build line in docs/getting-started.md, the nfpm
homepage, and both packaged units’ Documentation= URL now point at the forge
(https://rodent.software/r/vole) instead of the nonexistent GitHub repository.
Documentation. A new docs/go-modules.md (linked from the docs index and from
the public_url section of the configuration guide) explains the lookup, what the
instance must have configured, the public-only rule, and the naming limit below.
Naming limit
A repository named after a top-level route — r, u, admin, activity,
login, static, and so on — keeps its pages and clone URL but cannot claim
<host>/<name> as an import prefix, because the route wins the path. Because the
probe is answered from the catch-all, this needs no reserved-name list; it simply
never reaches it. That is documented rather than enforced, since renaming an
existing repository is not something this change should decide.
Verification
TestPublicRepositoryServesGoImportMetadatachecks the tag at the repository path, at a package path under it, and with a trailing slash, plus the HTML content type and that the path without the probe is still the styled 404.TestPrivateRepositoryDoesNotAdvertiseAGoModuleandTestGoImportIgnoresPathsThatNameNoRepositorycover the refusals.go build ./...andgofmtare clean under the new module path;./vole workflow run verifypasses (format, vet, build, licenses, govulncheck, packages, deployment files, markdown links, race tests).
Closes #248.