Skip to content

Add binary delivery with unsigned development artifacts - #250

Merged
crenshawdev merged 33 commits into
mainfrom
robbie
Oct 10, 2026
Merged

crenshawdev merged 33 commits into
mainfrom
robbie

Conversation

@crenshawdev

Copy link
Copy Markdown
Owner

Build 3 T14. Baley can now update itself, with unsigned development artifacts only. The binary lives in a versions folder behind the stable path ~/.local/bin/baley, which is a link to the active version. Two new settings drive it: updates.auto, off by default, and updates.source, an https:// address with no query or fragment. baley update runs a check from the command line, and when updates.auto is on, baley serve starts a hidden detached check at startup and never waits on it. Both claim the day's update check for the installation (the manual one claims even on a day already claimed), download the manifest and binary only behind a new claim, check the binary against the manifest's SHA-256 in verify_download, stage only a version higher than the active one, and swap the stable link in one atomic exchange. Every check records update.checked or update.failed on the install stream and prints a receipt naming what it did. The newly active binary then seeds its own catalog. An interrupted check is reconciled from what is on disk, and baley update resolve lets the owner clear a check held for them. The guard and the security proposal deny writes into the versions folder. install.sh at the repository root is a minimal POSIX installer held to the same rules: https:// only, the SHA-256 check before anything is staged, and the stable link placed only over a link Baley manages. The manifest is not signed. The release design #14 owns the signature format, the trust root and both verifiers, and has to fill the verify_download seam before any release. Designs 0001, 0002, 0003, 0010, 0012, 0014 and 0015, the C4 model, the roadmap and the README describe the updater as built.

Each acceptance check failed before its behaviour existed and passes now. nextest ran 3500 of 3500 after the last code change, clippy with -D warnings, cargo fmt --check and cargo deny pass, baley-core and baley-store still don't depend on rusqlite, Cargo.lock is unchanged, the touched docs' 46 Mermaid blocks parse, and a fresh C4 export matches the hand-edited blocks byte for byte. Verification passed 18 of 20 items and skipped two. The reviews found problems that are all fixed here: an update source with ? or # swallowed the platform path, a HOME spelled two ways gave one installation two identities, a relative link target was followed from the wrong folder, a lost claim could still stage or activate, staging and activation didn't sync the folder after the rename, an activation conflict was receipted with the old version, and install.sh read ~/.curlrc, set the mode after the move and judged the stable link too loosely.

Left open: nothing here has run baley update against a real HTTPS source, which T17 does live. The macOS renamex_np branch has never been compiled. install.sh has no unit test by design, and its real download, macOS and dash are unrun. The download client sets no_proxy, so an owner who can only reach the source through HTTPS_PROXY can't update. The claim fence is a point-in-time check, not one atomic ledger and filesystem step (#244). The refusals before and at the claim, the Current receipt, the renewal-error line, and reconciliation's stale-sequence guard have no test.

updates.auto is a global boolean that defaults to off. updates.source has no
default and takes only an https address, so config set cannot write a source
the file parser would then refuse.
Versions compare part by part as numbers, so 0.10.0 sorts above 0.9.0 and a
source that is behind reads as current, never as a downgrade. No semver crate
is added.
The installation is the stable path's text, so a trailing slash on HOME or a
re-pointed link keeps one identity. A link counts as a managed version only
when its target is exactly the staged binary under this home's versions
folder.
…osal

The folder is a write-only folder, not an artifact, so an unknown place never
reads as protection and the file list stays as it was. The proposal renders it
as a denyWrite entry and an Edit folder rule with no read rule, because a file
rule would leave the binaries inside it writable.
write_answer now checks a list of write-only folders beside home and config.
A read of a staged binary still passes, because sandboxed Bash has to run it.
The live guard passes an empty list until the install step supplies the real
folder.
verify_download is the one door to a VerifiedDownload, so staging can never be
handed bytes that skipped the SHA-256 check. It covers unsigned development
artifacts only, and the signed-manifest check replaces its body before any
release.
update.checked and update.failed go on the install stream in user at version 1.
No view or projector is added, so the view set stays at 7 and an older running
server keeps reading and writing the session's project.
A detached check asks the ledger for any claim of the installation and day and
is refused update-check-not-due when one exists. A manual check always
claims. Only a new claim yields a FetchPermit, which the fetchers will require,
so no download is reachable without it.
…path

The artifact addresses are the source text plus /<os>-<arch>/manifest and
/baley, so a source holding a query or fragment put the platform folder
inside it and fetched the wrong file. The file parser and config set share
one rule, which now refuses ? and #, and both messages say to write an
https:// address without a query or fragment.
…nk once

