One directory, one bundle, one signing key on the trust list. The only thing that changes between the two runs is whether a daemon is serving that directory.
$ sapedb install lib.bundle # nothing is serving it
installing bundle "library" version "1.0.0", signed by bc711bad…, trusted here as "me"
exit 0
$ sapedbd & # now something is
$ sapedb install lib.bundle
sapedb/vfs: another process has this database open: the directory …/db
the server is probably running; stop it, or work on a dump instead
exit 1
At that same moment, over the wire, the daemon answers normally — an invoke against it
in another window returns its rows. Nothing is broken. The offline command line simply takes the
exclusive lock, as it says it does, and a bundle arrives only through it.
Two ways of putting new declarations into a running database already exist: Declare
(frame 13) and Establish (frame 14). Both reach a live daemon. Both carry
one hand-written declaration at a time, and neither checks a
signature.
The path that does have the signature, the trust list, the all-or-nothing transaction and
the signing key recorded in the change log — SAPE-28's
install — has no frame at all. So the most carefully checked way to
change what a database will do is the one way that cannot be done while anybody is using it.
Stated from the other end: an operator who is handed a signed module by somebody they do not know has exactly one way to accept it, and it is downtime.
The triage question is whether it loses data, breaks a promise on a public surface, or stops somebody installing and running the thing. It does none of those. Installing works; it costs an outage. The refusal is loud, names the directory, and tells the operator what to do about it — it is a documented limit behaving as documented, not a defect.
It is recorded because it is now the largest gap on the path a stranger actually walks, and because the shape of it is the interesting part: the feature with the most checking on it is the feature with the least reach. A reader who notices that before we do should find it already written down.
internal/service.FromEnv deliberately does not read today, so it is a new
configuration surface as well as a new frame.bundle_* refusal codes already exist in internal/server and
already have no call site over the wire. They were written for this.