boo-flash 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,302 +1,30 @@
1
- # Boo Flash
2
-
3
- A small macOS and Linux USB flasher for public qualified Boo releases and the repository
4
- owner's private GitHub candidate images.
5
- OpenTUI + Solid, using the welcome application's palette directly (the same
6
- 13 colors as the native installer). It is **not included in Boo images** and is
7
- the repository's supported flasher.
1
+ <p align="center">
2
+ <a href="https://os.boo">
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://os.boo/boo-logo-dark.svg">
5
+ <img src="https://os.boo/boo-logo-light.svg" width="184" height="96" alt="Boo">
6
+ </picture>
7
+ </a>
8
+ </p>
8
9
 
9
- ## Run
10
+ # Boo Flash
10
11
 
11
- The MIT-licensed npm package is `boo-flash` version `0.1.0`. Its tool version is
12
- independent of Boo image release versions such as `0.1.0+151`. Run it without a
13
- global install using either supported runtime:
12
+ Flash [Boo](https://os.boo) to a USB drive from macOS or Linux.
14
13
 
15
14
  ```sh
16
- npx boo-flash # Node.js 26.4 or later
17
- bunx boo-flash # Bun 1.4 or later
15
+ npx boo-flash
18
16
  ```
19
17
 
20
- The package contains JavaScript, not a bundled Node or Bun executable. Its small
21
- launcher selects a compatible Node installation first and supplies Node's required
22
- `--experimental-ffi`; otherwise it uses Bun. The npm artifact carries separate
23
- Node- and Bun-compiled Solid entry points plus the matching package-installed
24
- OpenTUI native library. On Linux the launcher detects musl before OpenTUI loads
25
- and selects its musl artifact; an explicit valid `OPENTUI_LIBC` remains authoritative.
26
-
27
- Production flashing needs `curl` and `zstd`; Candidates add
28
- `gh` (GitHub CLI) and `unzip`. macOS can offer to install missing content tools
29
- through Homebrew. Existing commands on PATH or in `/opt/homebrew/bin` and
30
- `/usr/local/bin` are used directly, without changing shell configuration. If
31
- Homebrew is missing, follow [brew.sh](https://brew.sh), then press R to refresh.
32
- Linux never chooses a distribution or changes system packages: install reported
33
- missing commands with the distribution's package manager, then refresh. On Linux,
34
- every command is resolved to a root-owned executable beneath root-owned,
35
- non-writable directories; user-writable PATH shims are rejected.
36
- Only the Candidates screen requires your ordinary `gh auth login` with access
37
- to `anomalyco/boo`; Boo Flash does not manage authentication. Historical
38
- candidate artifacts carrying the repository's former name remain valid.
39
- macOS additionally uses `diskutil`, `plutil`, `ioreg`, `stat`, `sudo`, `dd`, and
40
- `sync`. Linux additionally uses `lsblk`, `findmnt`, `udevadm`, `sudo`, `umount`,
41
- `dd`, `sync`, and `udisksctl` from UDisks2. Run from a trusted checkout as your
42
- ordinary user, not root.
43
-
44
- For development from a checkout, use pinned Bun **1.4.0**:
18
+ or
45
19
 
46
20
  ```sh
47
- cd tools/boo-flash
48
- bun install --frozen-lockfile
49
- bun start
21
+ bunx boo-flash
50
22
  ```
51
23
 
52
- Dependency installation requires one **Install** confirmation, runs without
53
- sudo, and hands the terminal to Homebrew for output and any prerequisite
54
- guidance. Ctrl-C cancels and quits, including when Homebrew handles the interrupt
55
- through its own terminal and reports exit 130 or SIGINT. Boo Flash waits for its
56
- owned brew process to close before exiting; Homebrew manages its own children
57
- and cleanup, not a guarantee that every descendant has exited. SIGINT/SIGTERM
58
- sent only to Boo Flash request quit but do not actively cancel brew: Boo Flash
59
- still waits for it to finish, with no forced deadline. During that handoff,
60
- Q/Escape are not TUI controls.
61
- It does not bootstrap Homebrew, upgrade existing tools, or change shell files.
62
- Homebrew may install/update dependencies needed by the requested formulae.
63
- On failure, quit to read Homebrew's terminal output, resolve its reported issue
64
- (such as Xcode Command Line Tools), then retry. Partial installs are not undone.
65
-
66
- Select the **target GPU**, not the Mac's CPU. All three profiles are x86_64 UEFI
67
- images, not ARM images. Arrow keys choose, Enter continues, Escape goes back
68
- or cancels work, R refreshes, Q stops and quits. No USB is initially selected.
69
- The erase screen defaults to **Go back**. There is no typed phrase or countdown.
70
-
71
- The three GPU choices use the release identified by
72
- `https://images.os.boo/channels/main.json` and show its Boo version. **Candidates** is
73
- an additional choice: it checks the latest ten successful completed
74
- `candidate.yml` runs and lists up to ten unexpired image artifacts, each with
75
- its actual GPU profile, built-source SHA, run number/date and run title.
76
- Choose one to continue directly to USB selection; candidates need not provide
77
- all three profiles. They are **build-only, not boot/install/hardware qualified**.
78
- If none are available, **Go back** returns to the main choices; R refreshes the
79
- candidate list. Authentication/network errors are reported, not shown as empty.
80
-
81
- After confirmation, only the selected production `.raw.zst` is downloaded from
82
- its immutable release path (or reused from the verified cache). Candidates keep
83
- their authenticated GitHub artifact ZIP flow. Candidate flashing needs space for
84
- both the ZIP and compressed image; the raw image is always streamed, not staged.
85
- After verification, the UI releases the terminal for an ordinary `sudo -v`
86
- prompt. No password or GitHub token is read by the application. macOS runs only
87
- `dd` as root. Linux runs checked non-forced unmounts, `dd`, and UDisks safe
88
- power-off through `sudo -n`. No privileged app process, custom daemon, shell, or
89
- shell command string is used.
90
-
91
- Read-only live production metadata discovery (with no image download or software
92
- installation):
93
-
94
- ```sh
95
- bun start --list-images
96
- ```
97
-
98
- ## Download Cache
99
-
100
- Verified production images and GitHub candidate ZIPs are kept across flashes
101
- and app restarts in
102
- `os.tmpdir()/boo-flash-cache-<UID>/`, privately owned by your normal user.
103
- The temporary parent is resolved normally, including platform symlinks. Each entry
104
- is `<build-or-artifact-ID>-<SHA256>.{raw.zst|zip}`, not a profile or `latest`
105
- alias. There is
106
- no automatic expiry or eviction, but the OS may purge temporary storage: this
107
- is not permanent storage or an offline image library. Discovery still needs
108
- the public Boo channel for production or GitHub authentication and a usable,
109
- unexpired artifact for candidates. Candidate source checks remain unchanged. No
110
- tokens, signed URLs, or release catalogs are cached.
111
-
112
- Only a download with matching expected size and SHA-256 is published, with an
113
- atomic exclusive hardlink. Existing completed bytes are never overwritten.
114
- A hit is privately hardlinked into operation staging and its owner, regular-file
115
- type, size and SHA-256 are checked again before the usual image validation.
116
- The UI says **Using cached image**, not downloading. Unsafe paths or corrupt payloads
117
- fail before disk writes, without silently deleting or redownloading them. Clear
118
- a corrupt payload and retry; unsafe ownership/permissions/symlinks require manual
119
- correction. If the temporary filesystem cannot hardlink safely, the operation
120
- reports an error before writing rather than pretending it retained the download.
121
- If publication fails, the verified payload stays in its private operation folder
122
- as `image.raw.zst` or `image.zip`; the error reports that path for manual
123
- recovery. It is not a cache hit location and is not automatically reused or
124
- removed by Clear. No authorization or disk write begins on publication failure.
125
-
126
- **Clear cached images** in the main menu defaults to **Go back**. It removes
127
- downloaded payloads only, never USB contents or active operation staging. Clear
128
- does not interrupt a pinned active flash; a pending download can publish afterward.
129
- For clearing without GitHub, authentication, dependency setup or UI discovery:
130
-
131
- ```sh
132
- bun start --clear-cache
133
- ```
134
-
135
- This explicit CLI action needs no confirmation. It reports the cache path and
136
- number removed. Demo mode never clears the real cache. Operation scratch and
137
- partial/unverified downloads are still cleaned when the operation finishes;
138
- verified cached payloads
139
- are retained even when subsequent validation/flashing fails or is cancelled.
140
-
141
- ## Image Contract
142
-
143
- - Fetch `https://images.os.boo/channels/main.json` without caching, require its
144
- exact version/revision/prefix contract, then fetch and cross-check the immutable
145
- `releases/VERSION/release.json`. Require exactly Intel, AMD and NVIDIA metadata
146
- with release filenames, compressed/raw sizes and SHA-256 digests.
147
- - Candidates instead require exact `boo-{intel|amd|nvidia}-candidate-{full_sha}`
148
- image artifacts, never producer artifacts. The artifact SHA is the built
149
- source, not the workflow's orchestration `head_sha`. The selected artifact
150
- ID, name, run, expiry, size and ZIP digest are rechecked before download/reuse;
151
- `build-info.json` must agree on revision, profile and candidate source identity.
152
- Listing downloads no image payloads and does not browse older pages.
153
- - Stream only the selected production image from `images.os.boo`, measuring its
154
- exact bytes and validating its release-manifest SHA-256. Candidates stream only
155
- the chosen ZIP through authenticated `gh api` and validate the API size/digest.
156
- - For candidates, extract only exact, unique root basenames: the chosen `.raw.zst`,
157
- `build-info.json`, `SHA256SUMS` and `SHA256SUMS.raw`. Stream file content into
158
- newly created private files; never unpack arbitrary archive paths or symlinks.
159
- - Require schema 1, semantic version, build number, combined version, revision,
160
- profile, exact `boo-VERSION-x86_64-PROFILE.raw.zst` release basename,
161
- compressed byte count/hash and positive raw size/hash. Candidate `build-info`
162
- additionally requires repository, clean source and checksum agreement;
163
- candidates retain their existing basename and carry no release build number,
164
- while candidates predating this metadata remain valid.
165
- - Check capacity against `raw_apparent_bytes`. Decompress with `zstd`, bound,
166
- count and hash the raw stream, and await both decompressor and writer status.
167
- Raw-stream verification finishes **after writing**, not before erasure, and
168
- is **not device read-back verification**. A failure cannot restore old data.
169
-
170
- The progress bar is per-stage real byte progress, not an overall ETA or a timed
171
- fill. Write progress means backpressured bytes delivered to `dd`, not durable
172
- device bytes. A separate spinner covers activity; closing the writer, `sync`,
173
- and the platform safe-removal operation must all succeed before the real success
174
- screen says the USB is removable.
175
-
176
- ## Disk Safety
177
-
178
- ### macOS
179
-
180
- Selection requires structured `diskutil -plist` data converted with `plutil`,
181
- a physical whole disk, explicit external (`Internal=false`) USB transport,
182
- writable media with 512-byte logical sectors, and a unique current IOMedia
183
- `IORegistryEntryID` from `ioreg`. Current Boo images use a 512-byte-sector GPT:
184
- 512e disks remain eligible, but 4096-byte logical-sector (4Kn) disks do not.
185
- Root, APFS System/Data, original user's home, working directory, app source,
186
- Bun executable, resolved runtime packages/native assets, `gh`/`zstd` binaries
187
- and image staging storage are protected.
188
- Real paths and macOS `stat -f %Sd` map files/firmlinks to their devices; APFS
189
- containers are followed to **all** physical stores, then whole physical disks.
190
- Unresolved protected mappings fail closed, including unsupported virtual/RAID
191
- storage. It never assumes that `disk0` is the system disk. External USB root
192
- storage is excluded too. System/Data installations and multi-store APFS
193
- containers are conservatively excluded even when not the running system.
194
-
195
- Attachment ID, size, device-tree path and media name are compared after the
196
- password prompt and again after checked, non-forced `unmountDisk` (which covers
197
- APFS virtual volumes too). Mounted target partitions/APFS volumes are rejected
198
- before writing. Paths are derived only from validated `diskN` identifiers;
199
- the UI cannot supply arbitrary device paths. Attachment is rechecked before
200
- eject. These are repeated checks, **not an atomic Disk Arbitration claim**:
201
- do not unplug, swap or mount devices while an operation is running.
202
-
203
- ### Linux
204
-
205
- Selection uses explicit-column `lsblk --json` topology. A target must be a
206
- parentless whole block device with USB transport, writable media, a positive
207
- size, and 512-byte logical sectors. The device path must be the direct validated
208
- `/dev/ID` path. Devices with active swap, device-mapper, RAID, LVM, encrypted,
209
- multipath, or any other non-partition descendants are excluded rather than
210
- partially dismantled. Mounted ordinary partitions remain selectable so desktop
211
- auto-mount does not hide a USB.
212
-
213
- Root, standard system mount paths, home, working directory, app source, Bun
214
- executable, resolved runtime packages/native assets, tools, and image staging are resolved to filesystem
215
- major:minor identities with `findmnt`. `lsblk` parent relationships are followed
216
- to every physical disk, including multi-parent stacks. Attached block devices
217
- configured in `/etc/fstab` are also protected; every attached match for duplicated
218
- `UUID`, `LABEL`, `PARTUUID`, or `PARTLABEL` tags is included. Protected filesystem UUIDs expand
219
- to every attached member, and every member of an active shared-UUID filesystem
220
- is excluded even when `lsblk` reports its mountpoint on only one member. Discovery
221
- uses the util-linux 2.37 `SOURCE` column, not newer `SOURCES`. Unknown backing fails
222
- closed rather than assuming it cannot be a target. A Linux installation running
223
- from external USB is therefore excluded without assuming a device name.
224
-
225
- The selected attachment is identified by major:minor, capacity, udev device path,
226
- block-node device/inode/change time, a hardware-backed `/dev/disk/by-id` link,
227
- optional udev path/serial, and model. USBs without a current by-id route
228
- are not offered. Kernel sysfs resolves each whole block device to its nearest
229
- physical USB parent; every LUN is excluded when safe power-off would affect a
230
- sibling block device. Identity and eligibility are rechecked after authorization, after unmount, and
231
- before safe removal. Linux unmounts every mounted target filesystem with
232
- `umount --all-targets`, without force or lazy detach, then requires all target
233
- mounts to be gone. It writes through the revalidated by-id route using
234
- `dd bs=4M conv=fsync`, runs `sync`, refreshes sudo authorization, removes any
235
- post-write automounts without force, and requires `udisksctl power-off` to succeed
236
- before claiming the drive is safely powered off. UDisks2 may reject hardware it
237
- cannot safely power off; its service and selected block object are checked during
238
- discovery, and a later power-off rejection is reported as an incomplete outcome
239
- rather than a false success.
240
-
241
- These repeated checks are not an atomic kernel claim. Do not unplug, swap, or
242
- mount devices while an operation is running.
243
-
244
- Cancellation stops the decompressor/input stream, closes the writer's stdin,
245
- and **waits for the writer to finish its finite buffered tail** before cleanup
246
- or exit. It does not kill the sudo supervisor and orphan a root writer. If a
247
- device/driver stalls, stopping can take time: keep the USB connected and wait.
248
- No force/lazy unmount, forced exit deadline, or automatic safe removal follows
249
- failure. Use the host platform's safe-removal control for an incomplete USB.
250
- SIGKILL, power loss and device removal cannot be made safe by application cleanup.
251
-
252
- ## Preview And Status
253
-
254
- ```sh
255
- bun demo
256
- bun start --demo=empty
257
- bun start --demo=error
258
- bun start --demo=setup
259
- bun start --demo=setup-gh
260
- bun start --demo=setup-zstd
261
- bun start --demo=setup-error
262
- bun start --demo=setup-interrupted
263
- bun start --demo=no-homebrew
264
- bun run typecheck
265
- bun run test
266
- bun run test:package
267
- ```
24
+ Choose the target computer’s GPU and USB drive. Boo Flash downloads and verifies the latest qualified image, then erases and writes the selected drive.
268
25
 
269
- Demo modes use the **same actual Solid UI**, with in-memory images, non-device
270
- IDs, simulated progress and outcomes. They never run disk commands, sudo, a
271
- writer, Homebrew, package installation or image downloads. The header always
272
- says `DEMO - no disks written`. Setup previews also label simulated installation.
273
- These options are for manual UI review, not a test harness.
274
- `setup-interrupted` simulates a Homebrew interruption result without signaling
275
- Boo Flash; after the handoff it should quit, not return to setup or load images.
276
- The card fills compact terminals and is bounded/centered on large ones; below
277
- 40 columns or 16 rows, choices are hidden and Enter cannot approve erasure.
26
+ Requires Node.js 26.4+ or Bun 1.4+.
278
27
 
279
- The Linux topology policy has focused automated fixtures for system-disk
280
- protection, mounted USBs, post-unmount checks, multi-parent backing storage,
281
- complex holders, swap, read-only/4Kn/non-USB disks, malformed inventory, unknown
282
- backing devices, and ambiguous aliases. Typecheck, live production discovery,
283
- read-only live Linux host inventory, exact npm contents, and clean packed
284
- `npx`/Node and `bunx`/Bun native-renderer startup/exit are also verified. The
285
- published runtime dependency tree has zero known npm audit findings. **No macOS or Linux
286
- hardware flash, real payload write, or physical safe removal has been qualified.**
287
- Dependency setup and its terminal handoff are likewise only simulated on Linux;
288
- real Homebrew installation and interruption on macOS remain unqualified.
289
- Review the source and qualify on an explicitly chosen disposable USB on each host
290
- platform before relying on it for hardware use.
28
+ ## License
291
29
 
292
- Platform references: [diskutil manual](https://keith.github.io/xcode-man-pages/diskutil.8.html),
293
- [stat manual](https://keith.github.io/xcode-man-pages/stat.1.html),
294
- [Apple ioreg source](https://github.com/apple-oss-distributions/IOKitTools/blob/main/ioreg.tproj/ioreg.c),
295
- [Apple registry ID contract](https://github.com/apple-oss-distributions/IOKitUser/blob/main/IOKitLib.h),
296
- [sudo process/exit contract](https://www.sudo.ws/docs/man/sudo.man/),
297
- [lsblk manual](https://man7.org/linux/man-pages/man8/lsblk.8.html),
298
- [findmnt manual](https://man7.org/linux/man-pages/man8/findmnt.8.html),
299
- [umount manual](https://man7.org/linux/man-pages/man8/umount.8.html), and
300
- [UDisks documentation](https://storaged.org/doc/udisks2-api/latest/).
301
- Dependency references: [Homebrew installation](https://docs.brew.sh/Installation),
302
- [install options/environment](https://docs.brew.sh/Manpage#install-options-formulacask-).
30
+ MIT © 2026 Anomaly
@@ -37,3 +37,27 @@ Redistributions in binary form must reproduce the above copyright notice, this l
37
37
 
38
38
  THIS IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS,
39
39
  EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
40
+
41
+ solid-js
42
+ --------
43
+ MIT License
44
+
45
+ Copyright (c) 2016-2025 Ryan Carniato
46
+
47
+ Permission is hereby granted, free of charge, to any person obtaining a copy
48
+ of this software and associated documentation files (the "Software"), to deal
49
+ in the Software without restriction, including without limitation the rights
50
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
51
+ copies of the Software, and to permit persons to whom the Software is
52
+ furnished to do so, subject to the following conditions:
53
+
54
+ The above copyright notice and this permission notice shall be included in all
55
+ copies or substantial portions of the Software.
56
+
57
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
58
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
59
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
60
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
61
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
62
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
63
+ SOFTWARE.