Glossary, the mesh-controller/foundation vocabulary, ADR 0076, Phase 3 closed #43
@@ -0,0 +1,116 @@
|
|||||||
|
---
|
||||||
|
topic: the tiers
|
||||||
|
status: proposed
|
||||||
|
date: 2026-09-15
|
||||||
|
deciders: jochen
|
||||||
|
reconstructed: false
|
||||||
|
extends: 0014-no-npm-workspace.md
|
||||||
|
---
|
||||||
|
|
||||||
|
# 75. An artifact store is a provision; a package registry is a different one
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
Two questions have been circling, and they turn out to be one question asked twice.
|
||||||
|
|
||||||
|
**"Should gitea be the mesh's registry?"** It serves OCI images and a dozen package ecosystems, it
|
||||||
|
is already needed — genesis clones from one — and the mesh's own registry has neither
|
||||||
|
authentication ([issue 042](../04-ISSUES/042-nothing-gives-a-node-an-account-for-a-registry/00-report.md))
|
||||||
|
nor a transport a runtime will accept over a network
|
||||||
|
([issue 048](../04-ISSUES/048-nothing-makes-a-machine-trust-the-mesh-registry/00-report.md)). Gitea
|
||||||
|
has both.
|
||||||
|
|
||||||
|
**"Where does the SDK come from?"** [ADR 0014](0014-no-npm-workspace.md) already answers it — each
|
||||||
|
module consumes its dependencies, the mesh's own shared library included, *from the private
|
||||||
|
registry* — and nothing installs one, so today it comes from a git URL, which is
|
||||||
|
[issue 053](../04-ISSUES/053-the-sdk-is-pinned-twice-and-the-two-disagree/00-report.md).
|
||||||
|
|
||||||
|
**The framing that dissolves both:** `artifact-store` is already a provision, and `registry`
|
||||||
|
already provides it. So "should gitea be the registry" is not a question about replacing a
|
||||||
|
component. It is a question about **a second provider of an existing provision** — which this mesh
|
||||||
|
has a mechanism for, and uses for certificate authorities and VPNs already.
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
**Two provisions, because they are two jobs.**
|
||||||
|
|
||||||
|
| provision | is | for |
|
||||||
|
|---|---|---|
|
||||||
|
| `artifact-store` | content-addressed blobs, pinned by digest, no versions, no ranges | what the **mesh** delivers to **machines** |
|
||||||
|
| `package-registry` | an ecosystem's own registry — npm, cargo, PyPI, Go | what **code** resolves when it is compiled |
|
||||||
|
|
||||||
|
They are not the same store with different clients. One is addressed by digest and immutable by
|
||||||
|
construction; the other is addressed by name and version, and resolves ranges. Conflating them is
|
||||||
|
how a mesh that pins everything ends up rebuilding one commit into two different things.
|
||||||
|
|
||||||
|
**`registry` remains the provider genesis installs.** Not because it is better, but because of what
|
||||||
|
it is: a directory and one container, no database, no control plane, installable at step 8 of an
|
||||||
|
install where neither exists yet. Gitea needs a store and provisioning, which means a control plane,
|
||||||
|
which means the pivot has already happened — and the pivot needs somewhere to publish to.
|
||||||
|
|
||||||
|
**Gitea also provides `artifact-store`, and a mesh may choose it.** Two providers of one provision
|
||||||
|
is a thing the mesh understands: it refuses, names both, and choosing is assigning the one you want.
|
||||||
|
A mesh that assigns gitea gets authentication and TLS for its artifacts — which is to say, **issues
|
||||||
|
042 and 048 are answered by choosing a provider that already solved them**, rather than by
|
||||||
|
reimplementing accounts and certificates in a registry that has none.
|
||||||
|
|
||||||
|
**Gitea provides `package-registry`.** That is ADR 0014's private registry, and it is one service
|
||||||
|
rather than one per ecosystem. `verdaccio` may provide it too, for npm alone, and is then a choice
|
||||||
|
somebody makes rather than the answer.
|
||||||
|
|
||||||
|
**The registry is not removed at the end of installing.** A mesh that never runs gitea still has an
|
||||||
|
artifact store. Retiring it is a migration a mesh performs, not a step an installation ends with.
|
||||||
|
|
||||||
|
## Why not simply gitea, from the start
|
||||||
|
|
||||||
|
Because genesis would need a control plane before the thing that stores the control plane's image,
|
||||||
|
and that is circular rather than merely awkward. It would also make one of the three things the
|
||||||
|
build loop cannot produce for itself into a stateful application with a database — the pivot is the
|
||||||
|
hardest part of this design already.
|
||||||
|
|
||||||
|
And it puts every artifact in the service that is also the trust anchor for everything the mesh will
|
||||||
|
ever run ([ADR 0071](0071-where-genesis-gets-its-source.md)), which records that forge serving a
|
||||||
|
cryptominer with tampered git operations. Two blast radii are better than one.
|
||||||
|
|
||||||
|
## Moving from one provider to the other is a designed act
|
||||||
|
|
||||||
|
**Not a removal.** Every image a machine runs is pinned to a digest at a named store, the control
|
||||||
|
plane's own included. Changing the provider means:
|
||||||
|
|
||||||
|
1. gitea installed, reachable, and holding an account the builder may publish with
|
||||||
|
2. every artifact mirrored
|
||||||
|
3. every declaration re-pinned, the control plane's **last**, because it is what performs the others
|
||||||
|
4. **every machine verified to have converged and to be able to pull from the new store**
|
||||||
|
5. only then the old provider unassigned, and its volume kept ([ADR 0030](0030-data-outlives-the-mesh-that-declared-it.md))
|
||||||
|
|
||||||
|
Step 4 is the one that is easy to skip and the only thing between this and a mesh that cannot
|
||||||
|
restart its own control plane. A machine that reboots mid-migration pulls from a store that no
|
||||||
|
longer exists, and a local image cache hides that until exactly the moment it matters.
|
||||||
|
|
||||||
|
## Consequences
|
||||||
|
|
||||||
|
**The bootstrap is unchanged**, which is the point of keeping the small provider.
|
||||||
|
|
||||||
|
**042 and 048 gain a second answer.** They can be fixed in the registry, or dissolved by choosing a
|
||||||
|
provider that already has accounts and TLS. The second is less work and more service.
|
||||||
|
|
||||||
|
**ADR 0014 becomes satisfiable.** There is a provision for the private registry, something that
|
||||||
|
provides it, and a module may depend on it — so the SDK can be published and consumed rather than
|
||||||
|
cloned, and issue 053 has somewhere to go.
|
||||||
|
|
||||||
|
**A mesh can be minimal or complete, and both are legitimate.** One with the small registry and no
|
||||||
|
gitea builds and runs modules and cannot serve packages. That is a real configuration, not a broken
|
||||||
|
one — the same way a partial host is real.
|
||||||
|
|
||||||
|
**And the bootstrap still has no package registry.** The first build of the shared base happens
|
||||||
|
before anything has installed one. That is the same pivot as everything else and it is **not solved
|
||||||
|
here**: it is named, so the next person does not discover it.
|
||||||
|
|
||||||
|
## How this is checked
|
||||||
|
|
||||||
|
| Rule | Checked by |
|
||||||
|
|---|---|
|
||||||
|
| Genesis needs no database | The installer raises a mesh of one on a machine with nothing, and the artifact store it installs has no store of its own. |
|
||||||
|
| Two providers are a choice, not a conflict | A mesh holding both is asked to resolve `artifact-store` and refuses, naming both, until one is assigned. |
|
||||||
|
| The two stores are not interchangeable | A module depending on `package-registry` is not satisfied by `artifact-store`, and the refusal says why. |
|
||||||
|
| A migration is verified before it is finished | The old provider cannot be unassigned while any machine's declaration still names it. |
|
||||||
@@ -107,6 +107,7 @@ python3 00-META/checks/index.py fail if stale
|
|||||||
- **0072** — [Two graphs, and a build chain that orders itself](0072-two-graphs-and-the-build-chain.md)
|
- **0072** — [Two graphs, and a build chain that orders itself](0072-two-graphs-and-the-build-chain.md)
|
||||||
- **0073** — [The installer carries a builder, and the registry stays where it is](0073-the-installer-carries-a-builder.md)
|
- **0073** — [The installer carries a builder, and the registry stays where it is](0073-the-installer-carries-a-builder.md)
|
||||||
- **0074** — [The mesh defines a module protocol; an SDK is an implementation of it](0074-the-wire-is-specified-not-the-types.md)
|
- **0074** — [The mesh defines a module protocol; an SDK is an implementation of it](0074-the-wire-is-specified-not-the-types.md)
|
||||||
|
- **0075** — [An artifact store is a provision; a package registry is a different one](0075-two-stores-and-which-provides-what.md) *(proposed)*
|
||||||
|
|
||||||
### What runs on them, and how it gets there
|
### What runs on them, and how it gets there
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user