Updating a Project
A built project is a frozen snapshot. vesl-nockup reaches it through several independent channels, and none of them auto-propagate — an update is pulled, not pushed. Cargo.lock freezes the Rust graph, the template files are your copies, and app.hoon is yours. A vesl-nockup release changes nothing in a built project until you reinstall the nockup-graft binary, run nockup package install, or run cargo update.
What an update reaches
| Channel | Carries | Reaches the project via |
|---|---|---|
nockup-graft binary | the graft composer | re-running cargo install --git … --bin nockup-graft |
zkvesl/vesl-graft package | the Hoon graft library under hoon/lib/ | nockup package install |
vesl template | scaffold files (Cargo.toml, build.rs, main.rs, app.hoon) | only nockup project init — never an existing project |
Bundled crates (vesl-core, vesl-hull, vesl-test) | the Rust SDK | git-deps in Cargo.toml, frozen by Cargo.lock until cargo update |
Pins (NOCK_PIN, VESL_CORE_PIN, VESL_WALLET_PIN) | the nockchain and vesl-core revs baked into a scaffold | template Cargo.toml revs, set once at scaffold time |
The vesl template channel never re-touches a project once it is scaffolded. The nockup-graft binary and the vesl-graft package update on separate schedules and are not version-locked — update both in the same pass so the composer and the manifests it reads stay in step.
The update sequence
Run from the project root, in order.
- Commit or stash all work.
nockup graft updaterewritesapp.hoon; an isolated diff is reviewable, a mixed one is not. - Snapshot live state. For a running deployment,
vesl-checkpoint::snapshotthe kernel before recompiling. See State & Snapshots. - Update the composer:bashDo this first —
cargo install --git https://github.com/zkvesl/vesl-nockup --bin nockup-graft --forcenockup graft updatecannot replace its own running binary, and stops with this instruction if the refreshed library needs a newer one. - Run the update:bashOne verb for the recomposition: it runs
nockup graft update hoon/app/app.hoonnockup package install, previews the recomposition with thenockup graft doctorhealth report, prompts for confirmation, then writes viainject --apply. Read the preview before pressingy— ahand-edited-blockfinding means a customization inside a banner pair is about to be overwritten.--yesskips the prompt for CI. - Reconcile
Cargo.toml. A pin bump changes the nockchain and vesl-core revs the project should resolve to. Thenockup package installthat step 4 runs re-applies vesl-graft's[[patches]]; confirm the[patch]block agrees, thencargo update -p vesl-corerather than a blanketcargo update. - Recompile:bash
./compile.shcompile.shwrapshooncand fails loud if hoonc exits 0 with no jam written. - Rebuild:
cargo +nightly build. The scaffold'sbuild.rsre-runsnockup graft doctor, so a residual finding shows up in the build output. - Resume. After a snapshot,
vesl-checkpoint::resumefrom the newout.jam, then re-poke to restore state — resume reinitializes graft state to per-graft defaults. - Re-run the lifecycle suite with
vesl-testto confirm the kernel still behaves.
nockup graft update absorbs the old install/preview/apply trio. Driving nockup graft inject --apply by hand still works when you want composition without the orchestrator.
Re-injection is not a merge
nockup graft inject owns the region between each :: graft-inject:<graft>:<marker>:begin and :end banner pair. Every --apply — and every nockup graft update — strips that region and re-emits it from the manifest. It does not merge.
Edits inside a banner pair are discarded
Domain code added at the :: nockup:* markers but outside any banner pair — causes, peeks, ?- arms — survives a re-inject untouched. Code typed between a graft's begin and end banners does not: the next re-injection overwrites it, whether or not the manifest changed.
The common trap is replacing a verification gate by editing the gate body inside a %settle-* arm. That arm lives inside the settle-graft:poke block, so the next re-injection reverts it to the default hash gate. Change the gate through [graft.gates] in the manifest instead — see Swapping a Gate.
nockup graft doctor flags a hand-edited block before it is lost — it runs on every cargo build (the scaffold's build.rs invokes it) and in nockup graft update's preview.
The inverse case — a local edit to a graft library file (<name>-graft.hoon) that leaves inject reporting injected 0/N; skipped … but ./compile.sh still bakes the edit into out.jam — is covered in Inject → Manifest SHA vs Library Edits. Manifest TOML edits drive re-inject; library Hoon edits drive re-compile only.
When an update breaks the build
Update-time failures are catalogued in Common Pitfalls. The ones an update specifically provokes:
| Symptom | Cause |
|---|---|
hoonc exits 0, no out.jam | a newer graft library pulled a Hoon import the project's frozen hoon/common, hoon/dat, or hoon/jams subset doesn't satisfy — refresh those trees from a vesl-nockup checkout |
hoonc fails mint-lost / -lost %<tag> | the composer and the manifests are out of step — re-run steps 3 and 4 together |
cargo build fails on ibig / UBig | a pin moved; the [patch] block no longer matches the nockchain rev vesl-core resolves to — realign it (step 7) |
poke returns Ok(vec![]), stderr slog: invalid cause | the kernel re-composed with a renamed cause-tag the hull still calls by its old name — update the hull, and guard future renames with assert_kernel_cause_tag! |
| post-resume pokes emit nothing | the snapshot predates a graft-composition change — see Manual Migration |
nockup graft inject / update errors manifest schema too new | the graft library declares a newer manifest schema than the installed nockup-graft — update the binary (step 3) and re-run |
See Also
- vesl-nockup README — Updating an existing project — the procedure's source of truth.
- State & Snapshots — snapshot and resume across a composition change.
- Inject — marker semantics and the per-graft sha256 banner.
- Common Pitfalls — symptom-led catalogue of build and runtime failures.