<!-- nornir:class: design -->
# holger capability name map — the Rosetta stone
**One capability, up to six names.** This table answers "which crate serves PyPI?" — a
question that, until 2026-08-01, `grep` could not answer at all.
## Why this file exists
holger's PyPI support answers to four unrelated names, and none of them was the one you
would search for:
| the name | where it lives |
|---|---|
| `PyPI` | the docs, the UI, and what a human calls it |
| `pip` | the `ron_repo_type` an operator writes in RON — the **config** key |
| `ArtifactFormat::Pip`, `PipRepoZnippy` | the **code** |
| `znippy-python/`, `holger-znippy-python-repository` | the **directory** and the **package** |
`grep -ri pypi server/` found nothing in the implementation, there was no mapping table
anywhere under `.nornir/`, and — measured on 2026-08-01 — **zero** crates carried a
`pypi` or `pep503` keyword: all 17 publishable crates shared the same
`["znippy", "artifacts", "blake3", "cli", "airgap"]`. That cost about two hours and
nearly produced a SECOND PEP 503 implementation next to the one that already works.
Both halves are fixed now: each backend's `keywords` carry its ecosystem's real names,
and this table exists. Both are guarded by
[`traits/tests/manifest_metadata_guard.rs`](../traits/tests/manifest_metadata_guard.rs),
which reads `ArtifactFormat::home()` and FAILS if a format's names are missing from the
manifests **or from this file** — so the map cannot rot into decoration.
## The source of truth
`ArtifactFormat::home()` in [`traits/src/lib.rs`](../traits/src/lib.rs). It is code, so it
is checkable; this document is derived from it and the guard proves it stays in step.
When you are writing code, call `home()` rather than reading this table.
## The map
Search any name in the **names** column; it leads to the crate in the same row.
| format (code) | `ron_repo_type` | other names it answers to | directory | cargo package |
|---|---|---|---|---|
| `ArtifactFormat::Maven3` | `maven3` | `maven`, `maven2`, `java` | `server/repository/znippy-maven/` | `holger-znippy-maven-repository` |
| `ArtifactFormat::Pip` | `pip` | `pypi`, `python` | `server/repository/znippy-python/` | `holger-znippy-python-repository` |
| `ArtifactFormat::Rust` | `rust` | `cargo`, `crates.io`, `cratesio` | `server/repository/znippy-rust/` | `holger-rust-znippy-repository` |
| `ArtifactFormat::Go` | `go` | `golang` | `server/repository/znippy-go/` | `holger-znippy-go-repository` |
| `ArtifactFormat::Nuget` | `nuget` | `dotnet` | `server/repository/znippy-nuget/` | `holger-znippy-nuget-repository` |
| `ArtifactFormat::Npm` | `npm` | `node` | `server/repository/znippy-npm/` | `holger-znippy-npm-repository` |
| `ArtifactFormat::Gem` | `gem` | `ruby`, `rubygems` | `server/repository/znippy-gem/` | `holger-znippy-gem-repository` |
| `ArtifactFormat::Deb` | `deb` | `debian`, `apt` | `server/repository/znippy-deb/` | `holger-znippy-deb-repository` |
| `ArtifactFormat::Rpm` | `rpm` | `yum`, `dnf` | `server/repository/znippy-rpm/` | `holger-znippy-rpm-repository` |
| `ArtifactFormat::Helm` | `helm` | `chart` | `server/repository/znippy-helm/` | `holger-znippy-helm-repository` |
| `ArtifactFormat::Docker` | `docker` | `oci` | `server/repository/znippy-docker/` | `holger-znippy-docker-repository` |
| `ArtifactFormat::Conda` | `conda` | `anaconda` | `server/repository/znippy-conda/` | `holger-znippy-conda-repository` |
| `ArtifactFormat::Composer` | `composer` | `php` | `server/repository/znippy-composer/` | `holger-znippy-composer-repository` |
| `ArtifactFormat::Znippy` | `znippy` | `snippy` | `server/repository/znippy-package/` | `holger-znippy-package-repository` |
| `ArtifactFormat::Raw` | `raw` | `generic` | — (`server/lib/src/raw.rs`) | `holger-server-lib` |
### Formats with TWO backends
Some formats have both a read-only znippy-backed backend and a writable disk-backed peer.
Both answer the same `ron_repo_type`; which one you get depends on the repository's
storage wiring.
| format | read-only (znippy archive) | writable (disk) |
|---|---|---|
| `ArtifactFormat::Rust` | `holger-rust-znippy-repository` (`znippy-rust/`) | `holger-rust-file-repository` (`file-rust/`) |
| `ArtifactFormat::Docker` | `holger-znippy-docker-repository` (`znippy-docker/`) | `holger-file-oci-repository` (`file-oci/`) |
## The traps, named
* **`pip` vs `pypi` vs `python`.** Config says `pip`, code says `Pip`, the directory says
`python`, humans say PyPI. All four resolve to `holger-znippy-python-repository`, which
serves a real PEP 503 simple index (plus PEP 691 JSON and the `#sha256=` digests pip
and uv verify). **It already exists. Do not write a second one.**
* **`docker` vs `oci`.** `oci` is both an alias for the Docker format AND the name of a
second, file-backed, WRITABLE implementation (`file-oci/`). The znippy one
(`znippy-docker/`) is read-only passthrough.
* **`znippy` the format vs `znippy` the store.** Every `znippy-<eco>` crate is *backed by*
a znippy archive; only `znippy-package/` serves `.znippy` files **as** the artifact. And
note the directory is `znippy-package`, not `znippy`.
* **Word order flips.** `znippy-rust/` is package `holger-rust-znippy-repository`, not
`holger-znippy-rust-repository`. Same for `file-rust/` → `holger-rust-file-repository`.
* **`maven3` is the config key**, not `maven`. `maven`, `maven2` and `java` are aliases.
* **`raw` has no crate of its own.** The generic/raw blob bucket lives in
`server/lib/src/raw.rs`, which is why its directory column is empty.
## If you are about to implement an ecosystem
Search this table first, then `ArtifactFormat::home()`, then the directory. The name in
your head is probably not the name on disk — that is the entire reason this file exists.