A HOME with dot components or repeated slashes spelled the stable path
differently from the same home written plainly, so a managed link read as
outside the versions folder and a second daily claim could start. Those
components are now dropped and a .. component is refused. The stable-path
gatherer also followed the link a second time, which could pair one
generation's target with another's file facts, so it now reads the file
facts from the target it captured.
The claim and its result event commit together, so a finished check
always leaves one update.checked or update.failed on install and frees
the installation for the next check.
Both fetchers take the permit that only a new claim produces, so no
request is reachable without one. A failed status, a transport error or
a body past its bound is refused before anything is parsed or verified.
…hat is there

Staging refuses any file it did not place, and activation re-points the
stable link with a temporary link and one rename, so there is never a
moment without a stable path and no old version is removed.
Only the new binary knows its compiled table, so the updater launches it
after a successful activation and the seed result goes into the receipt
alone. No seed is requested for an update that did not activate.
A record that fails after activation names the version that is now
active and says the record is incomplete, instead of reporting a plain
success or the version that ran before.
A stable link Baley manages closes the interrupted claim as an
update.failed, so it no longer blocks later checks and nothing is
fetched again. Any other state holds the claim for the owner.
baley update claims the installation, fetches only behind the new claim,
stops the heartbeat and completes before the new binary seeds itself, and
prints the receipt chosen from the recorded result. The seed step stays
hidden from help.
The owner's word resolves the held claim from what is installed now, as
an update.failed with the held claim's own facts, so the installation is
released and the next check can claim it. A check still running is
refused and nothing is recorded.
…th folders

A reused staged file now has to be runnable, the claim is confirmed before staging and again before activation, and the stable path is exchanged rather than renamed over, so an owner's file that appears in the gap is put back. Publication and activation each sync their folder. After a conflict the receipt and the record name what the stable path holds now, not the version seen at the start.
The stable path's binary may be newer than the server that starts it, so the
entry is a hidden `update detached` subcommand. It runs the same check as
`baley update` but with the detached claim, so a day already claimed is refused.
…ng on it

The check runs only when the owner turned updates.auto on, through the stable
path in its own process group with its output sent to the null device. The
server's standard output is the MCP channel, and an unread pipe would stall
the child. The server starts it and serves; it never waits.
…s SHA-256

It stages the version beside the old ones and points the stable path at it,
and refuses to replace anything at the stable path that Baley did not place.
verify_download is the one place #14 adds signature checks before a release.
The system design, its C4 blocks and the model they come from still called
the updater planned and the release source signed. Both now say what the
code does: unsigned development artifacts, with #14 owning signatures.
…tations

Design 0010 still called the staged-version folder planned. It now describes
the write-only folder list the library decision takes and that T15 wires in.
Citations into files this phase changed point at the lines that hold them.
…date as built

The roadmap still had T13 in progress and T14 planned, and the README said
installation was not built. Both now say what exists: an unsigned development
install script and updater, with signatures left to #14 and the wiring to T15.
… links and reuse

The download is now made mode 755 inside the scratch folder, so it is
never world-writable outside it under any umask. The stable path is
managed only when it links to exactly versions/<version>/baley with a
valid version, so a link through .. or an extra component is refused.
A new temporary link is confirmed to be a link to the staged binary
before cleanup may remove it. A staged file with the right digest is
reused only when its owner can run it.
…s curl configuration

curl reads ~/.curlrc unless -q is its first argument, so a header or
upload option set there would ride on both artifact requests. Design 0002
says those requests carry no project content or provider credentials, and
now the script keeps that.
…s records in the host interface design

The delivery sections said the installer, the updater, the recovery command and the versions folder were planned. They are built for unsigned development artifacts, so the design now names the source setting, the manifest, the order of claim, verify_download, staging, link exchange, completion and seed, the receipt, the failure codes and the folder protection, and leaves T15 to T17 and #14 what they still own.
…with its claims, events and source setting

Designs 0001 and 0003 still called the update check, its claims and events and its command planned. They now say the detached updater opens the store, that update.checked and update.failed are recorded without an install view, that reconciliation uses update.reconcile with an automatic and an owner actor, and that updates.source is the unsigned development artifact source with no default. Figure 2 and its caption show the updater and the manual command as built.
… the HST-R19 deadline in the host interface design

The versions folder sits outside the ledger home only in the default layout, a BALEY_HOME at or inside it would put the binaries under the home's read denials. Only an activation receipt prints the kept versions, and the detached update check runs with no timeout, so the blanket claims said more than the code does.
…naries

A home above the versions folder covers every staged binary, and a home
inside it covers one only when it is a version folder. The sentence had
the containment direction wrong.
…lestone counts

The phase's diff reviews filed #238 to #249, and land needs them on the
roadmap. The milestone counts were stale before that: Evidence now holds
#227 and #235 and 21 open issues.


T12 merged in #236 without reproducing the dropped-call case live, and the
issue now gives the live check to T17.
@crenshawdev
crenshawdev merged commit beba914 into main Oct 10, 2026
5 checks passed
@crenshawdev
crenshawdev deleted the robbie branch October 10, 2026 01:38
@crenshawdev crenshawdev mentioned this pull request Oct 13, 2026
17 tasks done
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant