buninu-linux 0.2.1 → 0.3.1

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.
Files changed (42) hide show
  1. package/README.md +547 -190
  2. package/build-uki.sh +4 -2
  3. package/fetch-alpine.sh +78 -43
  4. package/index.js +4 -1
  5. package/initramfs/bin/ip +630 -0
  6. package/initramfs/bin/mount +401 -0
  7. package/initramfs/bin/poweroff +10 -0
  8. package/initramfs/bin/ps +256 -0
  9. package/initramfs/bin/reboot +10 -0
  10. package/initramfs/bin/tar +169 -0
  11. package/initramfs/bin/top +129 -0
  12. package/initramfs/bin/umount +71 -0
  13. package/initramfs/buninu/apps/bunmsh/CHANGELOG.md +36 -0
  14. package/initramfs/buninu/apps/bunmsh/README.md +7 -0
  15. package/initramfs/buninu/apps/bunmsh/package.json +1 -1
  16. package/initramfs/buninu/apps/bunmsh/src/fancy-ls.js +29 -14
  17. package/initramfs/buninu/apps/bunmsh/src/line-edit.js +43 -0
  18. package/initramfs/buninu/apps/bunmsh/src/main.js +15 -1
  19. package/initramfs/buninu/apps/bunmsh/src/mouse.js +29 -4
  20. package/initramfs/buninu/apps/bunmsh/src/shell.js +9 -1
  21. package/initramfs/buninu/apps/bunmsh/test/shell1.test.js +70 -0
  22. package/initramfs/buninu/apps/jsmdcui/CHANGELOG.md +10 -0
  23. package/initramfs/buninu/apps/jsmdcui/README.md +2 -0
  24. package/initramfs/buninu/apps/jsmdcui/package.json +1 -1
  25. package/initramfs/buninu/apps/jsmdcui/runtime/help/commands.md +4 -1
  26. package/initramfs/buninu/apps/jsmdcui/runtime/help/help.md +2 -0
  27. package/initramfs/buninu/apps/jsmdcui/src/index.js +40 -13
  28. package/initramfs/buninu/package.json +1 -1
  29. package/initramfs/init.js +164 -11
  30. package/initramfs/lib/dlopen.js +121 -0
  31. package/initramfs/lib/document.js +6 -0
  32. package/initramfs/lib/modprobe.js +235 -0
  33. package/initramfs/lib/power.js +35 -0
  34. package/initramfs/usr/share/doc/buninu-linux/ip.md +155 -0
  35. package/initramfs/usr/share/doc/buninu-linux/mount.md +155 -0
  36. package/initramfs/usr/share/doc/buninu-linux/poweroff.md +15 -0
  37. package/initramfs/usr/share/doc/buninu-linux/ps.md +94 -0
  38. package/initramfs/usr/share/doc/buninu-linux/reboot.md +15 -0
  39. package/initramfs/usr/share/doc/buninu-linux/tar.md +73 -0
  40. package/initramfs/usr/share/doc/buninu-linux/top.md +23 -0
  41. package/initramfs/usr/share/doc/buninu-linux/umount.md +48 -0
  42. package/package.json +2 -2
package/README.md CHANGED
@@ -22,84 +22,335 @@
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
+
28
+ - [Table of contents](#contents)
29
+ - .
25
30
  - 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
31
+ - [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`:
32
+ * Bun reaches its interactive REPL
33
+ * start() starts the Buninu userspace shell
34
+ * `cfg.disk` + shell `mount` mounts local disks
35
+ * `cfg.net` loads common wired NIC and Android USB-tethering drivers
36
+ * Android phone USB tethering over RNDIS has been tested successfully, allowing Buninu Linux to access the Internet through an Android phone
37
+ * IP addresses and routes are configured manually because the image does not yet include a DHCP client.
38
+ * The bundled `jmi` editor, jsmdcui editor/terminal multitasking, and local JavaScript execution also work.
39
+
40
+ ---
41
+
42
+ - The hello-world EFI application in [Section 1](#1-hello-world) is the starting point that the UKI replaces
34
43
  - It still builds, and it is the quickest way to check whether the disk image and firmware path work at all.
35
44
 
36
45
  ## Quick start
37
46
 
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)
47
+ - This guide builds a bootable `buninu-linux-<version>.img`
48
+ - The documented build environment is Debian 13 under Termux PRoot
49
+ - A regular Debian installation works as well.
50
+ - For a source checkout, clone into the Termux home so native Termux and PRoot
51
+ can share it; `~` differs between them, but the absolute path is the same.
45
52
 
46
53
  ### Steps
47
- #### in native Termux
48
- - pkg install qemu-system-x86-64
54
+
55
+ - Before anything, install Bun in Debian first:
56
+
57
+ ```sh
58
+ apt update
59
+ apt install curl unzip
60
+ curl -fsSL https://bun.sh/install | bash
61
+ ```
62
+
63
+ - Small tip: The below `apt install` command shows in the terminal when you run -b without the needed tools
64
+
65
+ #### Build directly with bun x
66
+
67
+ Install the build tools, enter a directory where
68
+ you want to keep the finished image, then run the published package:
69
+
70
+ ```sh
71
+ apt install binutils-mingw-w64-x86-64 cpio curl dosfstools fakeroot mtools parted unzip
72
+
73
+ mkdir -p /data/data/com.termux/files/home/buninu-build
74
+ cd /data/data/com.termux/files/home/buninu-build
75
+
76
+ $HOME/.bun/bin/bun x buninu-linux --version
77
+
78
+ $HOME/.bun/bin/bun x buninu-linux -fb --real --export
79
+ ```
80
+
81
+ `--export` copies the completed `vda.img` out of bunx's package directory and
82
+ into the directory where the command was invoked, as
83
+ `buninu-linux-<version>.img`. Use the exact exported filename printed by the
84
+ command when writing the USB drive.
85
+
86
+ #### Or build from a source checkout
87
+
88
+ In native Termux:
89
+
49
90
  - git clone https://github.com/jjtseng93/buninu-linux.git ~/buninu-linux
50
91
 
51
- #### in PRoot (Debian 13)
92
+ In Debian 13 under PRoot:
93
+
52
94
  - apt install binutils-mingw-w64-x86-64 cpio curl dosfstools fakeroot mtools parted unzip
53
95
  - cd /data/data/com.termux/files/home/buninu-linux
54
- - bun ./index.js -fb
55
- * fetch ~80 MB from Alpine/Bun & build vda.img
96
+ - bun ./index.js -fb --real --export
97
+ * Fetches from Alpine/Bun
98
+ * Selects the physical-hardware modules
99
+ * Builds `vda.img`
100
+ * Exports it to `buninu-linux-<version>.img`
56
101
 
57
- #### back in native Termux
58
- - cd ~/buninu-linux
59
- - bun ./index.js -r
60
- * boot it
102
+ #### Write the image to a USB drive
61
103
 
62
- `index.js` runs under bun or node. A successful boot ends like this, with a
63
- REPL on the serial console:
104
+ Write the complete exported image (or `vda.img` when not using `--export`), not
105
+ the FAT partition inside it, to a USB drive. The source filename below is an
106
+ example; replace it with the exact name printed by the build. The destination
107
+ is also only an example: identify the correct whole-disk device first, because
108
+ this overwrites it completely.
64
109
 
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
110
+ ```sh
111
+ sudo dd if='buninu-linux-<version>.img' of=/dev/sdX bs=4M conv=fsync status=progress
112
+ ```
73
113
 
114
+ #### Booting from the USB drive
115
+
116
+ Boot that drive as x86-64 UEFI media. The UKI is unsigned, so disable Secure
117
+ Boot unless you sign it yourself. A successful physical boot reaches the local
118
+ display and USB keyboard with a welcome ending like this:
119
+
120
+ ```text
74
121
  Welcome to Buninu Linux!
75
122
  Bun 1.4.2 is now PID 1
76
-
77
123
  Type start() to run buninu --local
124
+ Configuration getters: cfg.all, cfg.disk, cfg.net, cfg.power
125
+
78
126
  bun-repl>
79
127
  ```
80
128
 
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.
129
+ If this welcome is not visible, later kernel messages may simply have scrolled
130
+ over it on the same console. Press Enter at an empty REPL prompt; an empty or
131
+ whitespace-only line prints the welcome and current `cfg.*` getter list again.
132
+
133
+ Load only the subsystem needed, then enter the Buninu shell:
134
+
135
+ ```js
136
+ cfg.net // wired NIC and Android USB-tethering drivers
137
+ cfg.disk // SATA/NVMe/USB storage drivers and detected block devices
138
+ cfg.power // battery, AC, button and thermal drivers
139
+ start()
140
+ ```
141
+
142
+ There is no DHCP client yet; configure a detected wired interface with `ip` as
143
+ described under [Basic configuration](#basic-configuration). Run `poweroff` for a synced
144
+ shutdown. After editing the initramfs, `bun ./index.js -b --real --export` rebuilds the
145
+ image; `-h` lists all flags.
146
+
147
+ ## Using Buninu Linux
148
+
149
+ ### Basic configuration
150
+
151
+ Real hardware is the primary target. Boot the USB drive with Secure Boot
152
+ disabled unless you have signed the UKI yourself. The physical display and USB
153
+ keyboard provide the `bun-repl>` prompt.
154
+
155
+ Load only the hardware subsystem you need:
156
+
157
+ ```js
158
+ cfg.net
159
+ cfg.disk
160
+ cfg.power
161
+ ```
162
+
163
+ `cfg.net` loads the packaged wired-network and Android USB-tethering drivers and prints detected
164
+ interfaces and MAC addresses. There is no DHCP client yet, so configure the
165
+ interface manually after entering the Buninu shell with `start()`:
84
166
 
85
- ## 0. Install dependencies
167
+ ```sh
168
+ ip link set eth0 up
169
+ ip addr add 192.168.1.50/24 dev eth0
170
+ ip route add default via 192.168.1.1
171
+ ```
86
172
 
87
- - Building Buninu Linux (Section 2) — needs no compiler:
88
- - Bun ships as a prebuilt binary and the UKI is assembled with `objcopy`.
173
+ The real image initializes `/etc/resolv.conf` with `1.1.1.1` and `8.8.8.8`;
174
+ replace them if your network requires different DNS servers. `cfg.disk` loads
175
+ common SATA/PATA/SCSI, NVMe/VMD and USB-storage drivers so detected disks and
176
+ partitions appear in `/dev`. To inspect a filesystem without mounting it:
89
177
 
90
178
  ```sh
91
- apt install binutils-mingw-w64-x86-64 cpio curl dosfstools fakeroot mtools parted unzip
179
+ mount -fv /dev/sda1 /mnt
180
+ ```
181
+
182
+ Run `poweroff` for a synced shutdown.
183
+
184
+ ### Editor quick start
185
+
186
+ The bundled `jmi` terminal editor works without a network connection:
187
+
188
+ ```sh
189
+ jmi hlw.js
190
+ ```
191
+
192
+ - The recommended `jmi` color theme is `cmc-tc`
193
+ - Set it by:
194
+ * Press `Ctrl-E`
195
+ * Type `theme ` (including the trailing space)
196
+ * Press `Tab`, select it with up/dn keys
197
+ * Press `Enter`.
198
+
199
+ - Enter and save(Ctrl-S) this content:
200
+
201
+ ```js
202
+ console.log("Hello world from real hardware");
203
+ ```
204
+
205
+ After leaving the editor with `Ctrl-Q`, run it locally:
206
+
207
+ ```sh
208
+ bun hlw.js
92
209
  ```
93
210
 
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`
211
+ If `Enter` is not recognized in a particular terminal, try `Ctrl-J` or
212
+ `Ctrl-M`.
213
+
214
+ ### Panes, terminals, and tabs
215
+
216
+ `jmi` and jsmdcui can keep editors and terminal sessions open together. Press
217
+ `Ctrl-E`, type `term`, and press `Enter` to open the default Buninu shell in a
218
+ terminal pane. `term COMMAND` starts a specific command instead. Use `vsplit`,
219
+ `hsplit`, or `tab` from the same `Ctrl-E` command prompt to create another
220
+ editor pane or tab; each command optionally accepts a filename. From an editor
221
+ pane, `Ctrl-T` is the direct shortcut for a new empty tab.
222
+
223
+ #### Pane and tab controls
224
+
225
+ | Input | Result |
226
+ | --- | --- |
227
+ | Click a pane | Focus that editor or terminal pane. |
228
+ | `Ctrl-T` in an editor pane | Open a new empty tab. |
229
+ | `Ctrl-W` once | Move to the next pane in the current tab, from either an editor or terminal pane. |
230
+ | `Alt-T` | Move to the next tab. |
231
+ | **`Ctrl-W` twice quickly** | **Important escape hatch:** move to the next tab like `Alt-T`, or create an editor tab when needed. See the detailed explanation below. |
232
+ | `Esc` in a terminal pane | Close that terminal pane and return to its previous editor buffer |
233
+ | `Ctrl-Q` or `Alt-Q` in an editor pane | Close the current editor UI. |
234
+
235
+ **Why this escape hatch is important:** A terminal pane must forward almost every key combination unchanged to the
236
+ shell or application running inside it. `Ctrl-W` is therefore the one
237
+ multitasking escape key that jsmdcui intercepts: it lets you leave the active
238
+ terminal without terminating the work inside it. Press it once to move to the
239
+ next pane in the current tab. Press it twice quickly to move to the next tab.
240
+
241
+ The double press is especially important in an all-terminal workspace. When
242
+ the current tab is the rightmost tab and every existing tab contains only
243
+ terminal panes, it creates a new editor tab instead of wrapping back to the
244
+ first terminal tab. In every other case it performs the normal next-tab cycle.
245
+ Use `Esc` only when you actually want to close the current terminal pane.
246
+
247
+ ### Using Bun Modern Shell
248
+
249
+ bunmsh is the dependency-free, mksh-inspired shell bundled with Buninu. Its
250
+ main features are:
251
+
252
+ - Lines beginning with `Bun.*` run as JavaScript; returned values are printed
253
+ and promises are awaited automatically. Use `Bun.e;` or `Bun.e,` for
254
+ arbitrary JavaScript.
255
+ - Lightweight cwd tabs keep several working directories ready. In bunmsh,
256
+ `Ctrl-T` creates or cycles right through tabs, while `Alt-T` cycles left.
257
+ * Also useful cmds: `tab n`, `tab c`(Alt-C)
258
+ - Shell variables and JavaScript share a live variable table by `$.`, and `Bun.sha`
259
+ can retain JavaScript values across commands and cwd tabs.
260
+ - Saved history, Bash/Fish history import, completions, and ghost suggestions
261
+ are available interactively.
262
+ - `serve [directory]` starts a browsable HTTP file server. Together with
263
+ Android USB tethering, it provides a simple way to transfer files between
264
+ Buninu Linux and the connected phone.
265
+ - Cross-platform builtins and PATH-fallback commands.
266
+ - `Ctrl-U` removes and saves the text before the cursor; press it again at the
267
+ beginning of the line to restore it. `Ctrl-K` independently toggles the text
268
+ after the cursor when at the end of the line.
269
+ - Unquoted pathname patterns use `Bun.Glob` and follow directory symbolic
270
+ links used as intermediate path components, as traditional shells do. This
271
+ matters for sysfs, where class entries are normally links. For example:
98
272
 
99
273
  ```sh
100
- apt install clang lld
274
+ ls /sys/class/net/*/device
101
275
  ```
102
276
 
277
+ For a readable process overview, run:
278
+
279
+ ```sh
280
+ pspac
281
+ ```
282
+
283
+ `pspac` shows the real `PID COMMAND` process table and highlights each command
284
+ line as shell syntax with micro's Monokai colours. Leading directories are
285
+ dimmed so the program name stands out. Use `pspa` for the same table as plain
286
+ text.
287
+
288
+ See the [bunmsh repository](https://github.com/jjtseng93/bunmsh) for its full
289
+ syntax, interactive controls, builtins, and current compatibility details.
290
+
291
+ ## Commands inside /bin
292
+
293
+ | command | does | manual |
294
+ | --- | --- | --- |
295
+ | `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` |
296
+ | `umount` | `umount2(2)` with `-l`, `-f`, `-R` | `umount --help` |
297
+ | `ip` | iproute2 grammar over `SIOC*` ioctls and `/proc/net`: `link`, `addr`, `route`, `neigh` | `ip --help` |
298
+ | `ps` | composable process fields from `/proc`; also exports its snapshot and single-row formatter for other commands | `ps --help` |
299
+ | `top` | periodically refreshes the process snapshot and rows supplied by `ps` | `top --help` |
300
+ | `poweroff` | sync pending writes and power off through the Linux reboot system call | `poweroff --help` |
301
+ | `reboot` | sync pending writes and restart through the shared Linux reboot logic | `reboot --help` |
302
+ | `tar` | create, extract, or list tar archives with gzip and zstd compression through `Bun.Archive` | `tar --help` |
303
+
304
+ Besides `bun` (and `sh`/`node` pointing at it), `/bin` includes eight commands
305
+ implemented as Bun scripts.
306
+
307
+ The small `tar` follows the current `Bun.Archive` boundary: extraction
308
+ restores directories and symbolic links, while hard links are skipped by Bun
309
+ 1.4.3. Creation stores regular files but not Unix metadata, links, or empty
310
+ directories, and listing reports regular files only. It supports gzip and
311
+ Bun's built-in zstd, not xz or bzip2.
312
+
313
+ `--help` renders each Markdown manual in the terminal. Common examples:
314
+
315
+ ```sh
316
+ mount /dev/sda1 /mnt --mkdir
317
+ mount -fv /dev/sda1 /mnt
318
+ mount -t tmpfs -o size=64M tmpfs /tmp/x
319
+ mount -t ntfs3 -o force /dev/sda3 /mnt/windows
320
+ umount /mnt
321
+ umount -R /mnt
322
+
323
+ ip link set eth0 up
324
+ ip addr add 192.168.1.50/24 dev eth0
325
+ ip route add default via 192.168.1.1
326
+
327
+ ip -br addr && ip route
328
+ ip addr replace 192.168.1.50/24 dev eth0
329
+ ip route replace default via 192.168.1.1
330
+
331
+ ps -eo pid,args
332
+ ps -ef
333
+ ps aux
334
+ pspac
335
+ top
336
+ top -b -n 1
337
+
338
+ poweroff
339
+ reboot
340
+
341
+ tar cvf /tmp/buninu.tar README.md package.json
342
+ tar tvf /tmp/buninu.tar
343
+ mkdir -p /tmp/buninu-copy
344
+ tar xvf /tmp/buninu.tar -C /tmp/buninu-copy
345
+
346
+ # Add z to create a gzip-compressed archive
347
+ tar czvf /tmp/buninu.tar.gz README.md package.json
348
+ ```
349
+
350
+ ## Environment and dependencies
351
+
352
+ ### Build environment
353
+
103
354
  The baseline is the stock Debian 13 (trixie) OCI image, which is also what this
104
355
  PRoot is; a dependency is anything that image does not already have.
105
356
  `debian:13` and `debian:13-slim` carry the same 78 packages and identical
@@ -110,7 +361,16 @@ locales — so `debian:13-slim` is the better starting point at 105 MB against
110
361
  The base image already provides `bash`, GNU `tar`, `gzip`, `sha256sum`,
111
362
  `mknod`, `find`, `sort`, `mkdir`, `mktemp`, `cp`, `rm`, `chmod`, `truncate`,
112
363
  `dd`, `printf`, `awk` and `dirname`, all used by these scripts as they are.
113
- Everything else has to be added. For Section 2:
364
+ Everything else has to be added.
365
+
366
+ ### Buninu Linux ([Section 2](#2-buninu-linux-bun-as-pid-1))
367
+
368
+ Buninu Linux needs no compiler: Bun is downloaded as a prebuilt binary and the
369
+ UKI is assembled with `objcopy`.
370
+
371
+ ```sh
372
+ apt install binutils-mingw-w64-x86-64 cpio curl dosfstools fakeroot mtools parted unzip
373
+ ```
114
374
 
115
375
  * **`binutils-mingw-w64-x86-64`** — `x86_64-w64-mingw32-objcopy` appends the
116
376
  `.osrel`, `.cmdline`, `.linux` and `.initrd` sections to the EFI stub, and
@@ -130,30 +390,35 @@ Everything else has to be added. For Section 2:
130
390
  * **`parted`** — writes the protective MBR, the primary and backup GPT, and the
131
391
  ESP partition entry with its type GUID.
132
392
  * **`unzip`** — extracts the `bun` binary from the release zip.
393
+ * **`bun`** (or **`node`**) — runs `index.js` in the build environment.
394
+ + Under bun, `--readme` renders the Markdown; under node it is printed as-is.
133
395
 
134
- To boot, from native Termux rather than PRoot:
396
+ ### Hello world ([Section 1](#1-hello-world))
397
+
398
+ The hello-world EFI application needs the C/PE toolchain plus the shared disk
399
+ image tools:
400
+
401
+ ```sh
402
+ apt install clang lld dosfstools mtools parted
403
+ ```
135
404
 
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.
405
+ * **`clang`** — compiles `hello/hello.c` to a PE32+ object with
406
+ `--target=x86_64-pc-win32-coff`. It resolves to `clang-19` on trixie.
407
+ * **`lld`** — provides `lld-link`, which links `hello/hello.obj` into an EFI
408
+ application with `/subsystem:efi_application`. It resolves to `lld-19` on
409
+ trixie.
410
+ * **`dosfstools`**, **`mtools`** and **`parted`** — create the FAT ESP and wrap
411
+ it in the GPT `vda.img` through the shared `build-image.sh`.
142
412
 
143
- For Section 1 and the retired bootstrap only:
413
+ ### Retired native bootstrap
144
414
 
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`.
415
+ The retired `test/build-init-bootstrap.sh` also needs `clang lld`. It compiles
416
+ the historical freestanding x86-64 `/init`; the current Buninu Linux path does
417
+ not use or compile it.
152
418
 
153
- `parted`, `dosfstools` and `mtools` back `build-image.sh`, which both sections
154
- use.
419
+ ## Implementation details
155
420
 
156
- ## 1. Hello world
421
+ ### 1. Hello world
157
422
 
158
423
  - `hello/hello.c` is a freestanding PE32+ EFI application installed at the
159
424
  removable media fallback path `EFI/BOOT/BOOTX64.EFI`.
@@ -162,67 +427,74 @@ use.
162
427
  ```sh
163
428
  ./hello/build-hello.sh # in PRoot
164
429
  ./build-image.sh # in PRoot
165
- bun ./index.js -r # in native Termux
430
+ sudo dd if=vda.img of=/dev/sdX bs=4M conv=fsync status=progress
166
431
  ```
167
432
 
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.
433
+ After verifying `/dev/sdX` is the whole destination USB device, boot it on the
434
+ physical x86-64 UEFI machine. It writes `Hello world from x64 UEFI!` to the
435
+ firmware console and waits; press any key to return to the firmware interface.
173
436
 
174
- `build-image.sh` is shared with Section 2 — see [Disk layout](#disk-layout).
437
+ `build-image.sh` is shared with [Section 2](#2-buninu-linux-bun-as-pid-1) — see [Disk layout](#disk-layout).
175
438
 
176
- ## 2. Buninu Linux – Bun as PID 1
439
+ ### 2. Buninu Linux – Bun as PID 1
177
440
 
178
441
  An unsigned x64 Unified Kernel Image assembled from official Alpine packages,
179
442
  with Bun as the init process, replaces the hello-world application:
180
443
 
181
444
  ```sh
182
- bun ./index.js -fb # in PRoot
183
- bun ./index.js -r # in native Termux
445
+ # physical-machine image, in PRoot Debian
446
+ bun ./index.js -fb --real --export
447
+
448
+ # bun x buninu-linux -fb --real --export
184
449
  ```
185
450
 
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:
451
+ `index.js` is the single entry point (`npm run fetch` and `npm run pack` call
452
+ it too). Its physical-image stages run as fetch → build:
453
+
454
+ #### Build flags
189
455
 
190
456
  | flag | runs | use it when |
191
457
  | --- | --- | --- |
192
458
  | `-f`, `--fetch` | `fetch-alpine.sh`, `fetch-bun.sh` | first clone, or after bumping a pinned version |
193
459
  | `-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 |
460
+
461
+ `--export` is a post-build option: after `-b` finishes, it copies `vda.img` to
462
+ the directory where the command was invoked as
463
+ `buninu-linux-<version>.img`. It therefore requires `-b`/`--build` and is the
464
+ recommended way to retain an image produced through npx/bunx:
465
+
466
+ ```sh
467
+ bun x buninu-linux -fb --real --export
468
+ ```
195
469
 
196
470
  `--linux-lts` selects Alpine's general-purpose LTS kernel. `--real` implies
197
471
  `--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.
472
+ console and includes xHCI/USB HID, common wired NIC, Android USB-tethering,
473
+ storage and ACPI power modules needed by typical laptops and desktops.
199
474
 
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.
475
+ For real hardware, `-fb --real` is the complete image pipeline and
476
+ `-b --real` is the edit-and-rebuild loop. Before starting, `index.js` checks
477
+ that every required tool is on `PATH` and lists what is missing instead of
478
+ failing halfway. The shell scripts do the actual work and each still runs on
479
+ its own.
206
480
 
207
481
  ```text
208
- index.js entry point: -f / -b / -r / --linux-lts / --real
482
+ index.js entry point: -f / -b / --export / --linux-lts / --real
209
483
  fetch-alpine.sh [fetch] kernel, EFI stub, musl, network/input modules
210
484
  fetch-bun.sh [fetch] Bun, libstdc++, libgcc
211
485
  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
486
+ build-image.sh [build] GPT disk image; shared with the hello-world EFI
215
487
  scripts/fetch.sh hash-checked download helper, sourced by fetch-*.sh
216
488
  scripts/pack-initramfs.sh cpio archive with the device nodes
217
- hello/ Section 1: hello.c and build-hello.sh
489
+ hello/ hello-world EFI: hello.c and build-hello.sh
218
490
  test/ the retired C bootstrap and its build script
219
491
  initramfs/ init.js and the committed libraries; rest fetched
220
492
  ```
221
493
 
222
- The pinned inputs are Alpine v3.24 `linux-virt-6.18.52-r0`, musl `1.2.6-r2`,
494
+ The `--real` inputs are Alpine v3.24 `linux-lts-6.18.52-r0`, musl `1.2.6-r2`,
223
495
  `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.
496
+ `linux-x64-musl-baseline`. There is no BusyBox and no userland beyond Bun
497
+ itself.
226
498
 
227
499
  Every step is idempotent and `-f` re-downloads nothing: `scripts/fetch.sh`
228
500
  treats each pinned SHA-256 as the cache key, so a file already in `downloads/`
@@ -231,14 +503,17 @@ tampered with is fetched again and has to pass the same hash before a script
231
503
  extracts from it. Bumping a version changes both the file name and the hash, so
232
504
  it always refetches.
233
505
 
234
- ### Build pipeline
506
+ #### Build pipeline
507
+
508
+ The primary `bun ./index.js -fb --real` pipeline runs the steps below in order;
509
+ each writes files the next one reads. When selected, `--export` runs afterward
510
+ and copies the final `vda.img` to the invocation directory.
235
511
 
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.
512
+ ##### Pipeline inputs and outputs
238
513
 
239
514
  | script | reads | writes |
240
515
  | --- | --- | --- |
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` |
516
+ | `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
517
  | `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
518
  | `scripts/pack-initramfs.sh` | `initramfs/` | `build/initramfs.cpio.gz` |
244
519
  | `build-uki.sh` | that archive, kernel, stub | `build/cmdline`, `build/os-release`, `vda/EFI/BOOT/BOOTX64.EFI` |
@@ -251,30 +526,32 @@ The stages nest — the initramfs is baked into the UKI, and the UKI into the
251
526
  disk image — so a one-line edit to `initramfs/init.js` still needs both of:
252
527
 
253
528
  ```sh
254
- bun ./index.js -b # or: ./build-uki.sh && ./build-image.sh
529
+ bun ./index.js -b --real
255
530
  ```
256
531
 
257
532
  The kernel command line works the same way: `build-uki.sh` writes it to
258
533
  `build/cmdline` and compiles it into the UKI. Nothing reads it from disk at
259
534
  boot.
260
535
 
261
- ### UKI layout
536
+ #### UKI layout
262
537
 
263
538
  `build-uki.sh` appends sections to systemd's `linuxx64.efi.stub` with
264
539
  `objcopy`, at hand-picked VMAs because `objcopy` will not lay them out for you:
265
540
 
541
+ ##### UKI sections
542
+
266
543
  | section | offset from image base | contents | size |
267
544
  | --- | --- | --- | --- |
268
545
  | `.text` | +0x0 | the stub itself | 66 KB |
269
546
  | `.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 |
547
+ | `.cmdline` | +0x30000 | `build/cmdline` | 136 B |
548
+ | `.linux` | +0x2000000 | `kernel/vmlinuz-lts` | 14.5 MB |
549
+ | `.initrd` | +0x3000000 | `build/initramfs.cpio.gz` | 44.6 MB |
273
550
 
274
- The result is a single 46 MB PE32+ file holding kernel, initramfs and command
551
+ The current `--real` result is a single 59.3 MB PE32+ file holding kernel, initramfs and command
275
552
  line.
276
553
 
277
- ### Disk layout
554
+ #### Disk layout
278
555
 
279
556
  `build-image.sh` needs no mount, no loop device and no root privilege. It
280
557
  formats a standalone FAT32 image, fills it with mtools, builds the GPT with
@@ -295,79 +572,83 @@ LBA 262111.. backup GPT
295
572
  backup GPT. `EFI/BOOT/BOOTX64.EFI` is the removable-media fallback path, so the
296
573
  firmware runs it without any NVRAM boot entry.
297
574
 
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
575
+ #### Real-hardware boot details
310
576
 
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:
577
+ The physical boot chain is firmware → the UKI stub → its embedded
578
+ `.linux`/`.initrd`/`.cmdline` sections → kernel → `rdinit=/bin/bun`.
579
+ `--real` uses Alpine `linux-lts`, includes the xHCI and USB HID module chain,
580
+ and, with the currently pinned kernel, embeds this complete command line:
313
581
 
314
- ```sh
315
- bun ./index.js -fb --real
582
+ ```text
583
+ 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
584
  ```
317
585
 
318
- `--real` uses Alpine `linux-lts`, adds the xHCI and USB HID module chain, and
319
- changes the embedded console order to:
586
+ Serial kernel logging is retained, while the final console makes
587
+ `/dev/console` and the Bun REPL use the physical display and keyboard. This
588
+ path has been tested successfully on real hardware.
320
589
 
321
- ```text
322
- console=ttyS0,115200 console=tty0
323
- ```
590
+ #### QEMU development
324
591
 
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.
592
+ This optional path has been tested in native Android Termux and in an Ubuntu
593
+ 24.04 cloud VM with QEMU 8.2.2, TCG and OVMF. In native Termux, install the
594
+ runner separately from the PRoot build dependencies:
328
595
 
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.
596
+ ```sh
597
+ pkg install qemu-system-x86-64
598
+ ```
333
599
 
334
- From the Bun REPL, start the bundled Buninu userspace:
600
+ That package supplies OVMF at `$PREFIX/share/qemu/edk2-x86_64-code.fd`. Build
601
+ without `--real`; this selects Alpine `linux-virt`, omits the physical-hardware
602
+ module set, and puts `ttyS0` last so serial stdio owns `/dev/console`:
335
603
 
336
604
  ```text
337
- bun-repl> start()
605
+ console=tty0 console=ttyS0,115200 panic=0 PATH=/bin KERNEL_RELEASE=6.18.52-0-virt rdinit=/bin/bun -- -e import('/init.js')
338
606
  ```
339
607
 
340
- The bundled `jmi` terminal editor works without a network connection. For
341
- example, open a new `hlw.js` from the Buninu shell:
608
+ Build and boot it with:
342
609
 
343
610
  ```sh
344
- jmi hlw.js
611
+ bun ./index.js -fb
612
+
613
+ # When running inside PRoot with Termux's native qemu-system-x86_64,
614
+ # expose both the native executable and its OVMF path:
615
+ PREFIX=/data/data/com.termux/files/usr \
616
+ PATH="/data/data/com.termux/files/usr/bin:$PATH" \
617
+ bun ./index.js -r
345
618
  ```
346
619
 
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`.
620
+ If QEMU is installed inside the current Debian environment instead, plain
621
+ `bun ./index.js -r` is sufficient; `run-qemu.sh` will use Debian's OVMF path.
350
622
 
351
- Enter and save this content:
623
+ `bun ./index.js -r` runs `run-qemu.sh`, which boots `vda.img` on q35 under TCG
624
+ with 512 MiB, a virtio disk, virtio-net user networking, no display, and COM1
625
+ on stdio. Anything after `--` is appended to the QEMU command line. Ctrl-C
626
+ quits QEMU. Leaving the REPL does not end the session: `init.js` restarts it,
627
+ because a PID 1 that exits panics the kernel.
352
628
 
353
- ```js
354
- console.log("Hello world from real hardware");
355
- ```
629
+ The QEMU-only CLI stage is `-r`/`--run`; `-fbr` is the full virtual pipeline,
630
+ `-br` is its edit-and-boot loop, and arguments after `--` are passed through
631
+ verbatim, for example `-r -- -m 1G`. The corresponding repository entry is
632
+ `run-qemu.sh`. To smoke-test the [Section 1](#1-hello-world) EFI hello-world image instead, build
633
+ it and run the same `-r` stage; OVMF exposes its console over COM1.
634
+ The legacy `fetch-plus-build.sh` wrapper is equivalent to the virtual `-fb`
635
+ stages and does not select `--real`. `npm start` invokes the run stage.
356
636
 
357
- After leaving the editor, execute it locally:
637
+ For an even shorter initramfs loop, bypass the UKI and disk image:
358
638
 
359
639
  ```sh
360
- bun hlw.js
640
+ qemu-system-x86_64 -machine q35,accel=tcg -cpu max -m 512M \
641
+ -kernel kernel/vmlinuz-virt -initrd build/initramfs.cpio.gz \
642
+ -append "console=ttyS0,115200 panic=0 PATH=/bin rdinit=/bin/bun -- -e console.log(1+1)" \
643
+ -nic user,model=virtio-net-pci -display none -serial stdio -no-reboot
361
644
  ```
362
645
 
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`:
646
+ Only `./scripts/pack-initramfs.sh` must be rerun between these tests. A panic
647
+ parks the guest because of `panic=0`, so press Ctrl-C. This shortcut does not
648
+ exercise the UKI stub, GPT, physical console or hardware drivers; validate the
649
+ result afterward with `bun ./index.js -b --real` on the real UEFI path.
365
650
 
366
- ```sh
367
- bun ./index.js -fb --linux-lts
368
- ```
369
-
370
- #### Networking
651
+ ##### QEMU networking
371
652
 
372
653
  The NIC is QEMU user-mode networking (SLIRP): the guest is `10.0.2.15/24`
373
654
  behind a NAT with `10.0.2.2` as gateway and `10.0.2.3` as DNS, so it can reach
@@ -404,35 +685,45 @@ Two things to expect:
404
685
  * **QEMU write-locks `vda.img`.** A second instance fails with `Failed to get
405
686
  "write" lock`; copy the image first if you want two at once.
406
687
 
407
- ### Iterating on the boot
688
+ #### Command implementation
408
689
 
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.
690
+ The `/bin` commands described earlier are Bun scripts built on two shared
691
+ modules in `/lib`. `--help` uses `Bun.markdown.ansi` to render the Markdown
692
+ manual with hyperlinks. The shared pieces are:
413
693
 
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
- ```
694
+ * `/lib/dlopen.js` — one `bun:ffi` binding to `/lib/libc.musl-x86_64.so.1`
695
+ (`mount`, `umount2`, `ioctl`, `socket`, `sync`, `reboot`, `syscall`, …), `errno`/`strerror`,
696
+ a `SysError` class and the `showDocument()` helper behind every `--help`.
697
+ `init.js` keeps its own copy of the binding because it runs before `/proc`
698
+ exists and cannot import anything.
699
+ * `/lib/modprobe.js` — `modprobe(name)` resolves `modules.alias` and
700
+ `modules.dep` under `/lib/modules/<release>/` and calls `finit_module`
701
+ for each dependency in order. The kernel has no `/sbin/modprobe` to call
702
+ here, so `mount` preloads `ext4`+`jbd2`, `vfat`+NLS tables, `ntfs3`,
703
+ `sd_mod`, `nvme`, and so on itself.
420
704
 
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.
705
+ Which modules the image carries is the list in `fetch-alpine.sh`; the
706
+ `select_module` helper there pulls in dependencies from Alpine's
707
+ `modules.dep`, and `--real` adds common storage paths (`sd_mod`, `ahci`,
708
+ `ata_generic`, `pata_acpi`, `nvme`, Intel `vmd`, `usb-storage` and `uas`),
709
+ the wired NICs, Android USB-tethering drivers and the ACPI power drivers. Alpine's
710
+ kernels build all of these as modules; a self-built kernel with them `=y`
711
+ works the same way, the loaders just find nothing to load.
424
712
 
425
- ### Reaching JavaScript with nothing mounted
713
+ #### Reaching JavaScript with nothing mounted
426
714
 
427
- The default QEMU kernel command line is
715
+ The default `--real` image embeds this kernel command line (shown for the
716
+ currently pinned LTS release):
428
717
 
429
718
  ```text
430
- console=tty0 console=ttyS0,115200 panic=0 PATH=/bin rdinit=/bin/bun -- -e import('/init.js')
719
+ 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
720
  ```
432
721
 
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
722
+ The physical display (`tty0`) is the final console and therefore owns PID 1's
723
+ standard streams; serial logging remains available on `ttyS0`. The kernel
724
+ execs `/bin/bun` itself, with nothing native in between, and `init.js` mounts
725
+ `/proc`, `/sys`, `/dev`, `/tmp` and `/dev/pts` through `bun:ffi` as its first
726
+ action. Everything before `--` needs an `=`, because the kernel appends
436
727
  any bare word to the init argv instead of treating it as an environment
437
728
  assignment; everything after `--` becomes `argv[1..]` verbatim, with `argv[0]`
438
729
  set to the `rdinit=` path. Parameter order is otherwise free, so `rdinit=` is
@@ -463,8 +754,9 @@ Nothing has mounted devtmpfs at that point, so `scripts/pack-initramfs.sh`
463
754
  records `urandom` — along with `console`, which the kernel opens as init's
464
755
  fd 0/1/2 before the exec, and a few other harmless nodes — directly in the
465
756
  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.
757
+ For `--real`, `tty0` is listed last among the `console=` arguments so the
758
+ physical display owns `/dev/console`; `ttyS0` remains available for serial
759
+ kernel logs.
468
760
 
469
761
  The initramfs holds no `/init`; the kernel reaches Bun through `rdinit=` alone.
470
762
  The retired native bootstrap is kept in `test/build-init-bootstrap.sh`, which
@@ -474,10 +766,14 @@ it, repack, and boot it with `rdinit=/init`. Varying which filesystems such a
474
766
  bootstrap mounted before the exec is how the `/dev/urandom` requirement above
475
767
  was found.
476
768
 
477
- ### What init.js does
769
+ #### What init.js does
478
770
 
479
771
  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
772
+ `bun:ffi`, mounts `/proc`, `/sys`, `/dev`, `/tmp` and `/dev/pts` (without
773
+ the devpts mount every pty open fails with `ENODEV`, since `/dev/ptmx`
774
+ resolves through `/dev/pts/ptmx`), prints a greeting, and starts `node:repl`.
775
+ Submitting an empty or whitespace-only REPL line prints the welcome message
776
+ and the available `cfg` getters again. The loader and libc paths
481
777
  contain identical bytes but are deliberately distinct inodes:
482
778
 
483
779
  ```text
@@ -490,16 +786,75 @@ deadlocks, while the independent physical libc works. It provides `mount`,
490
786
  `waitpid`, networking calls, and the generic `syscall` entry point, so no
491
787
  custom `libinit.so` is needed.
492
788
 
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.
497
-
498
- The REPL exposes a global `bunmsh()` function. Calling it creates
499
- `/tmp/bunmsh-runtime`, installs the pinned `bunmsh@0.3.6` package there, and
500
- directly runs its `src/main.js` with a small explicit environment. The calls
501
- are synchronous so bunmsh exclusively owns the terminal until `exit` returns
502
- to the Bun REPL.
789
+ On a `--real` boot, init starts `/etc/resolv.conf` with `1.1.1.1` and
790
+ `8.8.8.8`; these are initial public resolvers until the user replaces them
791
+ with DNS appropriate for the local network. Hardware networking remains down
792
+ until `cfg.net` loads the packaged drivers and the user assigns an address and
793
+ route.
794
+
795
+ On a `--real` image the REPL also offers the getter-based `cfg` namespace:
796
+ `cfg.net`, `cfg.disk`, `cfg.power` and `cfg.all` run as soon as the property is read, without
797
+ parentheses. `cfg.net` loads every packaged network module (including PHY and bus support),
798
+ then reads the `modalias` of every PCI and USB network device, matches it
799
+ against the `pci:`/`usb:` lines
800
+ `fetch-alpine.sh` kept in `modules.alias` and lists the interfaces. `cfg.disk`
801
+ loads the packaged SATA/PATA/SCSI, NVMe/VMD and USB storage stacks so
802
+ their disks and partitions appear in `/dev`, then lists `/sys/class/block`. `cfg.all`
803
+ unconditionally attempts every packaged module and recursively loads its
804
+ declared dependencies. `--real` ships Intel `e1000`/`e1000e`/`igb`/`igc`,
805
+ Realtek `r8169`, Atheros `alx`, Broadcom `tg3`, common USB dongles, and
806
+ `rndis_host`/`cdc_ether`/`cdc_ncm`/`cdc_eem`/`cdc_subset`/`zaurus` for Android
807
+ USB tethering. `cfg.net` unconditionally attempts all of these packaged
808
+ network modules. `cfg.power` loads the ACPI
809
+ `battery`, `ac`, `button` and `thermal` modules through `/lib/modprobe.js`
810
+ and lists `/sys/class/power_supply` (a desktop without a battery simply
811
+ reports nothing registered). Nothing loads them at boot. PID 1 also installs
812
+ `uncaughtException`/`unhandledRejection` listeners: without them a stray
813
+ `Promise.reject()` at the prompt makes Bun exit, which is a kernel panic
814
+ (`Attempted to kill init!`).
815
+
816
+ `start()` launches the bundled Buninu userspace and its bunmsh (Bun Modern Shell) with
817
+ the current process environment preserved, while overriding `PATH` and `HOME`
818
+ for the userspace session.
819
+
820
+ ## Contents
821
+
822
+ - [Quick start](#quick-start)
823
+ * [Steps](#steps)
824
+ + [Build directly with bun x](#build-directly-with-bun-x)
825
+ + [Or build from a source checkout](#or-build-from-a-source-checkout)
826
+ + [Write the image to a USB drive](#write-the-image-to-a-usb-drive)
827
+ + [Booting from the USB drive](#booting-from-the-usb-drive)
828
+ - [Using Buninu Linux](#using-buninu-linux)
829
+ * [Basic configuration](#basic-configuration)
830
+ * [Editor quick start](#editor-quick-start)
831
+ * [Panes, terminals, and tabs](#panes-terminals-and-tabs)
832
+ + [Pane and tab controls](#pane-and-tab-controls)
833
+ * [Using Bun Modern Shell](#using-bun-modern-shell)
834
+ - [Commands inside /bin](#commands-inside-bin)
835
+ - [Environment and dependencies](#environment-and-dependencies)
836
+ * [Build environment](#build-environment)
837
+ * [Buninu Linux (Section 2)](#buninu-linux-section-2)
838
+ * [Hello world (Section 1)](#hello-world-section-1)
839
+ * [Retired native bootstrap](#retired-native-bootstrap)
840
+ - [Implementation details](#implementation-details)
841
+ * [1. Hello world](#1-hello-world)
842
+ * [2. Buninu Linux – Bun as PID 1](#2-buninu-linux-bun-as-pid-1)
843
+ + [Build flags](#build-flags)
844
+ + [Build pipeline](#build-pipeline)
845
+ - [Pipeline inputs and outputs](#pipeline-inputs-and-outputs)
846
+ + [UKI layout](#uki-layout)
847
+ - [UKI sections](#uki-sections)
848
+ + [Disk layout](#disk-layout)
849
+ + [Real-hardware boot details](#real-hardware-boot-details)
850
+ + [QEMU development](#qemu-development)
851
+ - [QEMU networking](#qemu-networking)
852
+ + [Command implementation](#command-implementation)
853
+ + [Reaching JavaScript with nothing mounted](#reaching-javascript-with-nothing-mounted)
854
+ + [What init.js does](#what-initjs-does)
855
+ - [Authorship](#authorship)
856
+ - [License](#license)
857
+ * [Bundled component licenses](#bundled-component-licenses)
503
858
 
504
859
  ## Authorship
505
860
 
@@ -521,11 +876,13 @@ The repository also commits four third-party platform binaries under
521
876
  `initramfs/lib/`, and the built image redistributes several more. Each stays
522
877
  under its own terms, with the full texts in [`LICENSES/`](LICENSES/):
523
878
 
879
+ ### Bundled component licenses
880
+
524
881
  | Component | License | Where |
525
882
  |---|---|---|
526
883
  | musl (`ld-musl-x86_64.so.1`, `libc.musl-x86_64.so.1`) | MIT | committed |
527
884
  | GCC runtime (`libgcc_s.so.1`, `libstdc++.so.6`) | GPL-3.0-or-later with the GCC Runtime Library Exception | committed |
528
- | Linux `virt`/`lts` kernels and VirtIO, networking, xHCI, USB and HID modules | GPL-2.0-only with the Linux syscall note | image only |
885
+ | Linux kernels and networking, storage, xHCI, USB and HID modules | GPL-2.0-only with the Linux syscall note | image only |
529
886
  | systemd EFI stub | LGPL-2.1-or-later | image only |
530
887
  | Bun | MIT, plus the licenses of what it statically links | image only |
531
888