CANP-WP-0002 T02: registry-scoped identity and namespace ownership
Section 17 already defined what a registry does when the same id@version is republished; nothing defined what a *consumer* does. That is where namespace conflict actually bites — in the catalog, after installing from two registries. Identity is now registry-scoped: an id names a package within a registry, the way a path names a file within a repository. A local-first format with no signing, no federation and no central authority cannot enforce global uniqueness, and an unenforceable guarantee is worse than none — it invites consumers to conflate two packages that merely share a name. A qualified `<registry>:<id>` reference distinguishes them, and `:` is now barred from ids so the separator stays available. Installing the same id from two registries is therefore not a conflict. The catalog is namespaced by registry and keeps both. Ownership is registry policy, not package data. An optional `registry.yaml` names a registry and records namespace claims. Those claims are explicitly descriptive — a filesystem registry cannot authenticate a publisher, and `publish` says so rather than implying it checked. Keeping the claim out of packages leaves artifacts free of unverifiable assertions of authority, and means package semantics do not change when a hosted registry appears later. Spec: 3.2 (registry-scoped identity, qualified references), 17 (immutability scoped to a registry), 20.1 and 20.2 (new), 18 (registry-manifest validation), 21 (qualified references, reserved `local` name). Reference CLI: parse_reference, check_registry_name, read_registry_manifest, registry_name, namespace_policy; registry_package_path and catalog_package_path split; resolve_installed reports ambiguity and returns the source registry; iter_catalog; `add --as`; closed-namespace warning on publish. Tests 11 -> 21. The catalog layout changed. An existing catalog is detected and reported with instructions rather than failing as "package not found". Signing, trust scoring and federation remain non-goals and were not touched. 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:
parent
4a8422e1aa
commit
ea70a59610
6 changed files with 533 additions and 45 deletions
|
|
@ -119,8 +119,42 @@ Rules for v0.1:
|
|||
- `/`, `-`, `_`, and `.` MAY be used;
|
||||
- whitespace MUST NOT be used;
|
||||
- the ID MUST NOT contain `..` path traversal segments;
|
||||
- `:` MUST NOT be used, so that it remains available as the registry separator
|
||||
in a qualified reference (below);
|
||||
- registry implementations MUST treat the ID as logical metadata rather than an unchecked filesystem path.
|
||||
|
||||
##### Identity is registry-scoped
|
||||
|
||||
An `id` names a package **within a registry**, the way a path names a file
|
||||
within a repository. CPF v0.1 does **not** claim that an id is globally unique:
|
||||
`practice/pqrst-estimate` obtained from two different registries may be two
|
||||
different packages, and a consumer that draws on more than one registry MUST
|
||||
keep track of which registry each package came from.
|
||||
|
||||
This follows from what the format actually guarantees. A local-first format
|
||||
with no signing, no federation and no central authority (all explicit
|
||||
non-goals) cannot enforce global uniqueness, and a guarantee that cannot be
|
||||
enforced is worse than none — it invites consumers to conflate two packages
|
||||
that merely share a name.
|
||||
|
||||
Where a consumer must distinguish them, a **qualified reference** names the
|
||||
registry:
|
||||
|
||||
```text
|
||||
<registry>:<id>
|
||||
```
|
||||
|
||||
For example:
|
||||
|
||||
```text
|
||||
house:practice/pqrst-estimate
|
||||
upstream:practice/pqrst-estimate
|
||||
```
|
||||
|
||||
An unqualified id is acceptable wherever it is unambiguous for that consumer.
|
||||
A tool that finds the same id in more than one registry MUST report the
|
||||
ambiguity rather than choosing for the caller.
|
||||
|
||||
#### `name`
|
||||
|
||||
Human-readable display name.
|
||||
|
|
@ -602,6 +636,12 @@ Published `<id>@<version>` pairs SHOULD be immutable.
|
|||
|
||||
A registry SHOULD reject publication of a package when the same ID/version already exists with different contents unless an explicit administrative override mechanism exists.
|
||||
|
||||
Immutability is scoped to a registry, because identity is (§ 3.2). The same
|
||||
`<id>@<version>` held by two registries is two packages, and a consumer holding
|
||||
both is not in a conflict — it is holding two things whose names happen to
|
||||
coincide. Only a repeat publication *within one registry* violates
|
||||
immutability.
|
||||
|
||||
Suggested versioning guidance:
|
||||
|
||||
- PATCH: wording/metadata correction with intended behavior unchanged;
|
||||
|
|
@ -632,6 +672,12 @@ A v0.1 validator SHOULD verify at least:
|
|||
A validator SHOULD additionally warn when a package intended for publication
|
||||
declares an inline derivation prompt (§ 6.1).
|
||||
|
||||
A validator that is given a **registry** rather than a package SHOULD verify,
|
||||
when `registry.yaml` is present, that `format` is
|
||||
`canned-prompt-registry/v0.1`, that `name` is a non-empty string usable in a
|
||||
qualified reference, and that each `namespaces` entry declares a `policy` of
|
||||
`open` or `closed`.
|
||||
|
||||
## 19. Security requirements
|
||||
|
||||
Prompt packages are content, not trusted code.
|
||||
|
|
@ -675,6 +721,70 @@ Conceptually, a registry stores immutable package versions keyed by:
|
|||
<id>@<version>
|
||||
```
|
||||
|
||||
### 20.1 Registry identity and namespace ownership
|
||||
|
||||
A registry MAY declare itself with a `registry.yaml` at its root:
|
||||
|
||||
```yaml
|
||||
format: canned-prompt-registry/v0.1
|
||||
name: house
|
||||
description: Internal prompt registry.
|
||||
|
||||
namespaces:
|
||||
practice:
|
||||
owner: Ada Example
|
||||
policy: closed
|
||||
scratch:
|
||||
policy: open
|
||||
```
|
||||
|
||||
Fields:
|
||||
|
||||
| Field | Required | Meaning |
|
||||
|---|---:|---|
|
||||
| `format` | yes | MUST be `canned-prompt-registry/v0.1` |
|
||||
| `name` | yes | Short registry name, used in qualified references (§ 3.2) |
|
||||
| `description` | no | Human-readable explanation |
|
||||
| `namespaces` | no | Mapping of namespace to its claim |
|
||||
|
||||
Within `namespaces`, `owner` is a human-readable claim and `policy` is
|
||||
`closed` (only the owner publishes) or `open` (anyone may). Both are
|
||||
descriptive: a filesystem registry has no way to authenticate a publisher, and
|
||||
authentication, signing and trust scoring are all explicit non-goals.
|
||||
|
||||
The file is **optional**. A bare directory remains a valid registry; a consumer
|
||||
that finds no manifest SHOULD take the registry's name from how it was
|
||||
addressed — for the reference implementation, the registry directory's
|
||||
basename.
|
||||
|
||||
**Ownership is a property of the registry, not of the package.** A package
|
||||
never declares who owns its namespace. This keeps the claim where it can
|
||||
actually be acted on — the registry decides what it admits — and keeps
|
||||
packages free of unverifiable assertions of authority, consistent with § 19's
|
||||
position that a package is content rather than a trusted actor. It also means
|
||||
package semantics do not change when a filesystem registry is later replaced
|
||||
by a hosted one.
|
||||
|
||||
A registry SHOULD refuse to publish into a `closed` namespace it does not
|
||||
consider the publisher to own. How it decides is registry policy and is
|
||||
outside this specification.
|
||||
|
||||
### 20.2 Consumer-side layout
|
||||
|
||||
A consumer drawing on more than one registry MUST keep packages from different
|
||||
registries distinct, because identity is registry-scoped (§ 3.2). Installing
|
||||
the same `<id>@<version>` from two registries is not an error and MUST NOT
|
||||
overwrite: both are retained and addressed by qualified reference.
|
||||
|
||||
The reference implementation stores its catalog as:
|
||||
|
||||
```text
|
||||
catalog/
|
||||
└── <registry name>/
|
||||
└── <id path>/
|
||||
└── <version>/
|
||||
```
|
||||
|
||||
The reference implementation uses the filesystem layout:
|
||||
|
||||
```text
|
||||
|
|
@ -689,6 +799,7 @@ For example:
|
|||
|
||||
```text
|
||||
registry/
|
||||
├── registry.yaml
|
||||
└── practice/
|
||||
└── pqrst-estimate/
|
||||
└── 0.1.0/
|
||||
|
|
@ -696,6 +807,9 @@ registry/
|
|||
└── prompt.md
|
||||
```
|
||||
|
||||
A registry's own layout is not namespaced by registry name: within one
|
||||
registry, an id is unambiguous by definition.
|
||||
|
||||
## 21. Reference CLI semantics
|
||||
|
||||
The v0.1 reference tool uses two stores:
|
||||
|
|
@ -715,6 +829,14 @@ publish PATH validate and copy a package into a filesystem registry
|
|||
install ID copy a package version from the registry into the catalog
|
||||
```
|
||||
|
||||
Every command that takes an `ID` accepts either a bare id or a qualified
|
||||
`<registry>:<id>` reference (§ 3.2). A bare id that matches packages installed
|
||||
from more than one registry is reported as ambiguous, listing the candidates,
|
||||
rather than resolved by guessing.
|
||||
|
||||
`add` takes a package from a path rather than from a registry, so it files the
|
||||
package under the reserved registry name `local`.
|
||||
|
||||
The reference tool never calls a model, so its `resolve` handles supplied
|
||||
values and static defaults only and reports any input whose derived default it
|
||||
cannot satisfy. `render` performs the same resolution and then substitutes;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue