buninu-linux 0.2.0 โ†’ 0.3.0

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
@@ -22,84 +22,224 @@
22
22
  + **BUNinu Is Not Unix** ๐Ÿฎ
23
23
  + **ๅนซไฝ ็‰›** ๐Ÿ‚ ใƒป **Bunใซๅ…ฅ้ญ‚** ๐Ÿ”ฅ
24
24
  - <img src="https://raw.githubusercontent.com/jjtseng93/buninu/main/icon.png" width="256">
25
+
26
+ ---
27
+
25
28
  - Still in the early stages
26
- * Built & tested on Android Termux QEMU (PRoot+Native)
27
- * Booted successfully on real x86-64 UEFI hardware with `--real`: Bun reached
28
- its interactive REPL on the local display and keyboard. Physical networking
29
- is not implemented yet, but the bundled `jmi` editor and local JavaScript
30
- execution work.
31
- * Built & booted in a cloud VM provided by ChatGPT Work mode, running Ubuntu 24.04.3 LTS on x86-64 with QEMU 8.2.2, TCG, and OVMF. Bun 1.4.2 was verified running as PID 1, and the Buninu userspace and bunmsh started successfully.
32
-
33
- - The hello-world EFI application in Section 1 is the starting point that the UKI replaces
29
+ - [Video here](https://www.reddit.com/r/bun/comments/1wkpraj/buninu_linux_a_distro_with_bun_as_pid_1): Booted successfully on real x86-64 UEFI hardware with `--real`:
30
+ * Bun reaches its interactive REPL
31
+ * start() starts the Buninu userspace shell
32
+ * `cfg.disk` + shell `mount` mounts local disks
33
+ * `cfg.net` loads common wired NIC drivers
34
+ * IP addresses and routes are configured manually because the image does not yet include a DHCP client.
35
+ * The bundled `jmi` editor, `jsmdcui` app runtime, and local JavaScript execution also work.
36
+
37
+ ---
38
+
39
+ - The hello-world EFI application in [Section 1](#1-hello-world) is the starting point that the UKI replaces
34
40
  - It still builds, and it is the quickest way to check whether the disk image and firmware path work at all.
35
41
 
36
42
  ## Quick start
37
43
 
38
- - Two shells are involved, sharing one checkout:
39
- * Build tools in Debian 13 PRoot,
40
- * QEMU in native Termux.
41
- - Clone into the Termux home
42
- * proot-distro exposes that directory inside the guest at the same absolute path
43
- * So both shells work on the same files
44
- * (`~` differs between them, the full path does not)
44
+ - This guide builds a bootable `buninu-linux-<version>.img`
45
+ - The documented build environment is Debian 13 under Termux PRoot
46
+ - A regular Debian installation works as well.
47
+ - For a source checkout, clone into the Termux home so native Termux and PRoot
48
+ can share it; `~` differs between them, but the absolute path is the same.
45
49
 
46
50
  ### Steps
47
- #### in native Termux
48
- - pkg install qemu-system-x86-64
51
+
52
+ - Before anything, install Bun in Debian first:
53
+
54
+ ```sh
55
+ apt update
56
+ apt install curl unzip
57
+ curl -fsSL https://bun.sh/install | bash
58
+ ```
59
+
60
+ - Small tip: The below `apt install` command shows in the terminal when you run -b without the needed tools
61
+
62
+ #### Build directly with bun x
63
+
64
+ Install the build tools, enter a directory where
65
+ you want to keep the finished image, then run the published package:
66
+
67
+ ```sh
68
+ apt install binutils-mingw-w64-x86-64 cpio curl dosfstools fakeroot mtools parted unzip
69
+
70
+ mkdir -p /data/data/com.termux/files/home/buninu-build
71
+ cd /data/data/com.termux/files/home/buninu-build
72
+
73
+ $HOME/.bun/bin/bun x buninu-linux --version
74
+
75
+ $HOME/.bun/bin/bun x buninu-linux -fb --real --export
76
+ ```
77
+
78
+ `--export` copies the completed `vda.img` out of bunx's package directory and
79
+ into the directory where the command was invoked, as
80
+ `buninu-linux-<version>.img`. Use the exact exported filename printed by the
81
+ command when writing the USB drive.
82
+
83
+ #### Or build from a source checkout
84
+
85
+ In native Termux:
86
+
49
87
  - git clone https://github.com/jjtseng93/buninu-linux.git ~/buninu-linux
50
88
 
51
- #### in PRoot (Debian 13)
89
+ In Debian 13 under PRoot:
90
+
52
91
  - apt install binutils-mingw-w64-x86-64 cpio curl dosfstools fakeroot mtools parted unzip
53
92
  - cd /data/data/com.termux/files/home/buninu-linux
54
- - bun ./index.js -fb
55
- * fetch ~80 MB from Alpine/Bun & build vda.img
93
+ - bun ./index.js -fb --real --export
94
+ * Fetches from Alpine/Bun
95
+ * Selects the physical-hardware modules
96
+ * Builds `vda.img`
97
+ * Exports it to `buninu-linux-<version>.img`
56
98
 
57
- #### back in native Termux
58
- - cd ~/buninu-linux
59
- - bun ./index.js -r
60
- * boot it
99
+ #### Write the image to a USB drive
61
100
 
62
- `index.js` runs under bun or node. A successful boot ends like this, with a
63
- REPL on the serial console:
101
+ Write the complete exported image (or `vda.img` when not using `--export`), not
102
+ the FAT partition inside it, to a USB drive. The source filename below is an
103
+ example; replace it with the exact name printed by the build. The destination
104
+ is also only an example: identify the correct whole-disk device first, because
105
+ this overwrites it completely.
64
106
 
65
- ```text
66
- Run /bin/bun as init process
67
- init.js: Bun runtime entered JavaScript
68
- init.js: loading physical musl libc for FFI
69
- init.js: mounted /proc, /sys, /dev, /tmp
70
- network: lo 127.0.0.1/8
71
- network: eth0 10.0.2.15/24 via 10.0.2.2
72
- fetch example.com: HTTP 200, text/html
107
+ ```sh
108
+ sudo dd if='buninu-linux-<version>.img' of=/dev/sdX bs=4M conv=fsync status=progress
109
+ ```
73
110
 
111
+ #### Booting from the USB drive
112
+
113
+ Boot that drive as x86-64 UEFI media. The UKI is unsigned, so disable Secure
114
+ Boot unless you sign it yourself. A successful physical boot reaches the local
115
+ display and USB keyboard with a welcome ending like this:
116
+
117
+ ```text
74
118
  Welcome to Buninu Linux!
75
119
  Bun 1.4.2 is now PID 1
76
-
77
120
  Type start() to run buninu --local
121
+ Configuration getters: cfg.all, cfg.disk, cfg.net, cfg.power
122
+
78
123
  bun-repl>
79
124
  ```
80
125
 
81
- Ctrl-C stops QEMU. After editing `initramfs/init.js`, `bun ./index.js -b` in
82
- PRoot rebuilds the image; `-h` lists the flags. Section 0 explains each
83
- dependency, Section 2 the build and how PID 1 works.
126
+ If this welcome is not visible, later kernel messages may simply have scrolled
127
+ over it on the same console. Press Enter at an empty REPL prompt; an empty or
128
+ whitespace-only line prints the welcome and current `cfg.*` getter list again.
129
+
130
+ Load only the subsystem needed, then enter the Buninu shell:
131
+
132
+ ```js
133
+ cfg.net // wired NIC drivers
134
+ cfg.disk // SATA/NVMe/USB storage drivers and detected block devices
135
+ cfg.power // battery, AC, button and thermal drivers
136
+ start()
137
+ ```
138
+
139
+ There is no DHCP client yet; configure a detected wired interface with `ip` as
140
+ described under [Real hardware](#real-hardware). Run `poweroff` for a synced
141
+ shutdown. After editing the initramfs, `bun ./index.js -b --real --export` rebuilds the
142
+ image; `-h` lists all flags.
143
+
144
+ ## Running
84
145
 
85
- ## 0. Install dependencies
146
+ ### Real hardware
86
147
 
87
- - Building Buninu Linux (Section 2) โ€” needs no compiler:
88
- - Bun ships as a prebuilt binary and the UKI is assembled with `objcopy`.
148
+ Real hardware is the primary target. Boot the USB drive with Secure Boot
149
+ disabled unless you have signed the UKI yourself. The physical display and USB
150
+ keyboard provide the `bun-repl>` prompt.
151
+
152
+ Load only the hardware subsystem you need:
153
+
154
+ ```js
155
+ cfg.net
156
+ cfg.disk
157
+ cfg.power
158
+ ```
159
+
160
+ `cfg.net` loads the packaged wired-network drivers and prints detected
161
+ interfaces and MAC addresses. There is no DHCP client yet, so configure the
162
+ interface manually after entering the Buninu shell with `start()`:
89
163
 
90
164
  ```sh
91
- apt install binutils-mingw-w64-x86-64 cpio curl dosfstools fakeroot mtools parted unzip
165
+ ip link set eth0 up
166
+ ip addr add 192.168.1.50/24 dev eth0
167
+ ip route add default via 192.168.1.1
168
+ ```
169
+
170
+ The real image initializes `/etc/resolv.conf` with `1.1.1.1` and `8.8.8.8`;
171
+ replace them if your network requires different DNS servers. `cfg.disk` loads
172
+ common SATA/PATA/SCSI, NVMe/VMD and USB-storage drivers so detected disks and
173
+ partitions appear in `/dev`. To inspect a filesystem without mounting it:
174
+
175
+ ```sh
176
+ mount -fv /dev/sda1 /mnt
177
+ ```
178
+
179
+ Run `poweroff` for a synced shutdown.
180
+
181
+ The bundled `jmi` terminal editor works without a network connection:
182
+
183
+ ```sh
184
+ jmi hlw.js
185
+ ```
186
+
187
+ - The recommended `jmi` color theme is `cmc-tc`
188
+ - Set it by:
189
+ * Press `Ctrl-E`
190
+ * Type `theme ` (including the trailing space)
191
+ * Press `Tab`, select it with up/dn keys
192
+ * Press `Enter`.
193
+
194
+ - Enter and save(Ctrl-S) this content:
195
+
196
+ ```js
197
+ console.log("Hello world from real hardware");
92
198
  ```
93
199
 
94
- - A C toolchain is only needed for:
95
- * The hello world .EFI (Section 1)
96
- + `hello/hello.c`
97
- * The retired bootstrap in `test/build-init-bootstrap.sh`
200
+ After leaving the editor with `Ctrl-Q`, run it locally:
98
201
 
99
202
  ```sh
100
- apt install clang lld
203
+ bun hlw.js
101
204
  ```
102
205
 
206
+ - Sometimes `Enters` in some scenarios don't directly work; if that's the case, try `Ctrl-J` or `Ctrl-M`
207
+
208
+ ## Commands inside /bin
209
+
210
+ Besides `bun` (and `sh`/`node` pointing at it), `/bin` includes four commands
211
+ implemented as Bun scripts:
212
+
213
+ | command | does | manual |
214
+ | --- | --- | --- |
215
+ | `mount` | `mount(2)` with type detection, `-o` parsing, `LABEL=`/`UUID=`, bind/move/remount; loads required filesystem and disk modules | `mount --help` โ†’ `/usr/share/doc/buninu-linux/mount.md` |
216
+ | `umount` | `umount2(2)` with `-l`, `-f`, `-R` | `umount --help` |
217
+ | `ip` | iproute2 grammar over `SIOC*` ioctls and `/proc/net`: `link`, `addr`, `route`, `neigh` | `ip --help` |
218
+ | `poweroff` | sync pending writes and power off through the Linux reboot system call | `poweroff --help` |
219
+
220
+ `--help` renders each Markdown manual in the terminal. Common examples:
221
+
222
+ ```sh
223
+ mount /dev/sda1 /mnt --mkdir
224
+ mount -fv /dev/sda1 /mnt
225
+ mount -t tmpfs -o size=64M tmpfs /tmp/x
226
+ mount -t ntfs3 -o force /dev/sda3 /mnt/windows
227
+
228
+ ip link set eth0 up
229
+ ip addr add 192.168.1.50/24 dev eth0
230
+ ip route add default via 192.168.1.1
231
+
232
+ ip -br addr && ip route
233
+ ip addr replace 192.168.1.50/24 dev eth0
234
+ ip route replace default via 192.168.1.1
235
+
236
+ poweroff
237
+ ```
238
+
239
+ ## Environment and dependencies
240
+
241
+ ### Build environment
242
+
103
243
  The baseline is the stock Debian 13 (trixie) OCI image, which is also what this
104
244
  PRoot is; a dependency is anything that image does not already have.
105
245
  `debian:13` and `debian:13-slim` carry the same 78 packages and identical
@@ -110,7 +250,16 @@ locales โ€” so `debian:13-slim` is the better starting point at 105 MB against
110
250
  The base image already provides `bash`, GNU `tar`, `gzip`, `sha256sum`,
111
251
  `mknod`, `find`, `sort`, `mkdir`, `mktemp`, `cp`, `rm`, `chmod`, `truncate`,
112
252
  `dd`, `printf`, `awk` and `dirname`, all used by these scripts as they are.
113
- Everything else has to be added. For Section 2:
253
+ Everything else has to be added.
254
+
255
+ ### Buninu Linux ([Section 2](#2-buninu-linux-bun-as-pid-1))
256
+
257
+ Buninu Linux needs no compiler: Bun is downloaded as a prebuilt binary and the
258
+ UKI is assembled with `objcopy`.
259
+
260
+ ```sh
261
+ apt install binutils-mingw-w64-x86-64 cpio curl dosfstools fakeroot mtools parted unzip
262
+ ```
114
263
 
115
264
  * **`binutils-mingw-w64-x86-64`** โ€” `x86_64-w64-mingw32-objcopy` appends the
116
265
  `.osrel`, `.cmdline`, `.linux` and `.initrd` sections to the EFI stub, and
@@ -130,30 +279,35 @@ Everything else has to be added. For Section 2:
130
279
  * **`parted`** โ€” writes the protective MBR, the primary and backup GPT, and the
131
280
  ESP partition entry with its type GUID.
132
281
  * **`unzip`** โ€” extracts the `bun` binary from the release zip.
282
+ * **`bun`** (or **`node`**) โ€” runs `index.js` in the build environment.
283
+ + Under bun, `--readme` renders the Markdown; under node it is printed as-is.
284
+
285
+ ### Hello world ([Section 1](#1-hello-world))
133
286
 
134
- To boot, from native Termux rather than PRoot:
287
+ The hello-world EFI application needs the C/PE toolchain plus the shared disk
288
+ image tools:
135
289
 
136
- * **`qemu-system-x86_64`** โ€” not an `apt` dependency: `pkg install
137
- qemu-system-x86-64` provides it together with the OVMF firmware at
138
- `$PREFIX/share/qemu/edk2-x86_64-code.fd`.
139
- * **`bun`** (or `node`) โ€” runs `index.js`; needed in both shells. Install bun
140
- from <https://bun.sh>, or `pkg install nodejs` / `apt install nodejs`. Under
141
- bun, `--readme` renders the Markdown; under node it is printed as-is.
290
+ ```sh
291
+ apt install clang lld dosfstools mtools parted
292
+ ```
142
293
 
143
- For Section 1 and the retired bootstrap only:
294
+ * **`clang`** โ€” compiles `hello/hello.c` to a PE32+ object with
295
+ `--target=x86_64-pc-win32-coff`. It resolves to `clang-19` on trixie.
296
+ * **`lld`** โ€” provides `lld-link`, which links `hello/hello.obj` into an EFI
297
+ application with `/subsystem:efi_application`. It resolves to `lld-19` on
298
+ trixie.
299
+ * **`dosfstools`**, **`mtools`** and **`parted`** โ€” create the FAT ESP and wrap
300
+ it in the GPT `vda.img` through the shared `build-image.sh`.
144
301
 
145
- * **`clang`** *(Section 1 only)* โ€” compiles `hello/hello.c` to a PE32+ object
146
- (`--target=x86_64-pc-win32-coff`) and, in `test/build-init-bootstrap.sh`, the
147
- freestanding x86-64 `/init`. Nothing in the Bun path compiles anything.
148
- Resolves to `clang-19` on trixie.
149
- * **`lld`** *(Section 1 only)* โ€” `lld-link` links `hello/hello.obj` into an EFI
150
- application (`/subsystem:efi_application`), and the same package backs
151
- `clang -fuse-ld=lld` for the bootstrap. Resolves to `lld-19`.
302
+ ### Retired native bootstrap
152
303
 
153
- `parted`, `dosfstools` and `mtools` back `build-image.sh`, which both sections
154
- use.
304
+ The retired `test/build-init-bootstrap.sh` also needs `clang lld`. It compiles
305
+ the historical freestanding x86-64 `/init`; the current Buninu Linux path does
306
+ not use or compile it.
155
307
 
156
- ## 1. Hello world
308
+ ## Implementation details
309
+
310
+ ### 1. Hello world
157
311
 
158
312
  - `hello/hello.c` is a freestanding PE32+ EFI application installed at the
159
313
  removable media fallback path `EFI/BOOT/BOOTX64.EFI`.
@@ -162,67 +316,72 @@ use.
162
316
  ```sh
163
317
  ./hello/build-hello.sh # in PRoot
164
318
  ./build-image.sh # in PRoot
165
- bun ./index.js -r # in native Termux
319
+ sudo dd if=vda.img of=/dev/sdX bs=4M conv=fsync status=progress
166
320
  ```
167
321
 
168
- It writes `Hello world from x64 UEFI!` to the UEFI console. The included OVMF
169
- firmware exposes that console over COM1 and `run-qemu.sh` attaches COM1 to the
170
- current terminal, so the message is visible without a graphical display. The
171
- application waits after printing; press any key to return to the OVMF
172
- interface, then Ctrl-C to stop QEMU.
322
+ After verifying `/dev/sdX` is the whole destination USB device, boot it on the
323
+ physical x86-64 UEFI machine. It writes `Hello world from x64 UEFI!` to the
324
+ firmware console and waits; press any key to return to the firmware interface.
173
325
 
174
- `build-image.sh` is shared with Section 2 โ€” see [Disk layout](#disk-layout).
326
+ `build-image.sh` is shared with [Section 2](#2-buninu-linux-bun-as-pid-1) โ€” see [Disk layout](#disk-layout).
175
327
 
176
- ## 2. Buninu Linux โ€“ Bun as PID 1
328
+ ### 2. Buninu Linux โ€“ Bun as PID 1
177
329
 
178
330
  An unsigned x64 Unified Kernel Image assembled from official Alpine packages,
179
331
  with Bun as the init process, replaces the hello-world application:
180
332
 
181
333
  ```sh
182
- bun ./index.js -fb # in PRoot
183
- bun ./index.js -r # in native Termux
334
+ # physical-machine image, in PRoot Debian
335
+ bun ./index.js -fb --real --export
336
+
337
+ # bun x buninu-linux -fb --real --export
184
338
  ```
185
339
 
186
- `index.js` is the single entry point (`npm run build` / `npm run pack` /
187
- `npm start` call it too). It has three stage flags that combine in any order
188
- and always run as fetch โ†’ build โ†’ run:
340
+ `index.js` is the single entry point (`npm run fetch` and `npm run pack` call
341
+ it too). Its physical-image stages run as fetch โ†’ build:
189
342
 
190
343
  | flag | runs | use it when |
191
344
  | --- | --- | --- |
192
345
  | `-f`, `--fetch` | `fetch-alpine.sh`, `fetch-bun.sh` | first clone, or after bumping a pinned version |
193
346
  | `-b`, `--build` | `build-uki.sh`, `build-image.sh` | after editing `initramfs/init.js` or the kernel command line |
194
- | `-r`, `--run` | `run-qemu.sh` | to boot what is there |
347
+
348
+ `--export` is a post-build option: after `-b` finishes, it copies `vda.img` to
349
+ the directory where the command was invoked as
350
+ `buninu-linux-<version>.img`. It therefore requires `-b`/`--build` and is the
351
+ recommended way to retain an image produced through npx/bunx:
352
+
353
+ ```sh
354
+ bun x buninu-linux -fb --real --export
355
+ ```
195
356
 
196
357
  `--linux-lts` selects Alpine's general-purpose LTS kernel. `--real` implies
197
358
  `--linux-lts` and builds for physical hardware: it makes `tty0` the primary
198
- console and includes the xHCI/USB HID modules needed by a typical USB keyboard.
359
+ console and includes xHCI/USB HID, common wired network, storage and ACPI power
360
+ modules needed by typical laptops and desktops.
199
361
 
200
- `-fbr` is the whole pipeline, `-br` is the edit-and-boot loop, and anything
201
- after `--` is handed to `qemu-system-x86_64` verbatim (`-r -- -m 1G`). Before
202
- starting, it checks that every tool the chosen stages need is on `PATH` and
203
- lists what is missing instead of failing halfway; fetch and build want the
204
- PRoot toolchain from Section 0, run wants Termux's QEMU. The shell scripts are
205
- what actually does the work and each still runs on its own.
362
+ For real hardware, `-fb --real` is the complete image pipeline and
363
+ `-b --real` is the edit-and-rebuild loop. Before starting, `index.js` checks
364
+ that every required tool is on `PATH` and lists what is missing instead of
365
+ failing halfway. The shell scripts do the actual work and each still runs on
366
+ its own.
206
367
 
207
368
  ```text
208
- index.js entry point: -f / -b / -r / --linux-lts / --real
369
+ index.js entry point: -f / -b / --export / --linux-lts / --real
209
370
  fetch-alpine.sh [fetch] kernel, EFI stub, musl, network/input modules
210
371
  fetch-bun.sh [fetch] Bun, libstdc++, libgcc
211
372
  build-uki.sh [build] UKI; calls scripts/pack-initramfs.sh
212
- build-image.sh [build] GPT disk image; shared with Section 1
213
- run-qemu.sh [run]
214
- fetch-plus-build.sh shell-only -fb
373
+ build-image.sh [build] GPT disk image; shared with the hello-world EFI
215
374
  scripts/fetch.sh hash-checked download helper, sourced by fetch-*.sh
216
375
  scripts/pack-initramfs.sh cpio archive with the device nodes
217
- hello/ Section 1: hello.c and build-hello.sh
376
+ hello/ hello-world EFI: hello.c and build-hello.sh
218
377
  test/ the retired C bootstrap and its build script
219
378
  initramfs/ init.js and the committed libraries; rest fetched
220
379
  ```
221
380
 
222
- The pinned inputs are Alpine v3.24 `linux-virt-6.18.52-r0`, musl `1.2.6-r2`,
381
+ The `--real` inputs are Alpine v3.24 `linux-lts-6.18.52-r0`, musl `1.2.6-r2`,
223
382
  `systemd-efistub-260.2-r0`, `libstdc++`/`libgcc` `15.2.0-r5`, and Bun 1.4.2
224
- `linux-x64-musl-baseline` โ€” about 80 MB in total. No BusyBox, and no userland
225
- beyond Bun itself.
383
+ `linux-x64-musl-baseline`. There is no BusyBox and no userland beyond Bun
384
+ itself.
226
385
 
227
386
  Every step is idempotent and `-f` re-downloads nothing: `scripts/fetch.sh`
228
387
  treats each pinned SHA-256 as the cache key, so a file already in `downloads/`
@@ -231,14 +390,15 @@ tampered with is fetched again and has to pass the same hash before a script
231
390
  extracts from it. Bumping a version changes both the file name and the hash, so
232
391
  it always refetches.
233
392
 
234
- ### Build pipeline
393
+ #### Build pipeline
235
394
 
236
- `fetch-plus-build.sh` is only a wrapper, the same as `bun ./index.js -fb`: it
237
- runs the steps below in order. Each one writes files the next one reads.
395
+ The primary `bun ./index.js -fb --real` pipeline runs the steps below in order;
396
+ each writes files the next one reads. When selected, `--export` runs afterward
397
+ and copies the final `vda.img` to the invocation directory.
238
398
 
239
399
  | script | reads | writes |
240
400
  | --- | --- | --- |
241
- | `fetch-alpine.sh` | Alpine CDN | `kernel/vmlinuz-virt`, `kernel/linuxx64.efi.stub`, `initramfs/lib/ld-musl-x86_64.so.1`, `initramfs/lib/modules/โ€ฆ/{failover,net_failover,virtio_net}.ko` |
401
+ | `fetch-alpine.sh` | Alpine CDN | `kernel/vmlinuz-lts`, `kernel/linuxx64.efi.stub`, musl, and the selected network, storage, input, power and filesystem modules with trimmed module indexes |
242
402
  | `fetch-bun.sh` | GitHub, Alpine CDN | `initramfs/bin/bun`, `initramfs/lib/{libc.musl-x86_64.so.1,libstdc++.so.6,libgcc_s.so.1}` |
243
403
  | `scripts/pack-initramfs.sh` | `initramfs/` | `build/initramfs.cpio.gz` |
244
404
  | `build-uki.sh` | that archive, kernel, stub | `build/cmdline`, `build/os-release`, `vda/EFI/BOOT/BOOTX64.EFI` |
@@ -251,14 +411,14 @@ The stages nest โ€” the initramfs is baked into the UKI, and the UKI into the
251
411
  disk image โ€” so a one-line edit to `initramfs/init.js` still needs both of:
252
412
 
253
413
  ```sh
254
- bun ./index.js -b # or: ./build-uki.sh && ./build-image.sh
414
+ bun ./index.js -b --real
255
415
  ```
256
416
 
257
417
  The kernel command line works the same way: `build-uki.sh` writes it to
258
418
  `build/cmdline` and compiles it into the UKI. Nothing reads it from disk at
259
419
  boot.
260
420
 
261
- ### UKI layout
421
+ #### UKI layout
262
422
 
263
423
  `build-uki.sh` appends sections to systemd's `linuxx64.efi.stub` with
264
424
  `objcopy`, at hand-picked VMAs because `objcopy` will not lay them out for you:
@@ -267,14 +427,14 @@ boot.
267
427
  | --- | --- | --- | --- |
268
428
  | `.text` | +0x0 | the stub itself | 66 KB |
269
429
  | `.osrel` | +0x20000 | `build/os-release` | 78 B |
270
- | `.cmdline` | +0x30000 | `build/cmdline` | 92 B |
271
- | `.linux` | +0x2000000 | `kernel/vmlinuz-virt` | 12 MB |
272
- | `.initrd` | +0x3000000 | `build/initramfs.cpio.gz` | 34 MB |
430
+ | `.cmdline` | +0x30000 | `build/cmdline` | 136 B |
431
+ | `.linux` | +0x2000000 | `kernel/vmlinuz-lts` | 14.5 MB |
432
+ | `.initrd` | +0x3000000 | `build/initramfs.cpio.gz` | 44.6 MB |
273
433
 
274
- The result is a single 46 MB PE32+ file holding kernel, initramfs and command
434
+ The current `--real` result is a single 59.3 MB PE32+ file holding kernel, initramfs and command
275
435
  line.
276
436
 
277
- ### Disk layout
437
+ #### Disk layout
278
438
 
279
439
  `build-image.sh` needs no mount, no loop device and no root privilege. It
280
440
  formats a standalone FAT32 image, fills it with mtools, builds the GPT with
@@ -295,79 +455,83 @@ LBA 262111.. backup GPT
295
455
  backup GPT. `EFI/BOOT/BOOTX64.EFI` is the removable-media fallback path, so the
296
456
  firmware runs it without any NVRAM boot entry.
297
457
 
298
- ### Running
299
-
300
- `bun ./index.js -r` runs `run-qemu.sh`, which boots `vda.img` on q35 under
301
- TCG with 512 MiB, a virtio disk, virtio-net user networking, no display, and
302
- COM1 on stdio; anything after `--` is appended to the QEMU command line. The
303
- chain is OVMF โ†’ the stub's `.text` โ†’ the stub loading its own
304
- `.linux`/`.initrd`/`.cmdline` โ†’ kernel โ†’ `rdinit=/bin/bun`.
305
-
306
- Ctrl-C quits QEMU. Leaving the REPL does not end the session: `init.js`
307
- restarts it, because a PID 1 that exits panics the kernel.
308
-
309
- #### Real hardware
458
+ #### Real-hardware boot details
310
459
 
311
- Build the physical-machine image in PRoot, then write the complete `vda.img`
312
- (not its inner FAT partition) to a USB drive and boot it as x86-64 UEFI media:
460
+ The physical boot chain is firmware โ†’ the UKI stub โ†’ its embedded
461
+ `.linux`/`.initrd`/`.cmdline` sections โ†’ kernel โ†’ `rdinit=/bin/bun`.
462
+ `--real` uses Alpine `linux-lts`, includes the xHCI and USB HID module chain,
463
+ and, with the currently pinned kernel, embeds this complete command line:
313
464
 
314
- ```sh
315
- bun ./index.js -fb --real
465
+ ```text
466
+ console=ttyS0,115200 console=tty0 REAL_MACHINE=1 panic=0 PATH=/bin KERNEL_RELEASE=6.18.52-0-lts rdinit=/bin/bun -- -e import('/init.js')
316
467
  ```
317
468
 
318
- `--real` uses Alpine `linux-lts`, adds the xHCI and USB HID module chain, and
319
- changes the embedded console order to:
469
+ Serial kernel logging is retained, while the final console makes
470
+ `/dev/console` and the Bun REPL use the physical display and keyboard. This
471
+ path has been tested successfully on real hardware.
320
472
 
321
- ```text
322
- console=ttyS0,115200 console=tty0
323
- ```
473
+ #### QEMU development
324
474
 
325
- Serial kernel logging is retained, while `/dev/console` and the Bun REPL use
326
- the physical display and keyboard. The UKI is unsigned, so Secure Boot must be
327
- disabled unless the image is signed separately.
475
+ This optional path has been tested in native Android Termux and in an Ubuntu
476
+ 24.04 cloud VM with QEMU 8.2.2, TCG and OVMF. In native Termux, install the
477
+ runner separately from the PRoot build dependencies:
328
478
 
329
- This path has been tested successfully on real hardware: the machine entered
330
- the interactive `bun-repl>` with working local keyboard input. Physical NIC
331
- drivers and configuration are not included yet, so networking is currently
332
- expected to fail there; that failure is caught and does not prevent local use.
479
+ ```sh
480
+ pkg install qemu-system-x86-64
481
+ ```
333
482
 
334
- From the Bun REPL, start the bundled Buninu userspace:
483
+ That package supplies OVMF at `$PREFIX/share/qemu/edk2-x86_64-code.fd`. Build
484
+ without `--real`; this selects Alpine `linux-virt`, omits the physical-hardware
485
+ module set, and puts `ttyS0` last so serial stdio owns `/dev/console`:
335
486
 
336
487
  ```text
337
- bun-repl> start()
488
+ console=tty0 console=ttyS0,115200 panic=0 PATH=/bin KERNEL_RELEASE=6.18.52-0-virt rdinit=/bin/bun -- -e import('/init.js')
338
489
  ```
339
490
 
340
- The bundled `jmi` terminal editor works without a network connection. For
341
- example, open a new `hlw.js` from the Buninu shell:
491
+ Build and boot it with:
342
492
 
343
493
  ```sh
344
- jmi hlw.js
494
+ bun ./index.js -fb
495
+
496
+ # When running inside PRoot with Termux's native qemu-system-x86_64,
497
+ # expose both the native executable and its OVMF path:
498
+ PREFIX=/data/data/com.termux/files/usr \
499
+ PATH="/data/data/com.termux/files/usr/bin:$PATH" \
500
+ bun ./index.js -r
345
501
  ```
346
502
 
347
- The recommended colour theme for `jmi` is `cmc-tc`. Once inside the editor,
348
- press `Ctrl-E`, type `theme ` (including the trailing space), press `Tab`, then
349
- use the `Up` and `Down` arrow keys to select `cmc-tc` and press `Enter`.
503
+ If QEMU is installed inside the current Debian environment instead, plain
504
+ `bun ./index.js -r` is sufficient; `run-qemu.sh` will use Debian's OVMF path.
350
505
 
351
- Enter and save this content:
506
+ `bun ./index.js -r` runs `run-qemu.sh`, which boots `vda.img` on q35 under TCG
507
+ with 512 MiB, a virtio disk, virtio-net user networking, no display, and COM1
508
+ on stdio. Anything after `--` is appended to the QEMU command line. Ctrl-C
509
+ quits QEMU. Leaving the REPL does not end the session: `init.js` restarts it,
510
+ because a PID 1 that exits panics the kernel.
352
511
 
353
- ```js
354
- console.log("Hello world from real hardware");
355
- ```
512
+ The QEMU-only CLI stage is `-r`/`--run`; `-fbr` is the full virtual pipeline,
513
+ `-br` is its edit-and-boot loop, and arguments after `--` are passed through
514
+ verbatim, for example `-r -- -m 1G`. The corresponding repository entry is
515
+ `run-qemu.sh`. To smoke-test the [Section 1](#1-hello-world) EFI hello-world image instead, build
516
+ it and run the same `-r` stage; OVMF exposes its console over COM1.
517
+ The legacy `fetch-plus-build.sh` wrapper is equivalent to the virtual `-fb`
518
+ stages and does not select `--real`. `npm start` invokes the run stage.
356
519
 
357
- After leaving the editor, execute it locally:
520
+ For an even shorter initramfs loop, bypass the UKI and disk image:
358
521
 
359
522
  ```sh
360
- bun hlw.js
523
+ qemu-system-x86_64 -machine q35,accel=tcg -cpu max -m 512M \
524
+ -kernel kernel/vmlinuz-virt -initrd build/initramfs.cpio.gz \
525
+ -append "console=ttyS0,115200 panic=0 PATH=/bin rdinit=/bin/bun -- -e console.log(1+1)" \
526
+ -nic user,model=virtio-net-pci -display none -serial stdio -no-reboot
361
527
  ```
362
528
 
363
- The normal QEMU image should still be built without `--real`, because QEMU's
364
- default runner uses `-display none` and expects the Bun REPL on `ttyS0`:
365
-
366
- ```sh
367
- bun ./index.js -fb --linux-lts
368
- ```
529
+ Only `./scripts/pack-initramfs.sh` must be rerun between these tests. A panic
530
+ parks the guest because of `panic=0`, so press Ctrl-C. This shortcut does not
531
+ exercise the UKI stub, GPT, physical console or hardware drivers; validate the
532
+ result afterward with `bun ./index.js -b --real` on the real UEFI path.
369
533
 
370
- #### Networking
534
+ ##### QEMU networking
371
535
 
372
536
  The NIC is QEMU user-mode networking (SLIRP): the guest is `10.0.2.15/24`
373
537
  behind a NAT with `10.0.2.2` as gateway and `10.0.2.3` as DNS, so it can reach
@@ -404,35 +568,45 @@ Two things to expect:
404
568
  * **QEMU write-locks `vda.img`.** A second instance fails with `Failed to get
405
569
  "write" lock`; copy the image first if you want two at once.
406
570
 
407
- ### Iterating on the boot
571
+ #### Command implementation
408
572
 
409
- Rebuilding the UKI and the disk image for every attempt is slow, and neither is
410
- needed to exercise the guest: QEMU can boot the kernel and initramfs directly
411
- and take the command line on the host side, so only
412
- `./scripts/pack-initramfs.sh` has to be re-run between attempts.
573
+ The `/bin` commands described earlier are Bun scripts built on two shared
574
+ modules in `/lib`. `--help` uses `Bun.markdown.ansi` to render the Markdown
575
+ manual with hyperlinks. The shared pieces are:
413
576
 
414
- ```sh
415
- qemu-system-x86_64 -machine q35,accel=tcg -cpu max -m 512M \
416
- -kernel kernel/vmlinuz-virt -initrd build/initramfs.cpio.gz \
417
- -append "console=ttyS0,115200 panic=0 PATH=/bin rdinit=/bin/bun -- -e console.log(1+1)" \
418
- -nic user,model=virtio-net-pci -display none -serial stdio -no-reboot
419
- ```
577
+ * `/lib/dlopen.js` โ€” one `bun:ffi` binding to `/lib/libc.musl-x86_64.so.1`
578
+ (`mount`, `umount2`, `ioctl`, `socket`, `sync`, `reboot`, `syscall`, โ€ฆ), `errno`/`strerror`,
579
+ a `SysError` class and the `showDocument()` helper behind every `--help`.
580
+ `init.js` keeps its own copy of the binding because it runs before `/proc`
581
+ exists and cannot import anything.
582
+ * `/lib/modprobe.js` โ€” `modprobe(name)` resolves `modules.alias` and
583
+ `modules.dep` under `/lib/modules/<release>/` and calls `finit_module`
584
+ for each dependency in order. The kernel has no `/sbin/modprobe` to call
585
+ here, so `mount` preloads `ext4`+`jbd2`, `vfat`+NLS tables, `ntfs3`,
586
+ `sd_mod`, `nvme`, and so on itself.
420
587
 
421
- A panicking guest does not exit QEMU โ€” `panic=0` parks it rather than rebooting
422
- into the firmware menu โ€” so press Ctrl-C. Run the real UEFI path once at the
423
- end, since it is the only thing that exercises the stub and the GPT.
588
+ Which modules the image carries is the list in `fetch-alpine.sh`; the
589
+ `select_module` helper there pulls in dependencies from Alpine's
590
+ `modules.dep`, and `--real` adds common storage paths (`sd_mod`, `ahci`,
591
+ `ata_generic`, `pata_acpi`, `nvme`, Intel `vmd`, `usb-storage` and `uas`),
592
+ the wired NICs and the ACPI power drivers. Alpine's
593
+ kernels build all of these as modules; a self-built kernel with them `=y`
594
+ works the same way, the loaders just find nothing to load.
424
595
 
425
- ### Reaching JavaScript with nothing mounted
596
+ #### Reaching JavaScript with nothing mounted
426
597
 
427
- The default QEMU kernel command line is
598
+ The default `--real` image embeds this kernel command line (shown for the
599
+ currently pinned LTS release):
428
600
 
429
601
  ```text
430
- console=tty0 console=ttyS0,115200 panic=0 PATH=/bin rdinit=/bin/bun -- -e import('/init.js')
602
+ console=ttyS0,115200 console=tty0 REAL_MACHINE=1 panic=0 PATH=/bin KERNEL_RELEASE=6.18.52-0-lts rdinit=/bin/bun -- -e import('/init.js')
431
603
  ```
432
604
 
433
- so the kernel execs `/bin/bun` itself, with nothing native in between, and
434
- `init.js` mounts `/proc`, `/sys`, `/dev` and `/tmp` through `bun:ffi` as its
435
- first action. Everything before `--` needs an `=`, because the kernel appends
605
+ The physical display (`tty0`) is the final console and therefore owns PID 1's
606
+ standard streams; serial logging remains available on `ttyS0`. The kernel
607
+ execs `/bin/bun` itself, with nothing native in between, and `init.js` mounts
608
+ `/proc`, `/sys`, `/dev`, `/tmp` and `/dev/pts` through `bun:ffi` as its first
609
+ action. Everything before `--` needs an `=`, because the kernel appends
436
610
  any bare word to the init argv instead of treating it as an environment
437
611
  assignment; everything after `--` becomes `argv[1..]` verbatim, with `argv[0]`
438
612
  set to the `rdinit=` path. Parameter order is otherwise free, so `rdinit=` is
@@ -463,8 +637,9 @@ Nothing has mounted devtmpfs at that point, so `scripts/pack-initramfs.sh`
463
637
  records `urandom` โ€” along with `console`, which the kernel opens as init's
464
638
  fd 0/1/2 before the exec, and a few other harmless nodes โ€” directly in the
465
639
  cpio archive.
466
- `ttyS0` is listed last among the `console=` arguments so it, rather than the
467
- invisible `tty0`, is what `/dev/console` points at.
640
+ For `--real`, `tty0` is listed last among the `console=` arguments so the
641
+ physical display owns `/dev/console`; `ttyS0` remains available for serial
642
+ kernel logs.
468
643
 
469
644
  The initramfs holds no `/init`; the kernel reaches Bun through `rdinit=` alone.
470
645
  The retired native bootstrap is kept in `test/build-init-bootstrap.sh`, which
@@ -474,10 +649,14 @@ it, repack, and boot it with `rdinit=/init`. Varying which filesystems such a
474
649
  bootstrap mounted before the exec is how the `/dev/urandom` requirement above
475
650
  was found.
476
651
 
477
- ### What init.js does
652
+ #### What init.js does
478
653
 
479
654
  It installs signal handlers, loads a separate physical copy of musl through
480
- `bun:ffi`, prints a greeting, and starts `node:repl`. The loader and libc paths
655
+ `bun:ffi`, mounts `/proc`, `/sys`, `/dev`, `/tmp` and `/dev/pts` (without
656
+ the devpts mount every pty open fails with `ENODEV`, since `/dev/ptmx`
657
+ resolves through `/dev/pts/ptmx`), prints a greeting, and starts `node:repl`.
658
+ Submitting an empty or whitespace-only REPL line prints the welcome message
659
+ and the available `cfg` getters again. The loader and libc paths
481
660
  contain identical bytes but are deliberately distinct inodes:
482
661
 
483
662
  ```text
@@ -490,16 +669,37 @@ deadlocks, while the independent physical libc works. It provides `mount`,
490
669
  `waitpid`, networking calls, and the generic `syscall` entry point, so no
491
670
  custom `libinit.so` is needed.
492
671
 
493
- The Bun init also loads Alpine's three compressed `virtio_net` modules through
494
- musl's generic `syscall`, configures QEMU user networking as `10.0.2.15/24`
495
- with the standard `10.0.2.2` gateway and `10.0.2.3` DNS proxy, and fetches
496
- `http://example.com` before opening the REPL.
672
+ On a `--real` boot, init starts `/etc/resolv.conf` with `1.1.1.1` and
673
+ `8.8.8.8`; these are initial public resolvers until the user replaces them
674
+ with DNS appropriate for the local network. Hardware networking remains down
675
+ until `cfg.net` loads the packaged drivers and the user assigns an address and
676
+ route.
677
+
678
+ On a `--real` image the REPL also offers the getter-based `cfg` namespace:
679
+ `cfg.net`, `cfg.disk`, `cfg.power` and `cfg.all` run as soon as the property is read, without
680
+ parentheses. `cfg.net` loads every packaged network module (including PHY and bus support),
681
+ then reads the `modalias` of every PCI and USB network device, matches it
682
+ against the `pci:`/`usb:` lines
683
+ `fetch-alpine.sh` kept in `modules.alias` and lists the interfaces. `cfg.disk`
684
+ loads the packaged SATA/PATA/SCSI, NVMe/VMD and USB storage stacks so
685
+ their disks and partitions appear in `/dev`, then lists `/sys/class/block`. `cfg.all`
686
+ unconditionally attempts every packaged module and recursively loads its
687
+ declared dependencies. `--real` ships Intel `e1000`/`e1000e`/`igb`/`igc`,
688
+ Realtek `r8169`, Atheros `alx`, Broadcom `tg3` and the `r8152`/`ax88179_178a`/
689
+ `cdc_ether` USB dongles. `cfg.power` loads the ACPI
690
+ `battery`, `ac`, `button` and `thermal` modules through `/lib/modprobe.js`
691
+ and lists `/sys/class/power_supply` (a desktop without a battery simply
692
+ reports nothing registered). Nothing loads them at boot. PID 1 also installs
693
+ `uncaughtException`/`unhandledRejection` listeners: without them a stray
694
+ `Promise.reject()` at the prompt makes Bun exit, which is a kernel panic
695
+ (`Attempted to kill init!`).
497
696
 
498
697
  The REPL exposes a global `bunmsh()` function. Calling it creates
499
698
  `/tmp/bunmsh-runtime`, installs the pinned `bunmsh@0.3.6` package there, and
500
699
  directly runs its `src/main.js` with a small explicit environment. The calls
501
700
  are synchronous so bunmsh exclusively owns the terminal until `exit` returns
502
- to the Bun REPL.
701
+ to the Bun REPL. `start()` launches Buninu with the current process environment
702
+ preserved, while overriding `PATH` and `HOME` for the userspace session.
503
703
 
504
704
  ## Authorship
505
705
 
@@ -511,27 +711,30 @@ the exact version, package and SHA-256 that ships here.
511
711
 
512
712
  ## License
513
713
 
514
- Everything in this repository that is not listed in the table below โ€” the
515
- build scripts, `hello/hello.c`, `init.js`, the retired bootstrap, and the
516
- configuration and metadata files โ€” is Buninu Linux's own work under the MIT
517
- License; see [LICENSE](LICENSE).
714
+ Buninu Linux's own work โ€” the build scripts, `hello/hello.c`, `init.js`, the
715
+ retired bootstrap, and the configuration and metadata files โ€” is under the
716
+ MIT License; see [LICENSE](LICENSE). The Buninu userspace under
717
+ `initramfs/buninu/` is also MIT (`initramfs/buninu/LICENSE`) and carries its
718
+ own third-party notices, listed at the end of [NOTICE.md](NOTICE.md).
518
719
 
519
- The repository also commits four third-party binaries under `initramfs/lib/`,
520
- and the built image redistributes several more. Each stays under its own
521
- terms, with the full texts in [`LICENSES/`](LICENSES/):
720
+ The repository also commits four third-party platform binaries under
721
+ `initramfs/lib/`, and the built image redistributes several more. Each stays
722
+ under its own terms, with the full texts in [`LICENSES/`](LICENSES/):
522
723
 
523
724
  | Component | License | Where |
524
725
  |---|---|---|
525
726
  | musl (`ld-musl-x86_64.so.1`, `libc.musl-x86_64.so.1`) | MIT | committed |
526
727
  | GCC runtime (`libgcc_s.so.1`, `libstdc++.so.6`) | GPL-3.0-or-later with the GCC Runtime Library Exception | committed |
527
- | Linux `virt`/`lts` kernels and VirtIO, networking, xHCI, USB and HID modules | GPL-2.0-only with the Linux syscall note | image only |
728
+ | Linux kernels and networking, storage, xHCI, USB and HID modules | GPL-2.0-only with the Linux syscall note | image only |
528
729
  | systemd EFI stub | LGPL-2.1-or-later | image only |
529
730
  | Bun | MIT, plus the licenses of what it statically links | image only |
530
731
 
531
732
  One component is worth naming here rather than leaving to be found: the two
532
- GCC runtime libraries are **GPL-3.0 with the GCC Runtime Library Exception**.
533
- That exception is what lets them ship next to MIT-licensed code, so nothing
534
- here changes Buninu Linux's own terms โ€” but if your organisation screens for
535
- GPL, this is the component it will find. [NOTICE.md](NOTICE.md) records the
733
+ GCC runtime libraries are **GPL-3.0 with the GCC Runtime Library Exception**
734
+ (as are the aarch64 `libgcc_s.so.1` / `libstdc++.so.6` that Buninu's
735
+ `apps/musl-la/` commits and the image carries along). That exception is what
736
+ lets them ship next to MIT-licensed code, so nothing here changes Buninu
737
+ Linux's own terms โ€” but if your organisation screens for GPL, these are the
738
+ components it will find. [NOTICE.md](NOTICE.md) records the
536
739
  exact Alpine build of every component, the pinned aports commit that is its
537
740
  Corresponding Source, and SHA-256 sums for every binary.