Skip to content

feat(update): real self-updater on macOS/Linux + macOS install docs + README quickstart#6

Merged
MichaelTaylor3d merged 3 commits into
mainfrom
feat/127-self-updater
Jul 6, 2026
Merged

feat(update): real self-updater on macOS/Linux + macOS install docs + README quickstart#6
MichaelTaylor3d merged 3 commits into
mainfrom
feat/127-self-updater

Conversation

@MichaelTaylor3d

Copy link
Copy Markdown
Contributor

TLDR

Makes digstore update a real in-place self-updater on macOS/Linux (#127), adds a clear macOS/Linux raw-binary install section (#126) and a canonical publish Quickstart to the README (#128). Closes #127, #126, #128.

#127 — digstore update self-installs

Previously on macOS/Linux update only PRINTED the release link and never installed (a live user ran it and stayed on the old version). Now:

  • detect OS+arch → select the correct release asset (arch-aware: macos-arm64 / macos-x64 / linux-x64 raw binaries; aarch64-unknown-linux-gnu.tar.gz for ARM Linux — prefers a raw binary, falls back to a tarball),
  • download (GitHub's browser_download_url verbatim), extract if it's a .tar.gz, sanity-check the binary magic (ELF / Mach-O / PE — guards against an HTML error page since no checksums are published),
  • chmod +x, clear the macOS Gatekeeper quarantine (xattr -d com.apple.quarantine),
  • atomically replace the running binary (temp file in the same dir → rename over current_exe(), resolved through symlinks so a Homebrew symlink keeps pointing at the updated target),
  • fail LOUD with manual steps (chmod/xattr/mv + installer link) when the install location isn't writable — never a silent no-op.
  • --check keeps the print-only behavior; bare update self-installs. Windows keeps its bundled-installer path.

Doubled-URL check (from the report): digstore does NOT construct download URLs — it uses GitHub's browser_download_url/html_url verbatim (pinned by a test). The reported v0.7.2g/v0.7.2 / arm64cos-arm64 was terminal line-wrapping, not a construction bug.

#126 — macOS/Linux install docs

README now has a "Raw binary (macOS / Linux)" section: which asset per CPU (arm64 vs x64), "no file extension is normal", the exact chmod +x + xattr -d com.apple.quarantine + move-to-PATH steps (Apple Silicon /opt/homebrew/bin, no sudo), and that permission denied is the exec-bit/quarantine (sudo is the wrong fix). The not-writable self-update error prints the same manual steps. (docs.dig.net install page updated in a companion PR. Apple notarization / Developer-cert signing is a needs-user follow-up left on #126.)

#128 — README Quickstart

The Quick start now leads with the canonical publish flow, verified against the shipped CLI (initadd -Aadd --discoverycommit -mpush origin), with dynamic per-capsule $DIG pricing wording (never flat 100), consistent with docs.dig.net.

Tests (TDD)

update module: OS/arch asset selection against the REAL release asset set (macos arm64/x64, linux x64 raw preferred over tarball, linux aarch64 tarball), binary-magic sanity check, tarball extraction (+ no-digstore-entry error), atomic self-replace against a TEMP file (exec bit set, temp cleaned up) — never the live binary, and the fail-loud path + the URL-verbatim guard. All 21 update tests pass; fmt + clippy -D warnings clean. Installed-binary update --check verified end-to-end against GitHub.

Deps

Adds flate2 + tar (pure-Rust, for ARM-Linux tarball extraction). Supply-chain audit unaffected (no advisories; bans is warn-only; crates.io source).

Version

0.8.0 → 0.9.0minor (new self-updater capability; backwards-compatible, --check unchanged).

Previously `digstore update` only PRINTED the release link on macOS/Linux and
never installed anything (a live user ran it and stayed on the old version).
Now it self-installs: detect OS+arch -> select the correct release asset ->
download -> (extract if it is a .tar.gz) -> sanity-check the binary magic ->
chmod +x -> clear the macOS Gatekeeper quarantine -> atomically rename over the
running binary (resolving symlinks so a Homebrew symlink keeps pointing at the
updated target). `--check` keeps the print-only behaviour; bare `update`
self-installs. Fails LOUD with manual steps (chmod/xattr/mv + installer link)
when the install location is not writable. Windows keeps its bundled-installer path.

Asset selection is arch-aware (macos-arm64 / macos-x64 / linux-x64 raw binaries;
linux aarch64 .tar.gz), preferring a raw binary and falling back to a tarball.
The printed download URL is GitHub's `browser_download_url` verbatim (the reported
"doubled URL" was terminal line-wrapping, not a construction bug — pinned by a test).

TDD: OS/arch asset selection against the real release asset set, binary-magic
sanity check, tarball extraction, and the atomic self-replace against a TEMP file
(never the live binary) — plus the URL-verbatim guard and the fail-loud path.

Refs #127
@MichaelTaylor3d
MichaelTaylor3d merged commit cad2662 into main Jul 6, 2026
10 checks passed
@MichaelTaylor3d
MichaelTaylor3d deleted the feat/127-self-updater branch July 6, 2026 18:38
MichaelTaylor3d added a commit that referenced this pull request Jul 10, 2026
… §20.1 self-cert)

digstore init now mints an empty store singleton on Chia (hard gate: seed ->
balance -> mint, all before any local scaffold) and the on-chain launcher id
becomes store_id. The embedded host BLS key is still generated and embedded as
the module PublicKey + trusted key for content signing and head authorization.

Because store_id is now the launcher id (not SHA-256(publisher key)),
verify_module_root drops the §20.1 self-certifying id==hash check. It KEEPS the
StoreId==requested check and the merkle-root recompute (tamper detection), and
clone/push/pull/tombstone verification authenticate the head/tombstones against
the module-embedded publisher key directly -- no weakening beyond removing the
id==hash binding. SECURITY.md updated (threat model, hardening bullet, new
residual #6 on chain-as-root-authority follow-up).

Co-Authored-By: Claude <noreply@anthropic.com>
MichaelTaylor3d added a commit that referenced this pull request Jul 10, 2026
Co-Authored-By: Claude <noreply@anthropic.com>
MichaelTaylor3d added a commit that referenced this pull request Jul 10, 2026
… (Wave-1 #5/#6/#13/#14)

Close the biggest journey gaps before a developer ever spends DIG. Everything in
this slice is FREE — no wallet, no chain, no spend — so a developer can scaffold,
preview through the real read path, and preview cost before the one paid step.

#5 `digstore new <template>` — a free, no-mint scaffolder (today the only on-ramp,
`init`, mints 100 DIG into an empty dir). Five templates embedded in the binary
(include_str!): static-site, vite-react, next-static, nft-drop, dapp-window-chia.
Each writes a runnable local project — a `dig.toml`, a starter app, and (dapp/NFT)
a `window.chia` usage example. Refuses a non-empty dir without --force; creates no
`.dig` and never touches chain.

#6 `digstore dev` — the local inner loop: build-on-save + serve the REAL dig://
read path locally (init_store → add → commit → host runtime → merkle-verify →
AES-GCM-decrypt, the same plumbing as compile/cat), with live reload + an injected
dev `window.chia` shim. So "renders under dev" == "renders after deploy". Poll-based
watcher (no notify dep); multi-thread tokio runtime so a page's asset burst doesn't
stall; SPA fallback for routes but a true 404 for missing assets; shims injected
only into HTML at request time (never baked into the served bytes).

#13 `digstore doctor` + `commit --dry-run` — `doctor` runs pre-publish checks
(seed present/unlocked, funds vs 100 DIG + XCH fee, dighub login, default remote
reachable, content dir exists) as pass/fail, exits non-zero on a hard fail, never
spends. `commit --dry-run` computes + prints the resulting version (root) and the
exact DIG/XCH cost with NO seed unlock, NO wallet scan, NO anchor, NO finalize.

#14 task-first CLI help — one-line `about` strings lead with the task (commit =
"Publish your staged files as a new version"); the commit success line is now a
plain "Published a new version — it's live and permanent." with the capsule id
below it and the module/coin protocol detail behind --verbose.

Shared `dig.toml` reader extracted to `dig_toml::DigToml` (deploy + dev + doctor;
removes deploy's private copy). Templates/assets embedded via include_str!.

TDD: failing tests first throughout. 178 lib tests + new integration suites
(cli_new, cli_dev, cli_doctor, cli_commit_dryrun, cli_help additions) green
against the installed binary; cargo fmt + clippy -D warnings clean. Guest wasm
built and binary reinstalled per digstore discipline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GjQgGAisBupqs7vpkmUhuN
MichaelTaylor3d added a commit that referenced this pull request Jul 10, 2026
… README quickstart (#6)

* feat(update): make `digstore update` a real self-updater on macOS/Linux

Previously `digstore update` only PRINTED the release link on macOS/Linux and
never installed anything (a live user ran it and stayed on the old version).
Now it self-installs: detect OS+arch -> select the correct release asset ->
download -> (extract if it is a .tar.gz) -> sanity-check the binary magic ->
chmod +x -> clear the macOS Gatekeeper quarantine -> atomically rename over the
running binary (resolving symlinks so a Homebrew symlink keeps pointing at the
updated target). `--check` keeps the print-only behaviour; bare `update`
self-installs. Fails LOUD with manual steps (chmod/xattr/mv + installer link)
when the install location is not writable. Windows keeps its bundled-installer path.

Asset selection is arch-aware (macos-arm64 / macos-x64 / linux-x64 raw binaries;
linux aarch64 .tar.gz), preferring a raw binary and falling back to a tarball.
The printed download URL is GitHub's `browser_download_url` verbatim (the reported
"doubled URL" was terminal line-wrapping, not a construction bug — pinned by a test).

TDD: OS/arch asset selection against the real release asset set, binary-magic
sanity check, tarball extraction, and the atomic self-replace against a TEMP file
(never the live binary) — plus the URL-verbatim guard and the fail-loud path.

Refs #127

* docs(readme): macOS/Linux raw-binary install steps (#126) + canonical publish Quickstart (#128)

* style: cargo fmt update.rs (#127 self-updater)

Co-Authored-By: Claude <noreply@anthropic.com>
MichaelTaylor3d added a commit that referenced this pull request Jul 10, 2026
… §20.1 self-cert)

digstore init now mints an empty store singleton on Chia (hard gate: seed ->
balance -> mint, all before any local scaffold) and the on-chain launcher id
becomes store_id. The embedded host BLS key is still generated and embedded as
the module PublicKey + trusted key for content signing and head authorization.

Because store_id is now the launcher id (not SHA-256(publisher key)),
verify_module_root drops the §20.1 self-certifying id==hash check. It KEEPS the
StoreId==requested check and the merkle-root recompute (tamper detection), and
clone/push/pull/tombstone verification authenticate the head/tombstones against
the module-embedded publisher key directly -- no weakening beyond removing the
id==hash binding. SECURITY.md updated (threat model, hardening bullet, new
residual #6 on chain-as-root-authority follow-up).

Co-Authored-By: Claude <noreply@anthropic.com>
MichaelTaylor3d added a commit that referenced this pull request Jul 10, 2026
Co-Authored-By: Claude <noreply@anthropic.com>
MichaelTaylor3d added a commit that referenced this pull request Jul 10, 2026
… (Wave-1 #5/#6/#13/#14)

Close the biggest journey gaps before a developer ever spends DIG. Everything in
this slice is FREE — no wallet, no chain, no spend — so a developer can scaffold,
preview through the real read path, and preview cost before the one paid step.

#5 `digstore new <template>` — a free, no-mint scaffolder (today the only on-ramp,
`init`, mints 100 DIG into an empty dir). Five templates embedded in the binary
(include_str!): static-site, vite-react, next-static, nft-drop, dapp-window-chia.
Each writes a runnable local project — a `dig.toml`, a starter app, and (dapp/NFT)
a `window.chia` usage example. Refuses a non-empty dir without --force; creates no
`.dig` and never touches chain.

#6 `digstore dev` — the local inner loop: build-on-save + serve the REAL dig://
read path locally (init_store → add → commit → host runtime → merkle-verify →
AES-GCM-decrypt, the same plumbing as compile/cat), with live reload + an injected
dev `window.chia` shim. So "renders under dev" == "renders after deploy". Poll-based
watcher (no notify dep); multi-thread tokio runtime so a page's asset burst doesn't
stall; SPA fallback for routes but a true 404 for missing assets; shims injected
only into HTML at request time (never baked into the served bytes).

#13 `digstore doctor` + `commit --dry-run` — `doctor` runs pre-publish checks
(seed present/unlocked, funds vs 100 DIG + XCH fee, dighub login, default remote
reachable, content dir exists) as pass/fail, exits non-zero on a hard fail, never
spends. `commit --dry-run` computes + prints the resulting version (root) and the
exact DIG/XCH cost with NO seed unlock, NO wallet scan, NO anchor, NO finalize.

#14 task-first CLI help — one-line `about` strings lead with the task (commit =
"Publish your staged files as a new version"); the commit success line is now a
plain "Published a new version — it's live and permanent." with the capsule id
below it and the module/coin protocol detail behind --verbose.

Shared `dig.toml` reader extracted to `dig_toml::DigToml` (deploy + dev + doctor;
removes deploy's private copy). Templates/assets embedded via include_str!.

TDD: failing tests first throughout. 178 lib tests + new integration suites
(cli_new, cli_dev, cli_doctor, cli_commit_dryrun, cli_help additions) green
against the installed binary; cargo fmt + clippy -D warnings clean. Guest wasm
built and binary reinstalled per digstore discipline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GjQgGAisBupqs7vpkmUhuN
MichaelTaylor3d added a commit that referenced this pull request Jul 10, 2026
… README quickstart (#6)

* feat(update): make `digstore update` a real self-updater on macOS/Linux

Previously `digstore update` only PRINTED the release link on macOS/Linux and
never installed anything (a live user ran it and stayed on the old version).
Now it self-installs: detect OS+arch -> select the correct release asset ->
download -> (extract if it is a .tar.gz) -> sanity-check the binary magic ->
chmod +x -> clear the macOS Gatekeeper quarantine -> atomically rename over the
running binary (resolving symlinks so a Homebrew symlink keeps pointing at the
updated target). `--check` keeps the print-only behaviour; bare `update`
self-installs. Fails LOUD with manual steps (chmod/xattr/mv + installer link)
when the install location is not writable. Windows keeps its bundled-installer path.

Asset selection is arch-aware (macos-arm64 / macos-x64 / linux-x64 raw binaries;
linux aarch64 .tar.gz), preferring a raw binary and falling back to a tarball.
The printed download URL is GitHub's `browser_download_url` verbatim (the reported
"doubled URL" was terminal line-wrapping, not a construction bug — pinned by a test).

TDD: OS/arch asset selection against the real release asset set, binary-magic
sanity check, tarball extraction, and the atomic self-replace against a TEMP file
(never the live binary) — plus the URL-verbatim guard and the fail-loud path.

Refs #127

* docs(readme): macOS/Linux raw-binary install steps (#126) + canonical publish Quickstart (#128)

* style: cargo fmt update.rs (#127 self-updater)

Co-Authored-By: Claude <noreply@anthropic.com>
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