Host-owned updates¶
The modern host is the only update provider. The guest never searches the internet, chooses a channel, or accepts a host-selected replacement without a request. Publication and installation are separate decisions.
Text equivalent: the host validates its catalog before sending an offer; the guest compares exact identity and requests one build; the host transfers that MacBinary through the existing file lane; the guest verifies the stream and Finder identity before exchanging files. It then reports the outcome and either tears down and relaunches the application or tells the person that the Extension will become active only after restart.
Publication unit¶
tools/write-update-manifest.py writes an adjacent
.now-update.json sidecar for each canonical MacBinary. It contains the
component, release version, exact build identity, byte count, SHA-256, channel,
and signature state. A loose artifact without a valid sidecar may still serve
onboarding, but UpdateProvider will not advertise it. The provider reads the
normal onboarding catalog, then recomputes byte count and SHA-256 before every
catalog snapshot becomes an offer.
contract/product_version.h owns the host/PPC application-family release
version. The classic vers resource, Swift identity, Xcode marketing version,
and fallback host Info.plist remain checked copies because their build systems
cannot all consume the same C macro. Application build identity is a full
SHA-256 over stable paths, each build input's SHA-256, and the compiler/toolchain
profile. It contains no wall clock, so the same inputs are idempotent across
worktrees. The Extension uses the same scheme for publication while retaining
the first 160 bits in its shipped in-memory ABI. NOW-68K retains its separate
experimental deployment version.
The wire has a third identity: info.x-contract-revision in AsyncAPI, shared
through contract/wire_limits.h. It gates compatibility during hello and is
not a product or Extension release number. AsyncAPI's own info.version
versions the contract document; it may happen to equal a product release but no
gate treats that equality as an invariant.
Release version answers product ordering and display. Deterministic build identity answers which source/toolchain inputs; artifact SHA-256 answers which exact MacBinary bytes. A host development build with the same version and a different build ID is therefore a real offer rather than “already current.” An older host artifact is identified as older and cannot arm Install; a version difference is not permission to downgrade.
tools/product-version-gate keeps all checked product-version copies coherent
and prevents rollback. Main is an integration boundary, not automatically a
release boundary: two serial branches may land at one release version because
their content-derived build IDs do not depend on landing order. An intentional
release advances the version and qualifies numbered candidates from a
release/vVERSION branch. Candidate artifacts use annotated
now-product-vVERSION-rc.N or now-extension-vVERSION-rc.N tags and carry a
candidate update channel. Accepted candidate revisions receive final
annotated tags without the suffix. Every annotation pins the component, full
build ID, and artifact SHA-256. write-update-manifest.py refuses an
unnumbered candidate, moved or lightweight tag, dirty source tree, or different
bytes under that publication identity. RELEASING.md
owns the branch, qualification, and promotion procedure. tools/land-main is
the bootstrap-safe route when a branch introduces
a new main-ref gate: it runs the candidate gates before moving the ref from the
candidate worktree. When main is checked out in the shared checkout, as this
repository requires, the tool refuses tracked dirt and fast-forwards that
worktree in place; unrelated untracked lab material is preserved. If no
worktree has main, it advances the ref directly. The reference-transaction
hook then covers later merges, fast-forwards, git fetch . branch:main, and
forced local ref moves. Extension build inputs follow the parallel non-rollback
and exact shared-bake gate.
Transfer and install¶
The guest requests the exact offered build and artifact SHA-256. The host
requires both to still name its published artifact before serving it through
the existing file.offer and bulk lane. Update
receives cannot resume, cannot choose an arbitrary destination, and must match
the pending component, request id, exact offer, byte count, and digest.
The SHA-256 covers the raw MacBinary stream. After the existing receiver has
decoded and committed the classic file, the installer also checks Finder
identity: APPL/NOWo for the application and INIT/NOWx for the Extension.
- The application uses
FSpExchangeFilesagainst the running canonical file. The replacement takes the canonical place while the previous bytes remain at the staging name. The main loop exits normally, closes logging, and only then asks Process Manager to launch the canonical application. - The Extension is exchanged with the installed resident or renamed into its
canonical place. The retained old file is changed away from Finder type
INIT, so two residents cannot load at the next boot. Activation is never claimed until the person restarts the classic Mac.
The guest compares the active table's resident major/minor with the version it compiled against and warns when they differ. Capability bits, not version, still govern which resident planes the application may use.
Trust boundary¶
SHA-256 is integrity, not signing. Every generated manifest currently says
signed: false; the guest labels the offer unsigned and requires local modal
confirmation in Connections. The shared console/wire command can inspect these
offers but cannot start an unsigned install, so a remote command cannot spend
the person's consent. A future release-signing design needs a pinned trust root,
key rotation and recovery policy before that flag can become true.
The underlying classic wire remains plaintext and unauthenticated. Artifact signing will authenticate release bytes, not make the transport safe for an untrusted network.
Verification boundary¶
Native tests cover SHA-256, exact-build comparison, trust labels, provider validation, contract round trips, and critical source ordering. Guest cross-builds prove the Carbon and Toolbox APIs compile. Emulator acceptance must still prove exchange, clean relaunch, Extension replacement, restart activation, and rollback. Only physical hardware can make the result metal-verified.