CANP-WP-0006 T05: HTTP registries, and one finding

--registry now accepts an http(s):// URL as well as a path, implemented with
urllib so reference/ keeps PyYAML as its only dependency.

This was the first real test of INTENT principle 10's claim that a hosted
registry layers on without changing package semantics. Verified against the
running service, almost everything survived the transport unaltered: identity
and its ambiguity rules (a bare id in two registries returns 409 over HTTP just
as it does locally), immutability of a published id@version (identical content
accepted, changed content refused, version bump accepted), strict packaging,
validation, and the index. A package published and then installed over HTTP was
byte-identical to its source — diff -r clean — and its canonical-fidelity eval
still passed after the round trip.

One thing did not survive: a URL is not a registry. A filesystem registry IS
one registry and section 20.1 names it from its directory; an HTTP service
HOSTS SEVERAL behind one base URL. The address therefore cannot name the
registry, so it must be named separately — --as when publishing, a qualified
reference when installing.

Recorded as section 20.4 rather than worked around silently in the client,
because the gap is in the specification's list of registry kinds, not in the
CLI. Publishing to an HTTP registry without --as fails with that explanation
rather than guessing a registry name.

Registry responses are treated as untrusted input (section 19): decode_files
refuses path traversal, with a test. The wire shape round-trips binary content
through base64, also tested.

Reference tests 99 -> 105.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Bjefh8NUiEiahN4JLwoSKM

Assistant: claude-code
Assistant-Model: opus
Assistant-Process: 388925@bnt-lap001
Assistant-Session: 3507023f-e0fd-4a1e-9d90-a0d4217d1502
This commit is contained in:
tegwick 2026-09-06 20:39:04 +02:00
parent 56b1708b58
commit c076d8395e
6 changed files with 263 additions and 3 deletions

View file

@ -1203,6 +1203,37 @@ about the last time someone ran a command.
Because identity is registry-scoped (§ 3.2), the same `id@version` from two
registries is two entries, not a conflict.
### 20.4 A URL is not a registry
§ 20 lists "an HTTP service" among the things a registry may be. Implementing
one showed that this conflates two different objects.
A **filesystem registry is one registry**: it is a directory, and § 20.1 names
it from that directory. An **HTTP service hosts several** — the reference
service holds `local`, `helix` and any number of others behind one base URL. A
URL therefore addresses a *host of registries*, and cannot name one the way a
directory does.
Consequently, when the transport is HTTP the registry MUST be named separately
from the address:
- **publishing** names the target registry explicitly, because the service
cannot infer which of its registries a package belongs to;
- **installing** names it in a qualified reference (§ 3.2), and a bare id
matching more than one is reported as ambiguous exactly as it is locally.
A service MAY instead expose each registry at its own URL prefix, making the
address name a registry again. This specification does not require either
arrangement, but a consumer MUST NOT assume that one address means one
registry.
Everything else survived the transport change unaltered, which is what INTENT
principle 10 claims: identity and its ambiguity rules (§ 3.2), immutability of a
published `<id>@<version>` (§ 17), strict packaging (§ 2), validation, and the
index (§ 20.3) all behave the same over HTTP as on a filesystem. The registry
naming above is the single point where the transport is visible in the
semantics, and it is a gap in this section rather than in the format's core.
## 21. Reference CLI semantics
The reference tool uses two stores: