Serve nested repositories over HTTP #67
Loading…
Reference in a new issue
No description provided.
Delete branch "worktree-agent-ab8e151c29539ab74"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Wild repos (
agents/<name>) worked over SSH and were unreachable over HTTP:repos::discoverread one level ofrepos_dir, so the repo was unlisted, 404'd at every path, and itsrefs/collab/*could not be fetched by an HTTP clone at all.Discovery now recurses and repositories are named by their full path under
repos_dir.Naming rule — the same key governance writes its rules against (
governance::repo_key): path relative torepos_dir,/-separated, trailing.gitstripped from the last segment unless a sibling with the stripped name is itself a repository. That separatestools.gitfromtools; stripping both collapsed them onto one name and left whicheverdiscoverreached second permanently unreachable. Both are now listed, browsable and clonable under distinct names.Routing rule — the longest leading run of path segments that names a repository is the repository; the rest is the sub-route. Longest wins because the repository is the concrete fact (a directory that exists) while the sub-route vocabulary is ours, so every repository on disk is reachable at its own address. A repository named
agents/issueswould shadow the issue list of a repository namedagents; that is an operator-chosen collision, and preferring the shorter name would instead leave the nested repository with no address at all. Implemented by folding the name into one percent-encoded segment in middleware wrapped around the router (Router::layerruns after routing), so every route, handler and template is untouched.Depth — bounded at 8 segments.
discoverwalks every non-repository directory and is reached from an anonymous page; without a bound, whatever an operator leaves underrepos_dirsets the cost of rendering the listing.resolveapplies the same bound, so a repository is never resolvable but unlisted. Real layouts are 2 deep.Caching — none, deliberately.
resolveno longer scans: a name determines a path, so it probes it (O(depth) stats), which removes the full-scan-per-request the old comment flagged and with it the motive for a cache. A wild repo is created by an SSH push and must be visible on the very next HTTP request — the exact workflow it exists for — andgovernance::loadalready made the same uncached-per-request call for the same reason. A test drives create-over-SSH then GET-over-HTTP.Safety —
resolvevalidates before probing (no empty/dot-prefixed/..segments, depth capped), and the walk down refuses to pass through a symlink, so a name cannot address anything outsiderepos_dir. A symlinked repository at a leaf is still discovered, as before. Dot-prefixed segments are skipped everywhere, which is what keeps.server/(the SSH host key) out; it is the only reserved name underrepos_dir.Governance — the settings-repo default now lives in
apply_governance_default, applied wherever an entry is built rather than only indiscover, because the anonymous surface reaches a repo viaresolve.agents/settingsis an ordinary public repository andsettingsis still closed by default; tested through both paths.entry_for_pathnow takesrepos_dirso SSH and HTTP agree on what a repository is called. SSH never used the name, so this is a tightening only: a repository under a dot-prefixed segment or past the depth limit is no longer served over SSH either.TDD throughout: the e2e reproduction was written first and failed with exactly the issue's table (
href="/alpha"only,/agents/claude-a404).Tests: 5 end-to-end against a live server (nested repo listed + 200 +
git fetch 'refs/collab/*'over real HTTP exercising both GET info/refs and POST upload-pack;toolsvstools.git;.server; traversal; SSH-create then HTTP-visible), plus unit tests for the naming rule, non-descent into repositories, the depth bound, traversal rejection, and the routing fold.Full suite green,
cargo clippy --all-targetssilent.Migrated from git-collab patche
02671b2a4f45837acbdc89a952753529b5ca64ad.Original author: a73x dev@a73x.sh.
Fixes #64
Revision 1:
15a970d19fa8e44648dd0aff18e43827aeebdbf7; baseeb3de6b1500bb2e5e0d5fea06ff163287772f29a