77 lines
3.9 KiB
Markdown
77 lines
3.9 KiB
Markdown
---
|
|
status: resolved
|
|
opened: 2026-09-15
|
|
located-in: [mesh-tools, mesh-host, mesh-sdk]
|
|
fixed-by: mesh-tools b618057 (the SDK resolved by version from the registry, one pin); mesh-host 7986306; mesh-controller 4b9bc50. Caveat: the base image still builds with npm install and no lock, so the build is not reproducible
|
|
amended-design:
|
|
---
|
|
|
|
# 053 — The SDK is pinned twice, and the two disagree
|
|
|
|
## Symptom
|
|
|
|
The module that carries the tool runtime names the SDK two ways, and they are not the same thing:
|
|
|
|
```
|
|
package.json @novox/mesh-sdk -> git+https://<the forge>/mesh-sdk.git#<a commit>
|
|
package-lock.json @novox/mesh-sdk -> ../mesh-sdk
|
|
```
|
|
|
|
The manifest names a commit in a repository any machine can reach. The lock names a **sibling
|
|
directory**, which exists on the workstation the lock was generated on and nowhere else.
|
|
|
|
It builds anyway, because the recipe runs `npm install` — which tolerates a lock that disagrees
|
|
with its manifest and re-resolves from the manifest. It is the one command that hides this.
|
|
|
|
## Why this matters
|
|
|
|
**A lock file exists to make a build reproducible, and this one describes one machine.** `npm ci` —
|
|
the command for exactly the case a lock is for — fails here, or worse, succeeds against whatever
|
|
happens to be at that path.
|
|
|
|
**It is the first thing a fresh mesh builds.** The toolchain image carries the SDK, and everything
|
|
with code of its own is compiled inside it. A dependency resolved differently on the build machine
|
|
than on a workstation is a difference in every module the mesh will ever build, arriving as a
|
|
compile error or a runtime mismatch far from here.
|
|
|
|
**And it is about to be copied.** Each language's toolchain will carry that language's SDK the same
|
|
way ([ADR 0074](../../02-DECISIONS/0074-the-wire-is-specified-not-the-types.md)). Whatever this
|
|
repository does, the Rust and Python ones will do, so the shape is worth getting right before there
|
|
are four of them.
|
|
|
|
## It is a violation, not an open question
|
|
|
|
[ADR 0014](../../02-DECISIONS/0014-no-npm-workspace.md) already decided this, and was accepted:
|
|
|
|
> Each module is an independent package that declares its dependencies and **consumes them from
|
|
> the private registry, including the mesh's own shared library.** A cross-package change is
|
|
> therefore two steps: publish the producer, then consume it.
|
|
|
|
So a git dependency at a pinned commit is not an alternative mechanism under consideration. It is
|
|
the mesh's own shared library being consumed by a means the record rules out, and the lock naming
|
|
a sibling directory is what that looks like when nobody publishes.
|
|
|
|
**Which makes the fix a direction rather than a discussion**: publish the SDK to the private
|
|
registry, consume it by version, and the lock stops being able to name a path that exists on one
|
|
machine.
|
|
|
|
## Open questions
|
|
|
|
- **Which private registry, and does the bootstrap have one?** The catalogue holds `verdaccio`;
|
|
a git host typically serves package registries too. ADR 0014 says *the* private registry as
|
|
though there is one, and today a fresh mesh has neither until something installs it — so the
|
|
first SDK build happens before the registry the record assumes.
|
|
- **Or should the SDK be a published package?** The catalogue holds a private registry module, and a
|
|
published package is how the rest of the world does this — at the cost of a mesh needing that
|
|
registry up before it can build anything, which is a bootstrap problem where there is currently
|
|
none.
|
|
- **What generates the lock, and on what?** A lock produced on a workstation with sibling checkouts
|
|
will keep saying this. A lock produced the way the image builds would not.
|
|
|
|
## How this would be checked
|
|
|
|
| Rule | Checked by |
|
|
|---|---|
|
|
| A build does not depend on the machine it runs on | The toolchain image builds with `npm ci` rather than `npm install`, which refuses a lock that disagrees with its manifest. |
|
|
| The SDK a module compiles against is the one named | The commit baked into the toolchain image is compared with the one the manifest pins. |
|