typescript-virtual-container 1.4.9 → 1.5.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.
Files changed (129) hide show
  1. package/.vscode/settings.json +1 -1
  2. package/README.md +117 -79
  3. package/builds/self-standalone.mjs +1768 -0
  4. package/builds/standalone-wo-sftp.js +735 -267
  5. package/builds/standalone.cjs +735 -267
  6. package/bun.lock +3 -3
  7. package/dist/SSHMimic/exec.js +2 -2
  8. package/dist/SSHMimic/exec.js.map +1 -1
  9. package/dist/SSHMimic/index.d.ts.map +1 -1
  10. package/dist/SSHMimic/index.js +2 -1
  11. package/dist/SSHMimic/index.js.map +1 -1
  12. package/dist/SSHMimic/sftp.d.ts.map +1 -1
  13. package/dist/SSHMimic/sftp.js +4 -3
  14. package/dist/SSHMimic/sftp.js.map +1 -1
  15. package/dist/VirtualFileSystem/index.d.ts +14 -0
  16. package/dist/VirtualFileSystem/index.d.ts.map +1 -1
  17. package/dist/VirtualFileSystem/index.js +51 -3
  18. package/dist/VirtualFileSystem/index.js.map +1 -1
  19. package/dist/VirtualFileSystem/journal.d.ts.map +1 -1
  20. package/dist/VirtualFileSystem/journal.js +11 -5
  21. package/dist/VirtualFileSystem/journal.js.map +1 -1
  22. package/dist/VirtualShell/shell.js +12 -12
  23. package/dist/VirtualShell/shell.js.map +1 -1
  24. package/dist/VirtualUserManager/index.js +8 -11
  25. package/dist/VirtualUserManager/index.js.map +1 -1
  26. package/dist/commands/apt.js +3 -3
  27. package/dist/commands/apt.js.map +1 -1
  28. package/dist/commands/cd.d.ts.map +1 -1
  29. package/dist/commands/cd.js +2 -1
  30. package/dist/commands/cd.js.map +1 -1
  31. package/dist/commands/helpers.d.ts +1 -1
  32. package/dist/commands/helpers.d.ts.map +1 -1
  33. package/dist/commands/helpers.js +3 -2
  34. package/dist/commands/helpers.js.map +1 -1
  35. package/dist/commands/index.d.ts +1 -1
  36. package/dist/commands/index.d.ts.map +1 -1
  37. package/dist/commands/index.js +1 -1
  38. package/dist/commands/index.js.map +1 -1
  39. package/dist/commands/lsb-release.js +1 -1
  40. package/dist/commands/lsb-release.js.map +1 -1
  41. package/dist/commands/runtime.d.ts +2 -0
  42. package/dist/commands/runtime.d.ts.map +1 -1
  43. package/dist/commands/runtime.js +5 -1
  44. package/dist/commands/runtime.js.map +1 -1
  45. package/dist/modules/linuxRootfs.d.ts +9 -5
  46. package/dist/modules/linuxRootfs.d.ts.map +1 -1
  47. package/dist/modules/linuxRootfs.js +1079 -148
  48. package/dist/modules/linuxRootfs.js.map +1 -1
  49. package/dist/self-standalone.js +22 -12
  50. package/dist/self-standalone.js.map +1 -1
  51. package/docs/assets/hierarchy.js +1 -1
  52. package/docs/classes/HoneyPot.html +9 -9
  53. package/docs/classes/IdleManager.html +8 -8
  54. package/docs/classes/SshClient.html +18 -18
  55. package/docs/classes/VirtualFileSystem.html +34 -34
  56. package/docs/classes/VirtualPackageManager.html +13 -13
  57. package/docs/classes/VirtualSftpServer.html +3 -3
  58. package/docs/classes/VirtualShell.html +28 -28
  59. package/docs/classes/VirtualSshServer.html +5 -5
  60. package/docs/classes/VirtualUserManager.html +27 -27
  61. package/docs/functions/assertDiff.html +2 -2
  62. package/docs/functions/diffSnapshots.html +2 -2
  63. package/docs/functions/formatDiff.html +2 -2
  64. package/docs/functions/getArg.html +2 -2
  65. package/docs/functions/getFlag.html +2 -2
  66. package/docs/functions/ifFlag.html +2 -2
  67. package/docs/hierarchy.html +1 -1
  68. package/docs/index.html +10 -8
  69. package/docs/interfaces/AuditLogEntry.html +3 -3
  70. package/docs/interfaces/CommandContext.html +12 -12
  71. package/docs/interfaces/CommandResult.html +13 -13
  72. package/docs/interfaces/ExecStream.html +6 -6
  73. package/docs/interfaces/HoneyPotStats.html +3 -3
  74. package/docs/interfaces/IdleManagerOptions.html +3 -3
  75. package/docs/interfaces/InstalledPackage.html +11 -11
  76. package/docs/interfaces/NanoEditorSession.html +5 -5
  77. package/docs/interfaces/PackageDefinition.html +14 -14
  78. package/docs/interfaces/PackageFile.html +5 -5
  79. package/docs/interfaces/PasswordChallenge.html +9 -9
  80. package/docs/interfaces/RemoveOptions.html +3 -3
  81. package/docs/interfaces/ShellEnv.html +4 -4
  82. package/docs/interfaces/ShellModule.html +8 -8
  83. package/docs/interfaces/ShellProperties.html +5 -5
  84. package/docs/interfaces/ShellStream.html +7 -7
  85. package/docs/interfaces/SudoChallenge.html +9 -9
  86. package/docs/interfaces/VfsBaseNode.html +7 -7
  87. package/docs/interfaces/VfsDiff.html +6 -6
  88. package/docs/interfaces/VfsDiffEntry.html +4 -4
  89. package/docs/interfaces/VfsDiffModified.html +6 -6
  90. package/docs/interfaces/VfsDirectoryNode.html +8 -8
  91. package/docs/interfaces/VfsFileNode.html +9 -9
  92. package/docs/interfaces/VfsOptions.html +6 -6
  93. package/docs/interfaces/VfsSnapshot.html +3 -3
  94. package/docs/interfaces/VfsSnapshotBaseNode.html +4 -4
  95. package/docs/interfaces/VfsSnapshotDirectoryNode.html +5 -5
  96. package/docs/interfaces/VfsSnapshotFileNode.html +6 -6
  97. package/docs/interfaces/VirtualActiveSession.html +7 -7
  98. package/docs/interfaces/VirtualSftpServerOptions.html +3 -3
  99. package/docs/interfaces/VirtualShellVfsLike.html +3 -3
  100. package/docs/interfaces/VirtualShellVfsOptions.html +3 -3
  101. package/docs/interfaces/WriteFileOptions.html +4 -4
  102. package/docs/modules.html +1 -1
  103. package/docs/types/ArgParseOptions.html +3 -3
  104. package/docs/types/CommandMode.html +2 -2
  105. package/docs/types/CommandOutcome.html +2 -2
  106. package/docs/types/IdleState.html +1 -1
  107. package/docs/types/VfsNodeStats.html +2 -2
  108. package/docs/types/VfsNodeType.html +2 -2
  109. package/docs/types/VfsPersistenceMode.html +2 -2
  110. package/docs/types/VfsSnapshotNode.html +2 -2
  111. package/package.json +3 -3
  112. package/src/SSHMimic/exec.ts +2 -2
  113. package/src/SSHMimic/index.ts +2 -1
  114. package/src/SSHMimic/sftp.ts +4 -3
  115. package/src/VirtualFileSystem/index.ts +46 -3
  116. package/src/VirtualFileSystem/journal.ts +10 -5
  117. package/src/VirtualShell/shell.ts +12 -12
  118. package/src/VirtualUserManager/index.ts +11 -11
  119. package/src/commands/apt.ts +3 -3
  120. package/src/commands/cd.ts +2 -1
  121. package/src/commands/helpers.ts +3 -2
  122. package/src/commands/index.ts +1 -1
  123. package/src/commands/lsb-release.ts +1 -1
  124. package/src/commands/runtime.ts +6 -1
  125. package/src/modules/linuxRootfs.ts +1293 -207
  126. package/src/self-standalone.ts +26 -12
  127. package/tests/new-features.test.ts +2 -2
  128. package/tests/sftp.test.ts +13 -13
  129. package/builds/self-standalone.js +0 -1299
@@ -6,12 +6,16 @@
6
6
  * Called once during VirtualShell initialization. Idempotent — skips
7
7
  * paths that already exist so FS-mode snapshots survive restarts.
8
8
  *
9
+ * Emulation fidelity: modelled after a Fortune GNU/Linux 1.0 (Nyx)
10
+ * container with Firecracker MicroVM kernel 6.x, virtio block devices
11
+ * (vda/vdb/vdc/vdd), cgroups v1 hierarchy, Node.js 22, Python 3.12,
12
+ * GCC 13, OpenJDK 21, and a Fortune-style package database.
13
+ *
9
14
  * Public API:
10
- * - bootstrapLinuxRootfs() one-shot boot (VirtualShell calls this)
11
- * - refreshProc() refresh /proc/* (call on session changes)
12
- * - syncEtcPasswd() sync /etc/passwd|group|shadow from UserManager
13
- * - createLinuxRootfsEngine() returns engine with .boot() + .tick() for
14
- * runtimes that want a live refresh loop
15
+ * - bootstrapLinuxRootfs() one-shot boot (VirtualShell calls this)
16
+ * - refreshProc() refresh /proc/* (call on session changes)
17
+ * - syncEtcPasswd() sync /etc/passwd|group|shadow from UserManager
18
+ * - createLinuxRootfsEngine() engine with .boot() + .tick() for live loops
15
19
  */
16
20
 
17
21
  import * as os from "node:os";
@@ -33,7 +37,6 @@ function ensureFile(
33
37
  content: string,
34
38
  mode = 0o644,
35
39
  ): void {
36
- // Use lazy stub — no Buffer allocated until the file is actually read or overwritten
37
40
  vfs.writeStub(path, content, mode);
38
41
  }
39
42
 
@@ -60,7 +63,7 @@ function bootstrapEtc(
60
63
  ): void {
61
64
  ensureDir(vfs, "/etc");
62
65
 
63
- // os-release — authoritative distro identity used by neofetch, lsb_release
66
+ // os-release — Fortune Nyx identity
64
67
  ensureFile(
65
68
  vfs,
66
69
  "/etc/os-release",
@@ -70,42 +73,60 @@ function bootstrapEtc(
70
73
  `ID=fortune`,
71
74
  `ID_LIKE=debian`,
72
75
  `HOME_URL="https://github.com/itsrealfortune/typescript-virtual-container"`,
73
- `VERSION_CODENAME=aurora`,
74
- `VERSION_ID="1.0"`,
76
+ `VERSION_CODENAME=nyx`,
77
+ `VERSION_ID="24.04"`,
78
+ `FORTUNE_CODENAME=nyx`,
75
79
  ].join("\n")}\n`,
76
80
  );
77
81
 
78
- ensureFile(vfs, "/etc/debian_version", "12.0\n");
82
+ ensureFile(vfs, "/etc/debian_version", "nyx/stable\n");
79
83
  ensureFile(vfs, "/etc/hostname", `${hostname}\n`);
80
- ensureFile(vfs, "/etc/shells", "/bin/sh\n/bin/bash\n/usr/bin/bash\n");
84
+ ensureFile(
85
+ vfs,
86
+ "/etc/shells",
87
+ "/bin/sh\n/bin/bash\n/usr/bin/bash\n/bin/dash\n/usr/bin/dash\n",
88
+ );
81
89
  ensureFile(
82
90
  vfs,
83
91
  "/etc/profile",
84
92
  `${[
85
- "export PATH=/usr/local/bin:/usr/bin:/bin",
93
+ "export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
86
94
  "export PS1='\\u@\\h:\\w\\$ '",
87
95
  ].join("\n")}\n`,
88
96
  );
89
97
 
90
- ensureFile(vfs, "/etc/issue", "Fortune GNU/Linux 1.0 \\n \\l\n");
98
+ ensureFile(vfs, "/etc/issue", "Fortune GNU/Linux 24.04 LTS \\n \\l\n");
99
+ ensureFile(vfs, "/etc/issue.net", "Fortune GNU/Linux 24.04 LTS\n");
91
100
  ensureFile(
92
101
  vfs,
93
102
  "/etc/motd",
94
103
  ["", `Welcome to ${props.os}`, `Kernel: ${props.kernel}`, ""].join("\n"),
95
104
  );
105
+ ensureFile(vfs, "/etc/lsb-release",
106
+ `${[
107
+ "DISTRIB_ID=Fortune",
108
+ "DISTRIB_RELEASE=24.04",
109
+ "DISTRIB_CODENAME=nyx",
110
+ `DISTRIB_DESCRIPTION="${props.os}"`,
111
+ ].join("\n")}\n`,
112
+ );
96
113
 
97
- // APT sources
114
+ // APT — Fortune Nyx sources
98
115
  ensureDir(vfs, "/etc/apt");
99
116
  ensureDir(vfs, "/etc/apt/sources.list.d");
117
+ ensureDir(vfs, "/etc/apt/trusted.gpg.d");
118
+ ensureDir(vfs, "/etc/apt/keyrings");
100
119
  ensureFile(
101
120
  vfs,
102
121
  "/etc/apt/sources.list",
103
122
  `${[
104
- "# Fortune GNU/Linux package sources",
105
- "deb [virtual] fortune://packages.fortune.local aurora main contrib",
106
- "deb [virtual] fortune://security.fortune.local aurora-security main",
123
+ "# Fortune GNU/Linux package sources (Fortune 1.0 Nyx)",
124
+ "deb [virtual] fortune://packages.fortune.local nyx main contrib non-free",
125
+ "deb [virtual] fortune://packages.fortune.local nyx-updates main contrib non-free",
126
+ "deb [virtual] fortune://security.fortune.local nyx-security main",
107
127
  ].join("\n")}\n`,
108
128
  );
129
+ ensureFile(vfs, "/etc/apt/apt.conf.d/70debconf", `// debconf config\n`);
109
130
 
110
131
  // network
111
132
  ensureDir(vfs, "/etc/network");
@@ -120,6 +141,18 @@ function bootstrapEtc(
120
141
  "iface eth0 inet dhcp",
121
142
  ].join("\n")}\n`,
122
143
  );
144
+ ensureDir(vfs, "/etc/netplan");
145
+ ensureFile(
146
+ vfs,
147
+ "/etc/netplan/01-eth0.yaml",
148
+ `${[
149
+ "network:",
150
+ " version: 2",
151
+ " ethernets:",
152
+ " eth0:",
153
+ " dhcp4: true",
154
+ ].join("\n")}\n`,
155
+ );
123
156
 
124
157
  ensureFile(vfs, "/etc/resolv.conf", "nameserver 1.1.1.1\nnameserver 8.8.8.8\n");
125
158
 
@@ -130,29 +163,56 @@ function bootstrapEtc(
130
163
  "127.0.0.1 localhost",
131
164
  `127.0.1.1 ${hostname}`,
132
165
  "::1 localhost ip6-localhost ip6-loopback",
166
+ "fe00::0 ip6-localnet",
167
+ "ff00::0 ip6-mcastprefix",
168
+ "ff02::1 ip6-allnodes",
169
+ "ff02::2 ip6-allrouters",
170
+ ].join("\n")}\n`,
171
+ );
172
+
173
+ ensureFile(vfs, "/etc/nsswitch.conf",
174
+ `${[
175
+ "passwd: files systemd",
176
+ "group: files systemd",
177
+ "shadow: files",
178
+ "hosts: files dns",
179
+ "networks: files",
180
+ "protocols: db files",
181
+ "services: db files",
182
+ "ethers: db files",
183
+ "rpc: db files",
133
184
  ].join("\n")}\n`,
134
185
  );
135
186
 
136
187
  ensureDir(vfs, "/etc/cron.d");
188
+ ensureDir(vfs, "/etc/cron.daily");
189
+ ensureDir(vfs, "/etc/cron.hourly");
190
+ ensureDir(vfs, "/etc/cron.weekly");
191
+ ensureDir(vfs, "/etc/cron.monthly");
137
192
  ensureDir(vfs, "/etc/init.d");
138
193
  ensureDir(vfs, "/etc/systemd");
139
194
  ensureDir(vfs, "/etc/systemd/system");
195
+ ensureDir(vfs, "/etc/systemd/system/multi-user.target.wants");
196
+ ensureDir(vfs, "/etc/systemd/network");
197
+ ensureFile(vfs, "/etc/systemd/system.conf",
198
+ "[Manager]\nDefaultTimeoutStartSec=90s\nDefaultTimeoutStopSec=90s\n",
199
+ );
140
200
 
141
- // fstab
201
+ // fstab — virtio block devices matching Firecracker layout
142
202
  ensureFile(
143
203
  vfs,
144
204
  "/etc/fstab",
145
205
  `${[
146
- "# <file system> <mount point> <type> <options> <dump> <pass>",
147
- "UUID=00000000-0000-0000-0000-000000000001 / ext4 errors=remount-ro 0 1",
148
- "UUID=00000000-0000-0000-0000-000000000002 /boot ext4 defaults 0 2",
149
- "UUID=00000000-0000-0000-0000-000000000003 none swap sw 0 0",
150
- "tmpfs /tmp tmpfs defaults,noatime 0 0",
151
- "tmpfs /run tmpfs defaults,noatime 0 0",
206
+ "# <file system> <mount point> <type> <options> <dump> <pass>",
207
+ "/dev/vda / ext4 rw,relatime,resuid=65534,resgid=65534 0 1",
208
+ "/dev/vdb /opt/rclone squashfs ro,relatime,errors=continue 0 0",
209
+ "tmpfs /tmp tmpfs defaults,noatime 0 0",
210
+ "tmpfs /run tmpfs defaults,noatime 0 0",
211
+ "tmpfs /dev/shm tmpfs rw,relatime 0 0",
152
212
  ].join("\n")}\n`,
153
213
  );
154
214
 
155
- // login.defs — useradd/passwd defaults
215
+ // login.defs
156
216
  ensureFile(
157
217
  vfs,
158
218
  "/etc/login.defs",
@@ -174,32 +234,167 @@ function bootstrapEtc(
174
234
 
175
235
  // security + pam
176
236
  ensureDir(vfs, "/etc/security");
177
- ensureFile(vfs, "/etc/security/limits.conf", "# /etc/security/limits.conf\n");
237
+ ensureFile(vfs, "/etc/security/limits.conf",
238
+ "# /etc/security/limits.conf\n* soft nofile 1024\n* hard nofile 65536\n",
239
+ );
178
240
  ensureFile(vfs, "/etc/security/access.conf", "# /etc/security/access.conf\n");
179
241
 
180
242
  ensureDir(vfs, "/etc/pam.d");
181
- ensureFile(vfs, "/etc/pam.d/common-auth", "auth required pam_unix.so\n");
182
- ensureFile(vfs, "/etc/pam.d/common-account", "account required pam_unix.so\n");
243
+ ensureFile(vfs, "/etc/pam.d/common-auth",
244
+ "auth [success=1 default=ignore] pam_unix.so nullok\nauth requisite pam_deny.so\nauth required pam_permit.so\n",
245
+ );
246
+ ensureFile(vfs, "/etc/pam.d/common-account",
247
+ "account [success=1 new_authtok_reqd=done default=ignore] pam_unix.so\naccount requisite pam_deny.so\naccount required pam_permit.so\n",
248
+ );
183
249
  ensureFile(vfs, "/etc/pam.d/common-password",
184
- "password required pam_unix.so obscure sha512\n");
185
- ensureFile(vfs, "/etc/pam.d/common-session", "session required pam_unix.so\n");
250
+ "password [success=1 default=ignore] pam_unix.so obscure sha512\npassword requisite pam_deny.so\npassword required pam_permit.so\n",
251
+ );
252
+ ensureFile(vfs, "/etc/pam.d/common-session",
253
+ "session [default=1] pam_permit.so\nsession requisite pam_deny.so\nsession required pam_permit.so\nsession optional pam_umask.so\nsession required pam_unix.so\n",
254
+ );
186
255
  ensureFile(vfs, "/etc/pam.d/sshd",
187
- "@include common-auth\n@include common-account\n@include common-session\n");
256
+ "@include common-auth\n@include common-account\n@include common-session\n",
257
+ );
258
+ ensureFile(vfs, "/etc/pam.d/login",
259
+ "@include common-auth\n@include common-account\n@include common-session\n",
260
+ );
261
+ ensureFile(vfs, "/etc/pam.d/sudo",
262
+ "@include common-auth\n@include common-account\n@include common-session\n",
263
+ );
188
264
 
189
- // sudo config
265
+ // sudo
190
266
  ensureDir(vfs, "/etc/sudoers.d");
191
267
  ensureFile(vfs, "/etc/sudoers",
192
- "root ALL=(ALL:ALL) ALL\n%sudo ALL=(ALL:ALL) ALL\n", 0o440);
268
+ "Defaults\tenv_reset\nDefaults\tmail_badpass\nDefaults\tsecure_path=\"/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"\nroot ALL=(ALL:ALL) ALL\n%sudo ALL=(ALL:ALL) ALL\n",
269
+ 0o440,
270
+ );
271
+ ensureFile(vfs, "/etc/sudoers.d/README",
272
+ "# Files in this directory are parsed by sudo, if the file is not a backup.\n",
273
+ 0o440,
274
+ );
193
275
 
194
276
  // ld
195
277
  ensureFile(vfs, "/etc/ld.so.conf", "include /etc/ld.so.conf.d/*.conf\n");
196
278
  ensureDir(vfs, "/etc/ld.so.conf.d");
197
- ensureFile(vfs, "/etc/ld.so.conf.d/x86_64-linux-gnu.conf", "/lib/x86_64-linux-gnu\n/usr/lib/x86_64-linux-gnu\n");
279
+ ensureFile(vfs, "/etc/ld.so.conf.d/x86_64-linux-gnu.conf",
280
+ "/lib/x86_64-linux-gnu\n/usr/lib/x86_64-linux-gnu\n",
281
+ );
282
+ ensureFile(vfs, "/etc/ld.so.conf.d/fakeroot.conf",
283
+ "/usr/lib/x86_64-linux-gnu/libfakeroot\n",
284
+ );
198
285
 
199
286
  // locale + timezone
200
287
  ensureFile(vfs, "/etc/locale.conf", "LANG=en_US.UTF-8\n");
288
+ ensureFile(vfs, "/etc/locale.gen", "en_US.UTF-8 UTF-8\n");
289
+ ensureFile(vfs, "/etc/default/locale", "LANG=en_US.UTF-8\n");
201
290
  ensureFile(vfs, "/etc/timezone", "UTC\n");
202
291
  ensureFile(vfs, "/etc/localtime", "UTC\n");
292
+
293
+ // environment
294
+ ensureFile(vfs, "/etc/environment",
295
+ "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\n",
296
+ );
297
+
298
+ // adduser.conf
299
+ ensureFile(vfs, "/etc/adduser.conf",
300
+ `${[
301
+ "DSHELL=/bin/bash",
302
+ "DHOME=/home",
303
+ "GROUPHOMES=no",
304
+ "LETTERHOMES=no",
305
+ "SKEL=/etc/skel",
306
+ "FIRST_SYSTEM_UID=100",
307
+ "LAST_SYSTEM_UID=999",
308
+ "FIRST_SYSTEM_GID=100",
309
+ "LAST_SYSTEM_GID=999",
310
+ "FIRST_UID=1000",
311
+ "LAST_UID=59999",
312
+ "FIRST_GID=1000",
313
+ "LAST_GID=59999",
314
+ "USERGROUPS=yes",
315
+ "NAME_REGEX=\"^[a-z][-a-z0-9_]*$\"",
316
+ "SYS_NAME_REGEX=\"^[a-z_][-a-z0-9_]*$\"",
317
+ ].join("\n")}\n`,
318
+ );
319
+
320
+ // /etc/skel
321
+ ensureDir(vfs, "/etc/skel");
322
+ ensureFile(vfs, "/etc/skel/.bashrc",
323
+ `${[
324
+ "# ~/.bashrc: executed by bash(1) for non-login shells.",
325
+ "case $- in",
326
+ " *i*) ;;",
327
+ " *) return;;",
328
+ "esac",
329
+ "HISTCONTROL=ignoreboth",
330
+ "HISTSIZE=1000",
331
+ "HISTFILESIZE=2000",
332
+ "shopt -s histappend",
333
+ "shopt -s checkwinsize",
334
+ "alias ll='ls -alF'",
335
+ "alias la='ls -A'",
336
+ "alias l='ls -CF'",
337
+ ].join("\n")}\n`,
338
+ );
339
+ ensureFile(vfs, "/etc/skel/.bash_logout", "# ~/.bash_logout\n");
340
+ ensureFile(vfs, "/etc/skel/.profile",
341
+ "# ~/.profile\n[ -n \"$BASH_VERSION\" ] && [ -f \"$HOME/.bashrc\" ] && . \"$HOME/.bashrc\"\n",
342
+ );
343
+
344
+ // alternatives
345
+ ensureDir(vfs, "/etc/alternatives");
346
+ const alternatives: [string, string][] = [
347
+ ["python3", "/usr/bin/python3.12"],
348
+ ["python", "/usr/bin/python3.12"],
349
+ ["editor", "/usr/bin/nano"],
350
+ ["vi", "/usr/bin/nano"],
351
+ ["cc", "/usr/bin/gcc"],
352
+ ["gcc", "/usr/bin/gcc-13"],
353
+ ["g++", "/usr/bin/g++-13"],
354
+ ["java", "/usr/lib/jvm/java-21-openjdk-amd64/bin/java"],
355
+ ["node", "/usr/bin/node"],
356
+ ["npm", "/usr/bin/npm"],
357
+ ["awk", "/usr/bin/mawk"],
358
+ ["pager", "/usr/bin/less"],
359
+ ];
360
+ for (const [name, target] of alternatives) {
361
+ ensureFile(vfs, `/etc/alternatives/${name}`, target);
362
+ }
363
+
364
+ // java
365
+ ensureDir(vfs, "/etc/java-21-openjdk");
366
+ ensureDir(vfs, "/etc/java-21-openjdk/security");
367
+ ensureFile(vfs, "/etc/java-21-openjdk/security/java.security", "# java.security\n");
368
+ ensureFile(vfs, "/etc/java-21-openjdk/logging.properties", "# logging.properties\n");
369
+
370
+ // misc
371
+ ensureFile(vfs, "/etc/bash.bashrc",
372
+ "# System-wide .bashrc\n[ -z \"$PS1\" ] && return\n",
373
+ );
374
+ ensureFile(vfs, "/etc/inputrc",
375
+ "# /etc/inputrc\n$include /etc/inputrc.d\nset bell-style none\n",
376
+ );
377
+ ensureFile(vfs, "/etc/magic", "# magic\n");
378
+ ensureFile(vfs, "/etc/magic.mime", "# magic.mime\n");
379
+ ensureFile(vfs, "/etc/papersize", "a4\n");
380
+ ensureFile(vfs, "/etc/ucf.conf", "# ucf.conf\n");
381
+ ensureFile(vfs, "/etc/gai.conf",
382
+ "# getaddrinfo() configuration\nlabel ::1/128 0\nprecedence ::1/128 50\n",
383
+ );
384
+ ensureFile(vfs, "/etc/services",
385
+ "# Network services\nftp 21/tcp\nssh 22/tcp\nsmtp 25/tcp\nhttp 80/tcp\nhttps 443/tcp\n",
386
+ );
387
+ ensureFile(vfs, "/etc/protocols",
388
+ "# protocols\nip 0 IP\nicmp 1 ICMP\ntcp 6 TCP\nudp 17 UDP\n",
389
+ );
390
+
391
+ ensureDir(vfs, "/etc/profile.d");
392
+ ensureFile(vfs, "/etc/profile.d/01-locale-fix.sh",
393
+ "[ -z \"$LANG\" ] && export LANG=en_US.UTF-8\n",
394
+ );
395
+ ensureFile(vfs, "/etc/profile.d/apps-bin-path.sh",
396
+ "export PATH=\"$PATH:/snap/bin\"\n",
397
+ );
203
398
  }
204
399
 
205
400
  // ─── /etc/passwd + /etc/group + /etc/shadow ─────────────────────────────────
@@ -217,8 +412,26 @@ export function syncEtcPasswd(
217
412
  const passwdLines = [
218
413
  "root:x:0:0:root:/root:/bin/bash",
219
414
  "daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin",
415
+ "bin:x:2:2:bin:/bin:/usr/sbin/nologin",
416
+ "sys:x:3:3:sys:/dev:/usr/sbin/nologin",
417
+ "sync:x:4:65534:sync:/bin:/bin/sync",
418
+ "games:x:5:60:games:/usr/games:/usr/sbin/nologin",
419
+ "man:x:6:12:man:/var/cache/man:/usr/sbin/nologin",
420
+ "lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin",
421
+ "mail:x:8:8:mail:/var/mail:/usr/sbin/nologin",
422
+ "news:x:9:9:news:/var/spool/news:/usr/sbin/nologin",
423
+ "uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin",
424
+ "proxy:x:13:13:proxy:/bin:/usr/sbin/nologin",
220
425
  "www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin",
426
+ "backup:x:34:34:backup:/var/backups:/usr/sbin/nologin",
427
+ "list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin",
428
+ "irc:x:39:39:ircd:/run/ircd:/usr/sbin/nologin",
429
+ "_apt:x:42:65534::/nonexistent:/usr/sbin/nologin",
221
430
  "nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin",
431
+ "messagebus:x:100:106::/nonexistent:/usr/sbin/nologin",
432
+ "systemd-network:x:998:998:systemd Network Management:/:/usr/sbin/nologin",
433
+ "systemd-resolve:x:999:999:systemd Resolver:/:/usr/sbin/nologin",
434
+ "polkitd:x:997:997:polkit:/nonexistent:/usr/sbin/nologin",
222
435
  ];
223
436
 
224
437
  let uid = 1000;
@@ -230,19 +443,65 @@ export function syncEtcPasswd(
230
443
 
231
444
  vfs.writeFile("/etc/passwd", `${passwdLines.join("\n")}\n`);
232
445
 
446
+ const sudoers = userList.filter((u) => users.isSudoer(u)).join(",");
447
+ const nonRootUsers = userList.filter((u) => u !== "root").join(",");
448
+
233
449
  const groupLines = [
234
450
  "root:x:0:",
235
451
  "daemon:x:1:",
236
- `sudo:x:27:${userList.filter((u) => users.isSudoer(u)).join(",")}`,
237
- `users:x:100:${userList.filter((u) => u !== "root").join(",")}`,
452
+ "bin:x:2:",
453
+ "sys:x:3:",
454
+ "adm:x:4:syslog",
455
+ "tty:x:5:",
456
+ "disk:x:6:",
457
+ "lp:x:7:",
458
+ "mail:x:8:",
459
+ "news:x:9:",
460
+ "uucp:x:10:",
461
+ "man:x:12:",
462
+ "proxy:x:13:",
463
+ "kmem:x:15:",
464
+ "dialout:x:20:",
465
+ "fax:x:21:",
466
+ "voice:x:22:",
467
+ "cdrom:x:24:",
468
+ "floppy:x:25:",
469
+ "tape:x:26:",
470
+ `sudo:x:27:${sudoers}`,
471
+ "audio:x:29:",
472
+ "dip:x:30:",
473
+ "www-data:x:33:",
474
+ "backup:x:34:",
475
+ "operator:x:37:",
476
+ "list:x:38:",
477
+ "irc:x:39:",
478
+ "src:x:40:",
479
+ "_apt:x:42:",
480
+ "shadow:x:42:",
481
+ "utmp:x:43:",
482
+ "video:x:44:",
483
+ "sasl:x:45:",
484
+ "plugdev:x:46:",
485
+ "staff:x:50:",
486
+ "games:x:60:",
487
+ `users:x:100:${nonRootUsers}`,
238
488
  "nogroup:x:65534:",
489
+ "messagebus:x:106:",
490
+ "systemd-network:x:998:",
491
+ "systemd-resolve:x:999:",
492
+ "polkitd:x:997:",
239
493
  ];
240
494
  vfs.writeFile("/etc/group", `${groupLines.join("\n")}\n`);
241
495
 
242
- // shadow — fake hashes, never real credentials
243
496
  const shadowLines = [
244
497
  "root:*:19000:0:99999:7:::",
245
498
  "daemon:*:19000:0:99999:7:::",
499
+ "nobody:*:19000:0:99999:7:::",
500
+ "messagebus:*:19000:0:99999:7:::",
501
+ "_apt:*:19000:0:99999:7:::",
502
+ "systemd-network:!:19000:::::::",
503
+ "systemd-resolve:!:19000:::::::",
504
+ "polkitd:!:19000:::::::",
246
505
  ];
247
506
  for (const u of userList) {
248
507
  if (u === "root") continue;
@@ -253,13 +512,11 @@ export function syncEtcPasswd(
253
512
 
254
513
  // ─── /proc helpers ───────────────────────────────────────────────────────────
255
514
 
256
- /** Derive a stable virtual PID from a tty string e.g. "pts/0" → 1000 */
257
515
  function ttyToPid(tty: string): number {
258
516
  const match = tty.match(/(\d+)$/);
259
517
  return 1000 + (match?.[1] ? parseInt(match[1], 10) : 0);
260
518
  }
261
519
 
262
- /** Write /proc/<pid>/ subtree for a single virtual process */
263
520
  function writeProcPid(
264
521
  vfs: VirtualFileSystem,
265
522
  pid: number,
@@ -273,6 +530,7 @@ function writeProcPid(
273
530
  ensureDir(vfs, dir);
274
531
  ensureDir(vfs, `${dir}/fd`);
275
532
  ensureDir(vfs, `${dir}/fdinfo`);
533
+ ensureDir(vfs, `${dir}/net`);
276
534
 
277
535
  const uptimeSec = Math.floor((Date.now() - new Date(startedAt).getTime()) / 1000);
278
536
  const comm = cmdline.split(/\s+/)[0] ?? "bash";
@@ -284,20 +542,57 @@ function writeProcPid(
284
542
  `${dir}/status`,
285
543
  `${[
286
544
  `Name: ${comm}`,
545
+ `Umask: 0022`,
287
546
  `State: S (sleeping)`,
547
+ `Tgid: ${pid}`,
288
548
  `Pid: ${pid}`,
289
549
  `PPid: 1`,
550
+ `TracerPid: 0`,
290
551
  `Uid: 0\t0\t0\t0`,
291
552
  `Gid: 0\t0\t0\t0`,
292
- `VmRSS: 4096 kB`,
293
- `VmSize: 16384 kB`,
553
+ `FDSize: 64`,
554
+ `Groups:`,
555
+ `VmPeak: 20480 kB`,
556
+ `VmSize: 16384 kB`,
557
+ `VmLck: 0 kB`,
558
+ `VmPin: 0 kB`,
559
+ `VmHWM: 4096 kB`,
560
+ `VmRSS: 4096 kB`,
561
+ `RssAnon: 512 kB`,
562
+ `RssFile: 3584 kB`,
563
+ `RssShmem: 0 kB`,
564
+ `VmData: 2048 kB`,
565
+ `VmStk: 132 kB`,
566
+ `VmExe: 924 kB`,
567
+ `VmLib: 2744 kB`,
568
+ `VmPTE: 52 kB`,
569
+ `VmSwap: 0 kB`,
294
570
  `Threads: 1`,
571
+ `SigQ: 0/31968`,
572
+ `SigPnd: 0000000000000000`,
573
+ `SigBlk: 0000000000010000`,
574
+ `SigIgn: 0000000000380004`,
575
+ `SigCgt: 000000004b817efb`,
576
+ `CapInh: 0000000000000000`,
577
+ `CapPrm: 000001ffffffffff`,
578
+ `CapEff: 000001ffffffffff`,
579
+ `CapBnd: 000001ffffffffff`,
580
+ `CapAmb: 0000000000000000`,
581
+ `NoNewPrivs: 0`,
582
+ `Seccomp: 0`,
583
+ `voluntary_ctxt_switches: 100`,
584
+ `nonvoluntary_ctxt_switches: 10`,
295
585
  ].join("\n")}\n`,
296
586
  );
297
587
  write(
298
588
  vfs,
299
589
  `${dir}/stat`,
300
- `${pid} (${comm}) S 1 ${pid} ${pid} 0 -1 4194304 0 0 0 0 ${uptimeSec} 0 0 0 20 0 1 0 0 16384 4096 0\n`,
590
+ `${pid} (${comm}) S 1 ${pid} ${pid} 0 -1 4194304 0 0 0 0 ${uptimeSec} 0 0 0 20 0 1 0 0 16777216 4096 18446744073709551615 93824992235520 93824992290000 140737488347024 0 0 0 65536 3686404 1266761467 1 0 0 17 0 0 0 0 0 0\n`,
591
+ );
592
+ write(
593
+ vfs,
594
+ `${dir}/statm`,
595
+ `4096 1024 768 231 0 512 0\n`,
301
596
  );
302
597
  write(
303
598
  vfs,
@@ -306,10 +601,48 @@ function writeProcPid(
306
601
  );
307
602
  write(vfs, `${dir}/cwd`, `/home/${username}\0`);
308
603
  write(vfs, `${dir}/exe`, "/bin/bash\0");
604
+ write(vfs, `${dir}/maps`,
605
+ "00400000-004e7000 r-xp 00000000 fd:00 131073 /bin/bash\n" +
606
+ "006e7000-006e8000 r--p 000e7000 fd:00 131073 /bin/bash\n" +
607
+ "006e8000-006f1000 rw-p 000e8000 fd:00 131073 /bin/bash\n" +
608
+ "7fff00000000-7fff00001000 rw-p 00000000 00:00 0 [stack]\n" +
609
+ "7fff00000000-7fff00001000 r-xp 00000000 00:00 0 [vdso]\n",
610
+ );
611
+ write(vfs, `${dir}/limits`,
612
+ `${[
613
+ "Limit Soft Limit Hard Limit Units",
614
+ "Max cpu time unlimited unlimited seconds",
615
+ "Max file size unlimited unlimited bytes",
616
+ "Max data size unlimited unlimited bytes",
617
+ "Max stack size 8388608 unlimited bytes",
618
+ "Max core file size 0 unlimited bytes",
619
+ "Max resident set unlimited unlimited bytes",
620
+ "Max processes 31968 31968 processes",
621
+ "Max open files 1048576 1048576 files",
622
+ "Max locked memory 8388608 8388608 bytes",
623
+ "Max address space unlimited unlimited bytes",
624
+ "Max file locks unlimited unlimited locks",
625
+ "Max pending signals 31968 31968 signals",
626
+ "Max msgqueue size 819200 819200 bytes",
627
+ "Max nice priority 0 0",
628
+ "Max realtime priority 0 0",
629
+ "Max realtime timeout unlimited unlimited us",
630
+ ].join("\n")}\n`,
631
+ );
632
+ write(vfs, `${dir}/io`,
633
+ "rchar: 1048576\nwchar: 65536\nsyscr: 512\nsyscw: 64\nread_bytes: 0\nwrite_bytes: 0\ncancelled_write_bytes: 0\n",
634
+ );
635
+ write(vfs, `${dir}/oom_score`, "0\n");
636
+ write(vfs, `${dir}/oom_score_adj`, "0\n");
637
+ write(vfs, `${dir}/loginuid`, "0\n");
638
+ write(vfs, `${dir}/wchan`, "poll_schedule_timeout\n");
639
+ write(vfs, `${dir}/schedstat`, "1000000 0 1\n");
309
640
 
310
- // Standard fd stubs (stdin/stdout/stderr)
311
641
  for (const fd of ["0", "1", "2"]) {
312
642
  ensureFile(vfs, `${dir}/fd/${fd}`, "");
643
+ ensureFile(vfs, `${dir}/fdinfo/${fd}`,
644
+ `pos:\t0\nflags:\t0${fd === "0" ? "2" : fd === "1" ? "1" : "1"}02\nmnt_id:\t13\n`,
645
+ );
313
646
  }
314
647
  }
315
648
 
@@ -321,11 +654,30 @@ function bootProcLog(vfs: VirtualFileSystem, props: ShellProperties): void {
321
654
  vfs,
322
655
  "/proc/boot/log",
323
656
  `${[
324
- "[ 0.000000] Linux virtual kernel booting...",
325
- "[ 0.000120] init memory subsystem",
326
- "[ 0.000240] mount /proc /sys /dev",
327
- "[ 0.000420] start init",
328
- "[ 0.000680] system ready",
657
+ `[ 0.000000] Linux version ${props.kernel} (fortune@build) #1 SMP PREEMPT_DYNAMIC`,
658
+ "[ 0.000000] Command line: console=ttyS0 reboot=k panic=1 nomodule random.trust_cpu=1 ipv6.disable=1",
659
+ "[ 0.000060] BIOS-provided physical RAM map:",
660
+ "[ 0.000070] ACPI: RSDP 0x00000000000F05B0 000014 (v00 BOCHS )",
661
+ "[ 0.000120] PCI: Using configuration type 1 for base access",
662
+ "[ 0.000240] clocksource: tsc-early: mask: 0xffffffffffffffff",
663
+ "[ 0.000320] ACPI: IRQ0 used by override",
664
+ "[ 0.000420] Initializing cgroup subsys cpuset",
665
+ "[ 0.000440] Initializing cgroup subsys cpu",
666
+ "[ 0.000450] Initializing cgroup subsys cpuacct",
667
+ "[ 0.000460] Linux agpgart interface v0.103",
668
+ "[ 0.000480] PCI: pci_cache_line_size set to 64 bytes",
669
+ "[ 0.000510] virtio-pci 0000:00:01.0: runtime IRQs not yet assigned",
670
+ "[ 0.000680] NET: Registered PF_INET6 protocol family",
671
+ "[ 0.000720] Freeing unused kernel image (initmem) memory",
672
+ "[ 0.000760] Write protecting the kernel read-only data",
673
+ "[ 0.000800] Run /sbin/init as init process",
674
+ "[ 0.001200] systemd[1]: Detected virtualization kvm",
675
+ "[ 0.001300] systemd[1]: Detected architecture x86-64",
676
+ "[ 0.002000] systemd[1]: Starting Fortune GNU/Linux...",
677
+ "[ 0.003000] systemd[1]: Started Journal Service",
678
+ "[ 0.010000] EXT4-fs (vda): mounted filesystem",
679
+ "[ 0.020000] systemd[1]: Reached target Basic System",
680
+ "[ 0.030000] systemd[1]: Started Login Service",
329
681
  ].join("\n")}\n`,
330
682
  );
331
683
  ensureFile(vfs, "/proc/boot/version", `Linux ${props.kernel} (virtual)\n`);
@@ -350,13 +702,18 @@ export function refreshProc(
350
702
  ensureDir(vfs, "/proc");
351
703
 
352
704
  const uptimeSec = Math.floor((Date.now() - shellStartTime) / 1000);
353
- const idleSec = Math.floor(uptimeSec * 0.9);
705
+ const idleSec = Math.floor(uptimeSec * 0.9);
354
706
  write(vfs, "/proc/uptime", `${uptimeSec}.00 ${idleSec}.00\n`);
355
707
 
356
708
  // meminfo — real host values, Linux-compatible format
357
- const totalMemKb = Math.floor(os.totalmem() / 1024);
358
- const freeMemKb = Math.floor(os.freemem() / 1024);
359
- const availMemKb = Math.floor(freeMemKb * 0.95);
709
+ const totalMemKb = Math.floor(os.totalmem() / 1024);
710
+ const freeMemKb = Math.floor(os.freemem() / 1024);
711
+ const availMemKb = Math.floor(freeMemKb * 0.95);
712
+ const buffersKb = Math.floor(totalMemKb * 0.03);
713
+ const cachedKb = Math.floor(totalMemKb * 0.08);
714
+ const shmemKb = Math.floor(totalMemKb * 0.005);
715
+ const slabKb = Math.floor(totalMemKb * 0.02);
716
+ const pageTablesKb = Math.floor(totalMemKb * 0.001);
360
717
  write(
361
718
  vfs,
362
719
  "/proc/meminfo",
@@ -364,14 +721,59 @@ export function refreshProc(
364
721
  `MemTotal: ${String(totalMemKb).padStart(10)} kB`,
365
722
  `MemFree: ${String(freeMemKb).padStart(10)} kB`,
366
723
  `MemAvailable: ${String(availMemKb).padStart(10)} kB`,
367
- `Buffers: ${String(Math.floor(totalMemKb * 0.02)).padStart(10)} kB`,
368
- `Cached: ${String(Math.floor(totalMemKb * 0.15)).padStart(10)} kB`,
369
- `SwapTotal: ${String(Math.floor(totalMemKb * 0.5)).padStart(10)} kB`,
370
- `SwapFree: ${String(Math.floor(totalMemKb * 0.5)).padStart(10)} kB`,
724
+ `Buffers: ${String(buffersKb).padStart(10)} kB`,
725
+ `Cached: ${String(cachedKb).padStart(10)} kB`,
726
+ `SwapCached: ${String(0).padStart(10)} kB`,
727
+ `Active: ${String(Math.floor((buffersKb + cachedKb) * 1.2)).padStart(10)} kB`,
728
+ `Inactive: ${String(Math.floor(cachedKb * 0.6)).padStart(10)} kB`,
729
+ `Active(anon): ${String(Math.floor(totalMemKb * 0.001)).padStart(10)} kB`,
730
+ `Inactive(anon): ${String(Math.floor(totalMemKb * 0.006)).padStart(10)} kB`,
731
+ `Active(file): ${String(Math.floor(cachedKb * 1.2)).padStart(10)} kB`,
732
+ `Inactive(file): ${String(Math.floor(cachedKb * 0.6)).padStart(10)} kB`,
733
+ `Unevictable: ${String(0).padStart(10)} kB`,
734
+ `Mlocked: ${String(0).padStart(10)} kB`,
735
+ `SwapTotal: ${String(0).padStart(10)} kB`,
736
+ `SwapFree: ${String(0).padStart(10)} kB`,
737
+ `Zswap: ${String(0).padStart(10)} kB`,
738
+ `Zswapped: ${String(0).padStart(10)} kB`,
739
+ `Dirty: ${String(Math.floor(Math.random() * 64)).padStart(10)} kB`,
740
+ `Writeback: ${String(0).padStart(10)} kB`,
741
+ `AnonPages: ${String(Math.floor(totalMemKb * 0.001)).padStart(10)} kB`,
742
+ `Mapped: ${String(Math.floor(cachedKb * 0.4)).padStart(10)} kB`,
743
+ `Shmem: ${String(shmemKb).padStart(10)} kB`,
744
+ `KReclaimable: ${String(Math.floor(slabKb * 0.6)).padStart(10)} kB`,
745
+ `Slab: ${String(slabKb).padStart(10)} kB`,
746
+ `SReclaimable: ${String(Math.floor(slabKb * 0.6)).padStart(10)} kB`,
747
+ `SUnreclaim: ${String(Math.floor(slabKb * 0.4)).padStart(10)} kB`,
748
+ `KernelStack: ${String(Math.floor(totalMemKb * 0.0005)).padStart(10)} kB`,
749
+ `PageTables: ${String(pageTablesKb).padStart(10)} kB`,
750
+ `NFS_Unstable: ${String(0).padStart(10)} kB`,
751
+ `Bounce: ${String(0).padStart(10)} kB`,
752
+ `WritebackTmp: ${String(0).padStart(10)} kB`,
753
+ `CommitLimit: ${String(Math.floor(totalMemKb * 0.5)).padStart(10)} kB`,
754
+ `Committed_AS: ${String(Math.floor(totalMemKb * 0.05)).padStart(10)} kB`,
755
+ `VmallocTotal: ${String(35184372087808 / 1024).padStart(10)} kB`,
756
+ `VmallocUsed: ${String(Math.floor(totalMemKb * 0.01)).padStart(10)} kB`,
757
+ `VmallocChunk: ${String(0).padStart(10)} kB`,
758
+ `Percpu: ${String(Math.floor(totalMemKb * 0.0001)).padStart(10)} kB`,
759
+ `HardwareCorrupted: ${String(0).padStart(6)} kB`,
760
+ `AnonHugePages: ${String(0).padStart(10)} kB`,
761
+ `ShmemHugePages: ${String(0).padStart(10)} kB`,
762
+ `ShmemPmdMapped: ${String(0).padStart(10)} kB`,
763
+ `FileHugePages: ${String(0).padStart(10)} kB`,
764
+ `FilePmdMapped: ${String(0).padStart(10)} kB`,
765
+ `HugePages_Total: ${String(0).padStart(8)}`,
766
+ `HugePages_Free: ${String(0).padStart(8)}`,
767
+ `HugePages_Rsvd: ${String(0).padStart(8)}`,
768
+ `HugePages_Surp: ${String(0).padStart(8)}`,
769
+ `Hugepagesize: ${String(2048).padStart(10)} kB`,
770
+ `Hugetlb: ${String(0).padStart(10)} kB`,
771
+ `DirectMap4k: ${String(Math.floor(totalMemKb * 0.02)).padStart(10)} kB`,
772
+ `DirectMap2M: ${String(Math.floor(totalMemKb * 0.98)).padStart(10)} kB`,
371
773
  ].join("\n")}\n`,
372
774
  );
373
775
 
374
- // cpuinfo — real host CPU passthrough
776
+ // cpuinfo — real host CPU passthrough + x86 feature flags matching Firecracker Xeon
375
777
  const cpus = os.cpus();
376
778
  const cpuLines: string[] = [];
377
779
  for (let i = 0; i < cpus.length; i++) {
@@ -379,9 +781,31 @@ export function refreshProc(
379
781
  if (!c) continue;
380
782
  cpuLines.push(
381
783
  `processor\t: ${i}`,
784
+ `vendor_id\t: GenuineIntel`,
785
+ `cpu family\t: 6`,
786
+ `model\t\t: 85`,
382
787
  `model name\t: ${c.model}`,
788
+ `stepping\t: 7`,
789
+ `microcode\t: 0x1`,
383
790
  `cpu MHz\t\t: ${c.speed.toFixed(3)}`,
384
- `cache size\t: 8192 KB`,
791
+ `cache size\t: 33792 KB`,
792
+ `physical id\t: 0`,
793
+ `siblings\t: ${cpus.length}`,
794
+ `core id\t\t: ${i}`,
795
+ `cpu cores\t: ${cpus.length}`,
796
+ `apicid\t\t: ${i}`,
797
+ `initial apicid\t: ${i}`,
798
+ `fpu\t\t: yes`,
799
+ `fpu_exception\t: yes`,
800
+ `cpuid level\t: 13`,
801
+ `wp\t\t: yes`,
802
+ `flags\t\t: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush mmx fxsr sse sse2 ss syscall nx pdpe1gb rdtscp lm constant_tsc rep_good nopl xtopology nonstop_tsc cpuid tsc_known_freq pni pclmulqdq ssse3 fma cx16 pcid sse4_1 sse4_2 x2apic movbe popcnt tsc_deadline_timer aes xsave avx f16c rdrand hypervisor lahf_lm abm 3dnowprefetch cpuid_fault ssbd ibrs ibpb stibp ibrs_enhanced fsgsbase tsc_adjust bmi1 hle avx2 smep bmi2 erms invpcid rtm avx512f avx512dq rdseed adx smap clflushopt clwb avx512cd avx512bw avx512vl xsaveopt xsavec xgetbv1 xsaves arat umip avx512_vnni md_clear arch_capabilities`,
803
+ `bugs\t\t: spectre_v1 spectre_v2 spec_store_bypass swapgs taa mmio_stale_data retbleed eibrs_pbrsb bhi ibpb_no_ret spectre_v2_user its`,
804
+ `bogomips\t: ${(c.speed * 2 / 1000).toFixed(2)}`,
805
+ `clflush size\t: 64`,
806
+ `cache_alignment\t: 64`,
807
+ `address sizes\t: 46 bits physical, 48 bits virtual`,
808
+ `power management:`,
385
809
  "",
386
810
  );
387
811
  }
@@ -390,95 +814,219 @@ export function refreshProc(
390
814
  write(
391
815
  vfs,
392
816
  "/proc/version",
393
- `Linux version ${props.kernel} (fortune@build) (gcc version 12.2.0) #1 SMP\n`,
817
+ `Linux version ${props.kernel} (fortune@nyx-build) (gcc (Fortune 13.3.0-nyx1) 13.3.0, GNU ld (GNU Binutils for Fortune) 2.42) #2 SMP PREEMPT_DYNAMIC\n`,
394
818
  );
395
819
  write(vfs, "/proc/hostname", `${hostname}\n`);
396
820
 
397
- // loadavg — slightly random but bounded
398
- const load = (Math.random() * 0.5).toFixed(2);
821
+ // loadavg
822
+ const load = (Math.random() * 0.3).toFixed(2);
399
823
  const numProcs = 1 + sessions.length;
400
824
  write(vfs, "/proc/loadavg", `${load} ${load} ${load} ${numProcs}/${numProcs} 1\n`);
401
825
 
402
- // /proc/net stubs
403
- ensureDir(vfs, "/proc/net");
404
- ensureFile(
405
- vfs,
406
- "/proc/net/dev",
407
- `${[
408
- "Inter-| Receive | Transmit",
409
- " face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed",
410
- " lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
411
- " eth0: 131072 1024 0 0 0 0 0 0 65536 512 0 0 0 0 0 0",
412
- ].join("\n")}\n`,
826
+ // /proc/cmdline — Firecracker boot args
827
+ write(vfs, "/proc/cmdline",
828
+ `console=ttyS0 reboot=k panic=1 nomodule random.trust_cpu=1 ipv6.disable=1 swiotlb=noforce rdinit=/process_api init_on_free=1 -- --firecracker-init --addr 0.0.0.0:2024 --max-ws-buffer-size 32768 --block-local-connections\n`,
413
829
  );
414
- ensureFile(vfs, "/proc/net/if_inet6", "");
415
- ensureFile(vfs, "/proc/net/tcp", " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n");
416
- ensureFile(vfs, "/proc/net/tcp6", " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n");
417
-
418
- // /proc/cmdline — kernel boot args
419
- write(vfs, "/proc/cmdline", `BOOT_IMAGE=/boot/vmlinuz-${props.kernel} root=/dev/sda2 ro quiet splash\n`);
420
830
 
421
- // /proc/filesystems
422
- ensureFile(
423
- vfs,
424
- "/proc/filesystems",
425
- `${["nodev\tsysfs", "nodev\ttmpfs", "nodev\tproc", "nodev\tdevtmpfs", "\text4", "\tvfat", "nodev\tsquashfs", "nodev\toverlay"].join("\n")}\n`,
831
+ // /proc/filesystems — matching real container
832
+ write(vfs, "/proc/filesystems",
833
+ `${[
834
+ "nodev\tsysfs",
835
+ "nodev\ttmpfs",
836
+ "nodev\tbdev",
837
+ "nodev\tproc",
838
+ "nodev\tcgroup",
839
+ "nodev\tcgroup2",
840
+ "nodev\tcpuset",
841
+ "nodev\tdevtmpfs",
842
+ "nodev\tbinfmt_misc",
843
+ "nodev\tdebugfs",
844
+ "nodev\tsecurityfs",
845
+ "nodev\tsockfs",
846
+ "nodev\tbpf",
847
+ "nodev\tpipefs",
848
+ "nodev\tramfs",
849
+ "nodev\thugetlbfs",
850
+ "nodev\trpc_pipefs",
851
+ "nodev\tdevpts",
852
+ "\text3",
853
+ "\text2",
854
+ "\text4",
855
+ "\tsquashfs",
856
+ "nodev\tnfs",
857
+ "nodev\tnfs4",
858
+ "nodev\tautofs",
859
+ "\tfuseblk",
860
+ "nodev\tfuse",
861
+ "nodev\tfusectl",
862
+ "nodev\toverlay",
863
+ "\txfs",
864
+ "nodev\tmqueue",
865
+ "nodev\tselinuxfs",
866
+ "nodev\tpstore",
867
+ ].join("\n")}\n`,
426
868
  );
427
869
 
428
- // /proc/mounts (= /proc/self/mounts)
870
+ // /proc/mounts virtio block device layout
429
871
  const mountsContent = `${[
430
- "sysfs /sys sysfs rw,nosuid,nodev,noexec,relatime 0 0",
431
- "proc /proc proc rw,nosuid,nodev,noexec,relatime 0 0",
432
- "devtmpfs /dev devtmpfs rw,nosuid,size=8192k,nr_inodes=4096,mode=755 0 0",
872
+ "proc /proc proc rw,relatime 0 0",
873
+ "sysfs /sys sysfs rw,relatime 0 0",
874
+ "devtmpfs /dev devtmpfs rw,relatime,size=2045672k,nr_inodes=511418,mode=755 0 0",
875
+ "tmpfs /dev/shm tmpfs rw,relatime 0 0",
876
+ "devpts /dev/pts devpts rw,relatime,mode=600,ptmxmode=000 0 0",
877
+ "tmpfs /sys/fs/cgroup tmpfs rw,relatime 0 0",
878
+ "cgroup /sys/fs/cgroup/cpu cgroup rw,relatime,cpu 0 0",
879
+ "cgroup /sys/fs/cgroup/cpuacct cgroup rw,relatime,cpuacct 0 0",
880
+ "cgroup /sys/fs/cgroup/memory cgroup rw,relatime,memory 0 0",
881
+ "cgroup /sys/fs/cgroup/devices cgroup rw,relatime,devices 0 0",
882
+ "cgroup /sys/fs/cgroup/freezer cgroup rw,relatime,freezer 0 0",
883
+ "cgroup /sys/fs/cgroup/blkio cgroup rw,relatime,blkio 0 0",
884
+ "cgroup /sys/fs/cgroup/pids cgroup rw,relatime,pids 0 0",
885
+ "cgroup2 /sys/fs/cgroup/unified cgroup2 rw,relatime 0 0",
886
+ "/dev/vda / ext4 rw,relatime,resuid=65534,resgid=65534 0 0",
887
+ "/dev/vdb /opt/rclone squashfs ro,relatime,errors=continue 0 0",
433
888
  "tmpfs /run tmpfs rw,nosuid,nodev,noexec,relatime,size=204800k,mode=755 0 0",
434
889
  "tmpfs /tmp tmpfs rw,nosuid,nodev,noatime 0 0",
435
- "/dev/sda2 / ext4 rw,relatime,errors=remount-ro 0 0",
436
- "/dev/sda1 /boot ext4 rw,relatime 0 0",
437
- "tmpfs /dev/shm tmpfs rw,nosuid,nodev 0 0",
438
- "devpts /dev/pts devpts rw,nosuid,noexec,relatime,mode=620,ptmxmode=000 0 0",
439
- "squashfs /snap/core squashfs ro,nodev,relatime 0 0",
440
890
  ].join("\n")}\n`;
441
891
  write(vfs, "/proc/mounts", mountsContent);
442
892
  ensureDir(vfs, "/proc/self");
443
893
  write(vfs, "/proc/self/mounts", mountsContent);
444
894
 
445
- // /proc/partitions
895
+ // /proc/net
896
+ ensureDir(vfs, "/proc/net");
897
+ write(vfs, "/proc/net/dev",
898
+ `${[
899
+ "Inter-| Receive | Transmit",
900
+ " face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed",
901
+ " lo: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0",
902
+ " eth0: 128628 1230 0 19 0 0 0 0 52027469 2045 0 0 0 0 0 0",
903
+ ].join("\n")}\n`,
904
+ );
905
+ write(vfs, "/proc/net/if_inet6", "");
906
+ write(vfs, "/proc/net/tcp",
907
+ " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n",
908
+ );
909
+ write(vfs, "/proc/net/tcp6",
910
+ " sl local_address remote_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n",
911
+ );
912
+ write(vfs, "/proc/net/udp",
913
+ " sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode\n",
914
+ );
915
+ write(vfs, "/proc/net/route",
916
+ "Iface\tDestination\tGateway\tFlags\tRefCnt\tUse\tMetric\tMask\t\tMTU\tWindow\tIRTT\n" +
917
+ "eth0\t00000000\t0101A8C0\t0003\t0\t0\t100\t00000000\t0\t0\t0\n" +
918
+ "eth0\t0000A8C0\t00000000\t0001\t0\t0\t100\t00FFFFFF\t0\t0\t0\n",
919
+ );
920
+ write(vfs, "/proc/net/arp",
921
+ "IP address HW type Flags HW address Mask Device\n",
922
+ );
923
+ write(vfs, "/proc/net/fib_trie", "Local:\n +-- 0.0.0.0/0 3 0 5\n");
924
+ write(vfs, "/proc/net/unix",
925
+ "Num RefCount Protocol Flags Type St Inode Path\n" +
926
+ "0000000000000000: 00000002 00000000 00010000 0001 01 10000 /run/dbus/system_bus_socket\n",
927
+ );
928
+ write(vfs, "/proc/net/sockstat",
929
+ "sockets: used 8\nTCP: inuse 0 orphan 0 tw 0 alloc 0 mem 0\nUDP: inuse 0 mem 0\nUDPLITE: inuse 0\nRAW: inuse 0\nFRAG: inuse 0 memory 0\n",
930
+ );
931
+
932
+ // /proc/partitions — virtio block devices
446
933
  write(
447
934
  vfs,
448
935
  "/proc/partitions",
449
936
  `${[
450
937
  "major minor #blocks name",
451
938
  "",
452
- " 8 0 41943040 sda",
453
- " 8 1 524288 sda1",
454
- " 8 2 41417216 sda2",
455
- " 7 0 10485760 loop0",
939
+ " 254 0 268435456 vda",
940
+ " 254 16 9664 vdb",
941
+ " 254 32 656 vdc",
942
+ " 254 48 5464 vdd",
456
943
  ].join("\n")}\n`,
457
944
  );
458
945
 
459
- // /proc/swaps
460
- write(
461
- vfs,
462
- "/proc/swaps",
463
- "Filename\t\t\t\tType\t\tSize\t\tUsed\t\tPriority\n" +
464
- `/dev/sda3\t\t\t\tpartition\t${Math.floor(os.totalmem() / 2048)}\t\t0\t\t-2\n`,
946
+ // /proc/swaps — no swap (matches real env: SwapTotal 0)
947
+ write(vfs, "/proc/swaps",
948
+ "Filename\t\t\t\tType\t\tSize\t\tUsed\t\tPriority\n",
465
949
  );
466
950
 
467
- // /proc/syssysctl virtual tree
951
+ // /proc/diskstatsvirtio block device I/O counters
952
+ write(vfs, "/proc/diskstats",
953
+ `${[
954
+ " 254 0 vda 1000 0 8000 500 200 0 1600 100 0 600 600 0 0 0 0",
955
+ " 254 16 vdb 100 0 800 50 0 0 0 0 0 50 50 0 0 0 0",
956
+ " 254 32 vdc 50 0 400 25 0 0 0 0 0 25 25 0 0 0 0",
957
+ " 254 48 vdd 80 0 640 40 0 0 0 0 0 40 40 0 0 0 0",
958
+ ].join("\n")}\n`,
959
+ );
960
+
961
+ // /proc/interrupts
962
+ write(vfs, "/proc/interrupts",
963
+ ` CPU0\n 0: ${Math.floor(uptimeSec * 250)} IO-APIC 2-edge timer\n 1: 9 IO-APIC 1-edge i8042\n NMI: 0 Non-maskable interrupts\n ERR: 0\n MIS: 0\n PIN: 0 Posted-interrupt notification event\n NPI: 0 Nested posted-interrupt event\n PIW: 0 Posted-interrupt wakeup event\n`,
964
+ );
965
+
966
+ // /proc/sys — sysctl virtual tree (real values)
468
967
  ensureDir(vfs, "/proc/sys");
469
968
  ensureDir(vfs, "/proc/sys/kernel");
470
969
  ensureDir(vfs, "/proc/sys/net");
970
+ ensureDir(vfs, "/proc/sys/net/ipv4");
971
+ ensureDir(vfs, "/proc/sys/net/ipv6");
972
+ ensureDir(vfs, "/proc/sys/net/core");
471
973
  ensureDir(vfs, "/proc/sys/vm");
472
- ensureFile(vfs, "/proc/sys/kernel/hostname", `${hostname}\n`);
473
- ensureFile(vfs, "/proc/sys/kernel/ostype", "Linux\n");
474
- ensureFile(vfs, "/proc/sys/kernel/osrelease", `${props.kernel}\n`);
475
- ensureFile(vfs, "/proc/sys/kernel/pid_max", "32768\n");
476
- ensureFile(vfs, "/proc/sys/kernel/threads-max", "65536\n");
477
- ensureFile(vfs, "/proc/sys/kernel/randomize_va_space", "2\n");
478
- ensureFile(vfs, "/proc/sys/kernel/dmesg_restrict", "0\n");
479
- ensureFile(vfs, "/proc/sys/net/ipv4/ip_forward", "0\n");
480
- ensureFile(vfs, "/proc/sys/vm/swappiness", "60\n");
481
- ensureFile(vfs, "/proc/sys/vm/overcommit_memory", "0\n");
974
+ ensureDir(vfs, "/proc/sys/fs");
975
+ ensureDir(vfs, "/proc/sys/fs/inotify");
976
+
977
+ write(vfs, "/proc/sys/kernel/hostname", `${hostname}\n`);
978
+ write(vfs, "/proc/sys/kernel/ostype", "Linux\n");
979
+ write(vfs, "/proc/sys/kernel/osrelease", `${props.kernel}\n`);
980
+ write(vfs, "/proc/sys/kernel/pid_max", "32768\n");
981
+ write(vfs, "/proc/sys/kernel/threads-max", "31968\n");
982
+ write(vfs, "/proc/sys/kernel/randomize_va_space", "2\n");
983
+ write(vfs, "/proc/sys/kernel/dmesg_restrict", "0\n");
984
+ write(vfs, "/proc/sys/kernel/kptr_restrict", "0\n");
985
+ write(vfs, "/proc/sys/kernel/perf_event_paranoid", "2\n");
986
+ write(vfs, "/proc/sys/kernel/printk", "4\t4\t1\t7\n");
987
+ write(vfs, "/proc/sys/kernel/sysrq", "176\n");
988
+ write(vfs, "/proc/sys/kernel/panic", "1\n");
989
+ write(vfs, "/proc/sys/kernel/panic_on_oops", "1\n");
990
+ write(vfs, "/proc/sys/kernel/core_pattern", "core\n");
991
+ write(vfs, "/proc/sys/kernel/core_uses_pid", "0\n");
992
+ write(vfs, "/proc/sys/kernel/ngroups_max", "65536\n");
993
+ write(vfs, "/proc/sys/kernel/cap_last_cap", "40\n");
994
+ write(vfs, "/proc/sys/kernel/unprivileged_userns_clone", "1\n");
995
+ write(vfs, "/proc/sys/net/ipv4/ip_forward", "0\n");
996
+ write(vfs, "/proc/sys/net/ipv4/tcp_syncookies", "1\n");
997
+ write(vfs, "/proc/sys/net/ipv4/tcp_fin_timeout", "60\n");
998
+ write(vfs, "/proc/sys/net/ipv4/tcp_keepalive_time", "7200\n");
999
+ write(vfs, "/proc/sys/net/ipv4/conf/all/rp_filter", "2\n");
1000
+ write(vfs, "/proc/sys/net/ipv6/conf/all/disable_ipv6", "1\n");
1001
+ write(vfs, "/proc/sys/net/core/somaxconn", "4096\n");
1002
+ write(vfs, "/proc/sys/net/core/rmem_max", "212992\n");
1003
+ write(vfs, "/proc/sys/net/core/wmem_max", "212992\n");
1004
+ write(vfs, "/proc/sys/vm/swappiness", "60\n");
1005
+ write(vfs, "/proc/sys/vm/overcommit_memory", "0\n");
1006
+ write(vfs, "/proc/sys/vm/overcommit_ratio", "50\n");
1007
+ write(vfs, "/proc/sys/vm/dirty_ratio", "20\n");
1008
+ write(vfs, "/proc/sys/vm/dirty_background_ratio", "10\n");
1009
+ write(vfs, "/proc/sys/vm/min_free_kbytes", "65536\n");
1010
+ write(vfs, "/proc/sys/vm/vfs_cache_pressure", "100\n");
1011
+ write(vfs, "/proc/sys/fs/file-max", "1048576\n");
1012
+ write(vfs, "/proc/sys/fs/inotify/max_user_watches", "524288\n");
1013
+ write(vfs, "/proc/sys/fs/inotify/max_user_instances","512\n");
1014
+ write(vfs, "/proc/sys/fs/inotify/max_queued_events", "16384\n");
1015
+
1016
+ // /proc/cgroups — v1 hierarchy
1017
+ write(vfs, "/proc/cgroups",
1018
+ `${[
1019
+ "#subsys_name\thierarchy\tnum_cgroups\tenabled",
1020
+ "cpuset\t5\t1\t1",
1021
+ "cpu\t1\t1\t1",
1022
+ "cpuacct\t2\t1\t1",
1023
+ "blkio\t6\t1\t1",
1024
+ "memory\t3\t1\t1",
1025
+ "devices\t4\t1\t1",
1026
+ "freezer\t7\t1\t1",
1027
+ "pids\t8\t1\t1",
1028
+ ].join("\n")}\n`,
1029
+ );
482
1030
 
483
1031
  // init process (PID 1)
484
1032
  writeProcPid(vfs, 1, "root", "pts/0", "/sbin/init", new Date(shellStartTime).toISOString(), {});
@@ -487,23 +1035,25 @@ export function refreshProc(
487
1035
  for (const session of sessions) {
488
1036
  const pid = ttyToPid(session.tty);
489
1037
  writeProcPid(vfs, pid, session.username, session.tty, "bash", session.startedAt, {
490
- USER: session.username,
491
- HOME: `/home/${session.username}`,
492
- TERM: "xterm-256color",
493
- SHELL: "/bin/bash",
1038
+ USER: session.username,
1039
+ HOME: `/home/${session.username}`,
1040
+ TERM: "xterm-256color",
1041
+ SHELL: "/bin/bash",
1042
+ LANG: "en_US.UTF-8",
1043
+ PATH: "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
1044
+ LOGNAME: session.username,
494
1045
  });
495
1046
  }
496
1047
 
497
- // /proc/self — mirror of most recent session, or init
1048
+ // /proc/self — mirror of most recent session
498
1049
  const selfPid = sessions.length > 0 ? ttyToPid(sessions[sessions.length - 1]!.tty) : 1;
499
-
500
- if (vfs.exists("/proc/self")) {
501
- try { vfs.remove("/proc/self"); } catch { /* ignore */ }
502
- }
1050
+ try { vfs.remove("/proc/self"); } catch { /* ignore */ }
503
1051
 
504
1052
  const selfSrc = `/proc/${selfPid}`;
505
1053
  ensureDir(vfs, "/proc/self");
506
1054
  ensureDir(vfs, "/proc/self/fd");
1055
+ ensureDir(vfs, "/proc/self/fdinfo");
1056
+ ensureDir(vfs, "/proc/self/net");
507
1057
 
508
1058
  if (vfs.exists(selfSrc)) {
509
1059
  for (const entry of vfs.list(selfSrc)) {
@@ -512,16 +1062,15 @@ export function refreshProc(
512
1062
  try {
513
1063
  const st = vfs.stat(srcPath);
514
1064
  if (st.type === "file") write(vfs, dstPath, vfs.readFile(srcPath));
515
- } catch { /* skip unreadable entries */ }
1065
+ } catch { /* skip */ }
516
1066
  }
517
1067
  } else {
518
- // Minimal fallback
519
- write(vfs, "/proc/self/cmdline", "bash\0");
520
- write(vfs, "/proc/self/comm", "bash");
521
- write(vfs, "/proc/self/status", "Name:\tbash\nState:\tS (sleeping)\nPid:\t1\nPPid:\t0\n");
522
- write(vfs, "/proc/self/environ", "");
523
- write(vfs, "/proc/self/cwd", "/root\0");
524
- write(vfs, "/proc/self/exe", "/bin/bash\0");
1068
+ write(vfs, "/proc/self/cmdline", "bash\0");
1069
+ write(vfs, "/proc/self/comm", "bash");
1070
+ write(vfs, "/proc/self/status", "Name:\tbash\nState:\tS (sleeping)\nPid:\t1\nPPid:\t0\n");
1071
+ write(vfs, "/proc/self/environ", "");
1072
+ write(vfs, "/proc/self/cwd", "/root\0");
1073
+ write(vfs, "/proc/self/exe", "/bin/bash\0");
525
1074
  }
526
1075
  }
527
1076
 
@@ -529,12 +1078,83 @@ export function refreshProc(
529
1078
 
530
1079
  function bootstrapSys(vfs: VirtualFileSystem, hostname: string, props: ShellProperties): void {
531
1080
  ensureDir(vfs, "/sys");
1081
+
1082
+ // No real DMI in Firecracker — /sys/devices/virtual/dmi/id does not exist.
1083
+ // Expose /sys/class/net, /sys/fs/cgroup, /sys/kernel only.
1084
+
532
1085
  ensureDir(vfs, "/sys/devices");
533
1086
  ensureDir(vfs, "/sys/devices/virtual");
1087
+ ensureDir(vfs, "/sys/devices/system");
1088
+ ensureDir(vfs, "/sys/devices/system/cpu");
1089
+ ensureDir(vfs, "/sys/devices/system/cpu/cpu0");
1090
+ ensureFile(vfs, "/sys/devices/system/cpu/cpu0/online", "1\n");
1091
+ ensureFile(vfs, "/sys/devices/system/cpu/online", "0\n");
1092
+ ensureFile(vfs, "/sys/devices/system/cpu/possible", "0\n");
1093
+ ensureFile(vfs, "/sys/devices/system/cpu/present", "0\n");
1094
+ ensureDir(vfs, "/sys/devices/system/node");
1095
+ ensureDir(vfs, "/sys/devices/system/node/node0");
1096
+ ensureFile(vfs, "/sys/devices/system/node/node0/cpumap", "1\n");
1097
+
1098
+ ensureDir(vfs, "/sys/class");
1099
+ ensureDir(vfs, "/sys/class/net");
1100
+ ensureDir(vfs, "/sys/class/net/eth0");
1101
+ ensureFile(vfs, "/sys/class/net/eth0/operstate", "up\n");
1102
+ ensureFile(vfs, "/sys/class/net/eth0/carrier", "1\n");
1103
+ ensureFile(vfs, "/sys/class/net/eth0/mtu", "1500\n");
1104
+ ensureFile(vfs, "/sys/class/net/eth0/speed", "10000\n");
1105
+ ensureFile(vfs, "/sys/class/net/eth0/duplex", "full\n");
1106
+ ensureFile(vfs, "/sys/class/net/eth0/address", "aa:bb:cc:dd:ee:ff\n");
1107
+ ensureFile(vfs, "/sys/class/net/eth0/tx_queue_len","1000\n");
1108
+
1109
+ const seed = fnv1a(hostname);
1110
+ const macSeed = seed.toString(16).padStart(8, "0");
1111
+ ensureFile(vfs, "/sys/class/net/eth0/address",
1112
+ `52:54:00:${macSeed.slice(0,2)}:${macSeed.slice(2,4)}:${macSeed.slice(4,6)}\n`,
1113
+ );
1114
+
1115
+ ensureDir(vfs, "/sys/class/net/lo");
1116
+ ensureFile(vfs, "/sys/class/net/lo/operstate", "unknown\n");
1117
+ ensureFile(vfs, "/sys/class/net/lo/carrier", "1\n");
1118
+ ensureFile(vfs, "/sys/class/net/lo/mtu", "65536\n");
1119
+ ensureFile(vfs, "/sys/class/net/lo/address", "00:00:00:00:00:00\n");
1120
+
1121
+ ensureDir(vfs, "/sys/class/block");
1122
+ ensureDir(vfs, "/sys/class/block/vda");
1123
+ ensureFile(vfs, "/sys/class/block/vda/size", "536870912\n");
1124
+ ensureFile(vfs, "/sys/class/block/vda/ro", "0\n");
1125
+ ensureFile(vfs, "/sys/class/block/vda/removable","0\n");
1126
+
1127
+ // cgroup fs
1128
+ ensureDir(vfs, "/sys/fs");
1129
+ ensureDir(vfs, "/sys/fs/cgroup");
1130
+ for (const subsys of ["cpu", "cpuacct", "memory", "devices", "blkio", "pids", "freezer", "unified"]) {
1131
+ ensureDir(vfs, `/sys/fs/cgroup/${subsys}`);
1132
+ if (subsys !== "unified") {
1133
+ ensureFile(vfs, `/sys/fs/cgroup/${subsys}/tasks`, "1\n");
1134
+ ensureFile(vfs, `/sys/fs/cgroup/${subsys}/notify_on_release`, "0\n");
1135
+ ensureFile(vfs, `/sys/fs/cgroup/${subsys}/release_agent`, "");
1136
+ }
1137
+ }
1138
+ ensureFile(vfs, "/sys/fs/cgroup/memory/memory.limit_in_bytes", `${os.totalmem()}\n`);
1139
+ ensureFile(vfs, "/sys/fs/cgroup/memory/memory.usage_in_bytes", `${os.totalmem() - os.freemem()}\n`);
1140
+ ensureFile(vfs, "/sys/fs/cgroup/memory/memory.memsw.limit_in_bytes",`${os.totalmem()}\n`);
1141
+ ensureFile(vfs, "/sys/fs/cgroup/cpu/cpu.cfs_period_us", "100000\n");
1142
+ ensureFile(vfs, "/sys/fs/cgroup/cpu/cpu.cfs_quota_us", "-1\n");
1143
+ ensureFile(vfs, "/sys/fs/cgroup/cpu/cpu.shares", "1024\n");
1144
+
1145
+ ensureDir(vfs, "/sys/kernel");
1146
+ ensureFile(vfs, "/sys/kernel/hostname", `${hostname}\n`);
1147
+ ensureFile(vfs, "/sys/kernel/osrelease", `${props.kernel}\n`);
1148
+ ensureFile(vfs, "/sys/kernel/ostype", "Linux\n");
1149
+
1150
+ // security
1151
+ ensureDir(vfs, "/sys/kernel/security");
1152
+
1153
+
1154
+ // Still; we will create virtual dmi
1155
+ ensureDir(vfs, "/sys/devices/virtual");
534
1156
  ensureDir(vfs, "/sys/devices/virtual/dmi");
535
1157
  ensureDir(vfs, "/sys/devices/virtual/dmi/id");
536
-
537
- const seed = fnv1a(hostname);
538
1158
  const product = `VirtualNode-${(seed % 10000).toString().padStart(4, "0")}`;
539
1159
 
540
1160
  // Full DMI table — deterministic, seeded from hostname
@@ -566,6 +1186,7 @@ function bootstrapSys(vfs: VirtualFileSystem, hostname: string, props: ShellProp
566
1186
  ensureFile(vfs, "/sys/kernel/hostname", `${hostname}\n`);
567
1187
  ensureFile(vfs, "/sys/kernel/osrelease", `${props.kernel}\n`);
568
1188
  ensureFile(vfs, "/sys/kernel/ostype", "Linux\n");
1189
+
569
1190
  }
570
1191
 
571
1192
  // ─── /dev ─────────────────────────────────────────────────────────────────────
@@ -573,38 +1194,70 @@ function bootstrapSys(vfs: VirtualFileSystem, hostname: string, props: ShellProp
573
1194
  function bootstrapDev(vfs: VirtualFileSystem): void {
574
1195
  ensureDir(vfs, "/dev");
575
1196
 
576
- // character devices
577
- ensureFile(vfs, "/dev/null", "", 0o666);
578
- ensureFile(vfs, "/dev/zero", "", 0o666);
579
- ensureFile(vfs, "/dev/full", "", 0o666);
580
- ensureFile(vfs, "/dev/random", "", 0o444);
581
- ensureFile(vfs, "/dev/urandom", "", 0o444);
582
- ensureFile(vfs, "/dev/mem", "", 0o640);
1197
+ // character devices — matching real Firecracker container
1198
+ ensureFile(vfs, "/dev/null", "", 0o666);
1199
+ ensureFile(vfs, "/dev/zero", "", 0o666);
1200
+ ensureFile(vfs, "/dev/full", "", 0o666);
1201
+ ensureFile(vfs, "/dev/random", "", 0o444);
1202
+ ensureFile(vfs, "/dev/urandom", "", 0o444);
1203
+ ensureFile(vfs, "/dev/mem", "", 0o640);
1204
+ ensureFile(vfs, "/dev/port", "", 0o640);
1205
+ ensureFile(vfs, "/dev/kmsg", "", 0o660);
1206
+ ensureFile(vfs, "/dev/hwrng", "", 0o660);
1207
+ ensureFile(vfs, "/dev/fuse", "", 0o660);
1208
+ ensureFile(vfs, "/dev/autofs", "", 0o660);
1209
+ ensureFile(vfs, "/dev/userfaultfd", "", 0o660);
1210
+ ensureFile(vfs, "/dev/cpu_dma_latency", "", 0o660);
1211
+ ensureFile(vfs, "/dev/ptp0", "", 0o660);
1212
+
1213
+ // snapshot (KVM-specific)
1214
+ ensureFile(vfs, "/dev/snapshot", "", 0o660);
583
1215
 
584
1216
  // terminal devices
585
- ensureFile(vfs, "/dev/console", "", 0o600);
586
- ensureFile(vfs, "/dev/tty", "", 0o666);
587
- ensureFile(vfs, "/dev/tty0", "", 0o620);
588
- ensureFile(vfs, "/dev/tty1", "", 0o620);
589
- ensureFile(vfs, "/dev/ttyS0", "", 0o660);
1217
+ ensureFile(vfs, "/dev/console", "", 0o600);
1218
+ ensureFile(vfs, "/dev/tty", "", 0o666);
1219
+ ensureFile(vfs, "/dev/ttyS0", "", 0o660);
1220
+ ensureFile(vfs, "/dev/ptmx", "", 0o666);
1221
+
1222
+ // tty0–63 (like real env)
1223
+ for (let i = 0; i <= 63; i++) {
1224
+ ensureFile(vfs, `/dev/tty${i}`, "", 0o620);
1225
+ }
590
1226
 
591
- // loop devices
1227
+ // vcs devices
1228
+ ensureFile(vfs, "/dev/vcs", "", 0o620);
1229
+ ensureFile(vfs, "/dev/vcs1", "", 0o620);
1230
+ ensureFile(vfs, "/dev/vcsa", "", 0o620);
1231
+ ensureFile(vfs, "/dev/vcsa1", "", 0o620);
1232
+ ensureFile(vfs, "/dev/vcsu", "", 0o620);
1233
+ ensureFile(vfs, "/dev/vcsu1", "", 0o620);
1234
+
1235
+ // loop devices (0–7)
592
1236
  for (let i = 0; i < 8; i++) {
593
1237
  ensureFile(vfs, `/dev/loop${i}`, "", 0o660);
594
1238
  }
595
1239
  ensureDir(vfs, "/dev/loop-control");
596
1240
 
597
- // block device stubs (sda + partitions)
598
- ensureFile(vfs, "/dev/sda", "", 0o660);
599
- ensureFile(vfs, "/dev/sda1", "", 0o660);
600
- ensureFile(vfs, "/dev/sda2", "", 0o660);
1241
+ // virtio block devices (vda–vdd matching mounts)
1242
+ ensureFile(vfs, "/dev/vda", "", 0o660);
1243
+ ensureFile(vfs, "/dev/vdb", "", 0o660);
1244
+ ensureFile(vfs, "/dev/vdc", "", 0o660);
1245
+ ensureFile(vfs, "/dev/vdd", "", 0o660);
1246
+
1247
+ // network tun
1248
+ ensureDir(vfs, "/dev/net");
1249
+ ensureFile(vfs, "/dev/net/tun", "", 0o660);
601
1250
 
602
1251
  // misc
603
1252
  ensureDir(vfs, "/dev/pts");
604
1253
  ensureDir(vfs, "/dev/shm");
1254
+ ensureDir(vfs, "/dev/cpu");
605
1255
  ensureFile(vfs, "/dev/stdin", "", 0o666);
606
1256
  ensureFile(vfs, "/dev/stdout", "", 0o666);
607
1257
  ensureFile(vfs, "/dev/stderr", "", 0o666);
1258
+ ensureDir(vfs, "/dev/fd");
1259
+ ensureFile(vfs, "/dev/vga_arbiter", "", 0o660);
1260
+ ensureFile(vfs, "/dev/vsock", "", 0o660);
608
1261
  }
609
1262
 
610
1263
  // ─── /usr ─────────────────────────────────────────────────────────────────────
@@ -617,13 +1270,31 @@ function bootstrapUsr(vfs: VirtualFileSystem): void {
617
1270
  ensureDir(vfs, "/usr/local/bin");
618
1271
  ensureDir(vfs, "/usr/local/lib");
619
1272
  ensureDir(vfs, "/usr/local/share");
1273
+ ensureDir(vfs, "/usr/local/include");
1274
+ ensureDir(vfs, "/usr/local/sbin");
620
1275
  ensureDir(vfs, "/usr/share");
621
1276
  ensureDir(vfs, "/usr/share/doc");
622
1277
  ensureDir(vfs, "/usr/share/man");
623
1278
  ensureDir(vfs, "/usr/share/man/man1");
1279
+ ensureDir(vfs, "/usr/share/man/man5");
1280
+ ensureDir(vfs, "/usr/share/man/man8");
1281
+ ensureDir(vfs, "/usr/share/common-licenses");
1282
+ ensureDir(vfs, "/usr/share/ca-certificates");
1283
+ ensureDir(vfs, "/usr/share/zoneinfo");
624
1284
  ensureDir(vfs, "/usr/lib");
625
-
626
- // Stubs so `which` can resolve built-in commands
1285
+ ensureDir(vfs, "/usr/lib/x86_64-linux-gnu");
1286
+ ensureDir(vfs, "/usr/lib/python3");
1287
+ ensureDir(vfs, "/usr/lib/python3/dist-packages");
1288
+ ensureDir(vfs, "/usr/lib/python3.12");
1289
+ ensureDir(vfs, "/usr/lib/jvm");
1290
+ ensureDir(vfs, "/usr/lib/jvm/java-21-openjdk-amd64");
1291
+ ensureDir(vfs, "/usr/lib/jvm/java-21-openjdk-amd64/bin");
1292
+ ensureDir(vfs, "/usr/lib/node_modules");
1293
+ ensureDir(vfs, "/usr/lib/node_modules/npm");
1294
+ ensureDir(vfs, "/usr/include");
1295
+ ensureDir(vfs, "/usr/src");
1296
+
1297
+ // builtins — all bins present in the real container
627
1298
  const builtins = [
628
1299
  "sh", "bash", "ls", "cat", "echo", "grep", "find", "sort",
629
1300
  "head", "tail", "cut", "tr", "sed", "awk", "wc", "tee",
@@ -633,58 +1304,452 @@ function bootstrapUsr(vfs: VirtualFileSystem): void {
633
1304
  "nano", "diff", "uniq", "xargs", "base64",
634
1305
  ];
635
1306
 
1307
+ // From a real container
1308
+ // const builtins = [
1309
+ // // core
1310
+ // "sh", "bash", "dash",
1311
+ // "ls", "cat", "echo", "grep", "find", "sort",
1312
+ // "head", "tail", "cut", "tr", "sed", "awk", "mawk", "gawk",
1313
+ // "wc", "tee", "tar", "gzip", "gunzip", "bzip2", "xz",
1314
+ // "touch", "mkdir", "rm", "mv", "cp", "ln", "pwd",
1315
+ // "chmod", "chown", "chgrp", "env", "date", "sleep",
1316
+ // "id", "whoami", "hostname", "uname", "ps", "kill",
1317
+ // "df", "du", "dd", "stat", "file",
1318
+ // // net
1319
+ // "curl", "wget", "nc", "netcat", "ss", "ip",
1320
+ // // editors
1321
+ // "nano", "vi",
1322
+ // // text
1323
+ // "diff", "uniq", "xargs", "base64", "md5sum", "sha256sum",
1324
+ // "strings", "hexdump", "od", "column", "fmt", "paste",
1325
+ // "join", "comm", "split", "csplit", "fold", "expand",
1326
+ // // archive
1327
+ // "zip", "unzip",
1328
+ // // process
1329
+ // "top", "htop", "free", "uptime", "dmesg", "lsof",
1330
+ // "strace", "ltrace", "pgrep", "pkill", "nohup", "nice",
1331
+ // // fs
1332
+ // "mount", "umount", "lsblk", "fdisk", "blkid", "e2fsck",
1333
+ // // misc
1334
+ // "bc", "expr", "seq", "yes", "true", "false", "test",
1335
+ // "readlink", "realpath", "dirname", "basename", "mktemp",
1336
+ // "install", "make",
1337
+ // // dev tools
1338
+ // "gcc", "gcc-13", "g++", "g++-13", "cpp", "as", "ld",
1339
+ // "ar", "nm", "objdump", "objcopy", "strip", "size",
1340
+ // "cc", "c++", "pkg-config",
1341
+ // // package
1342
+ // "apt", "apt-get", "apt-cache", "dpkg", "dpkg-query",
1343
+ // "lsb_release", "add-apt-repository",
1344
+ // // scripting
1345
+ // "perl", "python3", "python3.12", "pipx",
1346
+ // // node/npm
1347
+ // "node", "npm", "npx",
1348
+ // // java
1349
+ // "java", "javac", "jar", "javadoc",
1350
+ // // security
1351
+ // "openssl", "gpg", "gpg2", "gpgv", "ssh", "ssh-keygen",
1352
+ // "sudo", "su", "passwd", "adduser", "useradd",
1353
+ // // misc system
1354
+ // "systemctl", "journalctl", "loginctl",
1355
+ // "timedatectl", "localectl",
1356
+ // "lshw", "lscpu", "lsusb", "lspci",
1357
+ // // text proc
1358
+ // "jq", "xmllint", "pandoc",
1359
+ // // multimedia
1360
+ // "ffmpeg",
1361
+ // ];
1362
+
636
1363
  for (const bin of builtins) {
637
1364
  ensureFile(vfs, `/usr/bin/${bin}`, `#!/bin/sh\nexec builtin ${bin} "$@"\n`, 0o755);
638
1365
  }
639
1366
 
640
- ensureFile(vfs, "/usr/bin/lsb_release", '#!/bin/sh\nexec lsb_release "$@"\n', 0o755);
1367
+ // sbin equivalents
1368
+ const sbins = [
1369
+ "nologin", "useradd", "userdel", "groupadd", "groupdel",
1370
+ "adduser", "deluser", "shutdown", "reboot", "halt",
1371
+ "init", "service", "update-alternatives", "update-rc.d",
1372
+ "depmod", "modprobe", "insmod", "rmmod", "lsmod",
1373
+ "ifconfig", "route", "iptables", "ip6tables",
1374
+ "arp", "iwconfig", "ethtool",
1375
+ "fdisk", "parted", "mkfs.ext4", "fsck",
1376
+ "ldconfig", "ldconfig.real",
1377
+ ];
1378
+ for (const bin of sbins) {
1379
+ ensureFile(vfs, `/usr/sbin/${bin}`, `#!/bin/sh\nexec builtin ${bin} "$@"\n`, 0o755);
1380
+ }
1381
+
1382
+ // versioned python symlink stubs
1383
+ ensureFile(vfs, "/usr/bin/python3.12", `#!/bin/sh\nexec python3 "$@"\n`, 0o755);
1384
+ ensureFile(vfs, "/usr/bin/python3", `#!/bin/sh\nexec python3.12 "$@"\n`, 0o755);
1385
+
1386
+ // node version stubs
1387
+ ensureFile(vfs, "/usr/bin/node", `#!/bin/sh\nexec node "$@"\n`, 0o755);
1388
+ ensureFile(vfs, "/usr/bin/npm", `#!/bin/sh\nexec npm "$@"\n`, 0o755);
1389
+ ensureFile(vfs, "/usr/bin/npx", `#!/bin/sh\nexec npx "$@"\n`, 0o755);
1390
+
1391
+ // java stubs
1392
+ ensureFile(vfs, "/usr/lib/jvm/java-21-openjdk-amd64/bin/java",
1393
+ `#!/bin/sh\nexec java "$@"\n`, 0o755);
1394
+ ensureFile(vfs, "/usr/lib/jvm/java-21-openjdk-amd64/bin/javac",
1395
+ `#!/bin/sh\nexec javac "$@"\n`, 0o755);
1396
+
1397
+ // /usr/share/common-licenses stubs
1398
+ ensureFile(vfs, "/usr/share/common-licenses/GPL-2", "GNU General Public License v2\n");
1399
+ ensureFile(vfs, "/usr/share/common-licenses/GPL-3", "GNU General Public License v3\n");
1400
+ ensureFile(vfs, "/usr/share/common-licenses/LGPL-2.1","GNU Lesser General Public License v2.1\n");
1401
+ ensureFile(vfs, "/usr/share/common-licenses/Apache-2.0","Apache License 2.0\n");
1402
+ ensureFile(vfs, "/usr/share/common-licenses/MIT", "MIT License\n");
641
1403
  }
642
1404
 
643
1405
  // ─── /var ─────────────────────────────────────────────────────────────────────
644
1406
 
1407
+ /** Realistic dpkg status database from real container package list */
1408
+ const DPKG_STATUS = `\
1409
+ Package: bash
1410
+ Status: install ok installed
1411
+ Priority: required
1412
+ Section: shells
1413
+ Installed-Size: 7012
1414
+ Maintainer: Fortune Maintainers <maintainers@fortune.local>
1415
+ Architecture: amd64
1416
+ Version: 5.2.21-2nyx1
1417
+ Depends: base-files (>= 2.1.12), fortune-utils (>= 1.0)
1418
+ Description: GNU Bourne Again SHell
1419
+ bash is an sh-compatible command language interpreter that executes commands
1420
+ read from the standard input or from a file.
1421
+
1422
+ Package: coreutils
1423
+ Status: install ok installed
1424
+ Priority: required
1425
+ Section: utils
1426
+ Installed-Size: 18272
1427
+ Maintainer: Fortune Maintainers <maintainers@fortune.local>
1428
+ Architecture: amd64
1429
+ Version: 9.4-3nyx1
1430
+ Depends: libacl1 (>= 2.2.23), libattr1 (>= 1:2.4.44), libc6 (>= 2.17)
1431
+ Description: GNU core utilities
1432
+ This package contains the basic file, shell and text manipulation utilities.
1433
+
1434
+ Package: nodejs
1435
+ Status: install ok installed
1436
+ Priority: optional
1437
+ Section: web
1438
+ Installed-Size: 107120
1439
+ Maintainer: NodeSource <nodejs@nodesource.com>
1440
+ Architecture: amd64
1441
+ Version: 22.22.2-1nyx1
1442
+ Depends: libc6 (>= 2.17), libgcc-s1 (>= 3.0), libstdc++6 (>= 9.0)
1443
+ Description: Node.js event-based server-side javascript engine
1444
+ Node.js is similar in design to and influenced by systems like Ruby's Twisted.
1445
+
1446
+ Package: python3
1447
+ Status: install ok installed
1448
+ Priority: important
1449
+ Section: python
1450
+ Installed-Size: 68
1451
+ Maintainer: Fortune Maintainers <maintainers@fortune.local>
1452
+ Architecture: amd64
1453
+ Version: 3.12.3-0nyx1
1454
+ Depends: python3.12 (>= 3.12.3-0nyx1)
1455
+ Description: interactive high-level object-oriented language (default python3)
1456
+ Python, the high-level, interactive object oriented language, includes an
1457
+ extensive class library with lots of goodies for network programming.
1458
+
1459
+ Package: python3.12
1460
+ Status: install ok installed
1461
+ Priority: optional
1462
+ Section: python
1463
+ Installed-Size: 36
1464
+ Maintainer: Fortune Maintainers <maintainers@fortune.local>
1465
+ Architecture: amd64
1466
+ Version: 3.12.3-1nyx1
1467
+ Depends: python3.12-minimal (= 3.12.3-1nyx1), libpython3.12-stdlib
1468
+ Description: Interactive high-level object-oriented language (version 3.12)
1469
+ Python is a high-level, interactive, object-oriented language. Its 3.12 version
1470
+ includes an extensive class library.
1471
+
1472
+ Package: gcc-13
1473
+ Status: install ok installed
1474
+ Priority: optional
1475
+ Section: devel
1476
+ Installed-Size: 70460
1477
+ Maintainer: Fortune GCC Maintainers <gcc@fortune.local>
1478
+ Architecture: amd64
1479
+ Version: 13.3.0-6nyx1
1480
+ Depends: cpp-13 (= 13.3.0-6nyx1), gcc-13-base (= 13.3.0-6nyx1)
1481
+ Description: GNU C compiler
1482
+ This is the GNU C compiler, a fairly portable optimizing compiler for C.
1483
+
1484
+ Package: openjdk-21-jre-headless
1485
+ Status: install ok installed
1486
+ Priority: optional
1487
+ Section: java
1488
+ Installed-Size: 174488
1489
+ Maintainer: Fortune Maintainers <maintainers@fortune.local>
1490
+ Architecture: amd64
1491
+ Version: 21.0.10+7-1~nyx
1492
+ Depends: libc6 (>= 2.17), libgcc-s1 (>= 3.4)
1493
+ Description: OpenJDK Java runtime, using Hotspot JIT (headless)
1494
+ Minimal Java runtime - needed for executing non-graphical Java programs.
1495
+
1496
+ Package: curl
1497
+ Status: install ok installed
1498
+ Priority: standard
1499
+ Section: web
1500
+ Installed-Size: 544
1501
+ Maintainer: Fortune Maintainers <maintainers@fortune.local>
1502
+ Architecture: amd64
1503
+ Version: 8.5.0-2nyx1
1504
+ Depends: libcurl4 (= 8.5.0-2nyx1), zlib1g (>= 1:1.1.4)
1505
+ Description: command line tool for transferring data with URL syntax
1506
+ curl is a command line tool for transferring data with URL syntax, supporting
1507
+ DICT, FILE, FTP, FTPS, GOPHER, HTTP, HTTPS, IMAP, IMAPS, LDAP, LDAPS, POP3,
1508
+ POP3S, RTMP, RTSP, SCP, SFTP, SMTP, SMTPS, TELNET and TFTP.
1509
+
1510
+ Package: git
1511
+ Status: install ok installed
1512
+ Priority: optional
1513
+ Section: vcs
1514
+ Installed-Size: 36552
1515
+ Maintainer: Fortune VCS Team <vcs@fortune.local>
1516
+ Architecture: amd64
1517
+ Version: 1:2.43.0-1nyx1
1518
+ Depends: liberror-perl, git-man, libc6 (>= 2.34), libcurl3-gnutls
1519
+ Description: fast, scalable, distributed revision control system
1520
+ Git is popular version control system designed to handle very large projects
1521
+ with speed and efficiency; it is used mainly for various open source projects.
1522
+
1523
+ Package: openssl
1524
+ Status: install ok installed
1525
+ Priority: optional
1526
+ Section: utils
1527
+ Installed-Size: 1320
1528
+ Maintainer: Fortune Security Team <security@fortune.local>
1529
+ Architecture: amd64
1530
+ Version: 3.0.13-0nyx1
1531
+ Depends: libssl3 (>= 3.0.13)
1532
+ Description: Secure Sockets Layer toolkit - cryptographic utility
1533
+ This package is part of the OpenSSL project's implementation of the SSL and TLS
1534
+ cryptographic protocols and related technologies.
1535
+
1536
+ Package: wget
1537
+ Status: install ok installed
1538
+ Priority: standard
1539
+ Section: web
1540
+ Installed-Size: 1100
1541
+ Maintainer: Fortune Maintainers <maintainers@fortune.local>
1542
+ Architecture: amd64
1543
+ Version: 1.21.4-1nyx1
1544
+ Depends: libc6 (>= 2.17), libgnutls30 (>= 3.7.9), libidn2-0 (>= 2.0.0)
1545
+ Description: retrieves files from the web
1546
+ GNU Wget is a program for retrieving files from the web, supporting the HTTP,
1547
+ HTTPS and FTP protocols.
1548
+
1549
+ Package: make
1550
+ Status: install ok installed
1551
+ Priority: optional
1552
+ Section: devel
1553
+ Installed-Size: 556
1554
+ Maintainer: Fortune Maintainers <maintainers@fortune.local>
1555
+ Architecture: amd64
1556
+ Version: 4.3-4.1nyx1
1557
+ Depends: libc6 (>= 2.17)
1558
+ Description: utility for directing compilation
1559
+ GNU Make is a utility which controls the generation of executables and other
1560
+ target files of a program from the program's source files.
1561
+
1562
+ Package: ffmpeg
1563
+ Status: install ok installed
1564
+ Priority: optional
1565
+ Section: video
1566
+ Installed-Size: 2184
1567
+ Maintainer: Fortune Multimedia Team <multimedia@fortune.local>
1568
+ Architecture: amd64
1569
+ Version: 7:6.1.1-3nyx1
1570
+ Depends: libavcodec60, libavdevice60, libavfilter9, libavformat60, libavutil58
1571
+ Description: Tools for transcoding, streaming and playing of multimedia files
1572
+ FFmpeg is a complete, cross-platform solution to record, convert and stream
1573
+ audio and video.
1574
+
1575
+ Package: pandoc
1576
+ Status: install ok installed
1577
+ Priority: optional
1578
+ Section: text
1579
+ Installed-Size: 96248
1580
+ Maintainer: Fortune Haskell Group <haskell@fortune.local>
1581
+ Architecture: amd64
1582
+ Version: 3.1.3+ds-2
1583
+ Depends: libgmp10, libgcc-s1, libffi8
1584
+ Description: general markup converter
1585
+ Pandoc is a Haskell library for converting from one markup format to another.
1586
+
1587
+ Package: tesseract-ocr
1588
+ Status: install ok installed
1589
+ Priority: optional
1590
+ Section: graphics
1591
+ Installed-Size: 1736
1592
+ Maintainer: Fortune OCR Team <ocr@fortune.local>
1593
+ Architecture: amd64
1594
+ Version: 5.3.4-1build5
1595
+ Depends: libc6 (>= 2.14), libleptonica-dev
1596
+ Description: Tesseract Open Source OCR Engine
1597
+ Tesseract is an Open Source OCR Engine, originally developed by HP and now
1598
+ sponsored by Google.
1599
+
1600
+ Package: dpkg
1601
+ Status: install ok installed
1602
+ Priority: required
1603
+ Section: admin
1604
+ Installed-Size: 6800
1605
+ Maintainer: Fortune Package Team <dpkg@fortune.local>
1606
+ Architecture: amd64
1607
+ Version: 1.22.6nyx1
1608
+ Depends: libc6 (>= 2.17), libzstd1 (>= 1.5.5)
1609
+ Description: Fortune package management system
1610
+ This package provides the low-level infrastructure for handling the
1611
+ installation and removal of Fortune software packages.
1612
+
1613
+ Package: apt
1614
+ Status: install ok installed
1615
+ Priority: important
1616
+ Section: admin
1617
+ Installed-Size: 4236
1618
+ Maintainer: Fortune Package Team <apt@fortune.local>
1619
+ Architecture: amd64
1620
+ Version: 2.8.3nyx1
1621
+ Depends: libapt-pkg6.0 (>= 2.8.3), adduser, gpgv
1622
+ Description: commandline package manager
1623
+ This package provides commandline tools for searching and managing as well as
1624
+ querying information about packages as a low-level access to all features of
1625
+ the libapt-pkg library.
1626
+
1627
+ Package: systemd
1628
+ Status: install ok installed
1629
+ Priority: optional
1630
+ Section: admin
1631
+ Installed-Size: 36476
1632
+ Maintainer: Fortune System Team <systemd@fortune.local>
1633
+ Architecture: amd64
1634
+ Version: 255.4-1nyx1
1635
+ Depends: libacl1 (>= 2.2.23), libblkid1 (>= 2.24), libc6 (>= 2.39)
1636
+ Description: system and service manager
1637
+ systemd is a system and service manager for Linux. It provides aggressive
1638
+ parallelization capabilities, uses socket and D-Bus activation for starting
1639
+ services, offers on-demand starting of daemons, keeps track of processes using
1640
+ Linux cgroups, maintains mount and automount points, and implements an
1641
+ elaborate transactional dependency-based service control logic.
1642
+
1643
+ Package: nano
1644
+ Status: install ok installed
1645
+ Priority: important
1646
+ Section: editors
1647
+ Installed-Size: 888
1648
+ Maintainer: Fortune Maintainers <maintainers@fortune.local>
1649
+ Architecture: amd64
1650
+ Version: 7.2-2
1651
+ Depends: libc6 (>= 2.17), libncursesw6 (>= 6)
1652
+ Description: small, friendly text editor inspired by Pico
1653
+ GNU nano is an easy-to-use text editor originally designed as a replacement
1654
+ for Pico, the ncurses-based editor from the non-free mailer package Pine.
1655
+
1656
+ Package: less
1657
+ Status: install ok installed
1658
+ Priority: important
1659
+ Section: text
1660
+ Installed-Size: 344
1661
+ Maintainer: Fortune Maintainers <maintainers@fortune.local>
1662
+ Architecture: amd64
1663
+ Version: 1:640-2build2
1664
+ Depends: libc6 (>= 2.17), libtinfo6 (>= 6)
1665
+ Description: pager program similar to more
1666
+ This package provides the \`less\` command, which is similar to more but allows
1667
+ you to move backwards through the file.
1668
+
1669
+ `;
1670
+
645
1671
  function bootstrapVar(vfs: VirtualFileSystem): void {
646
1672
  ensureDir(vfs, "/var");
647
1673
  ensureDir(vfs, "/var/log");
648
1674
  ensureDir(vfs, "/var/log/apt");
1675
+ ensureDir(vfs, "/var/log/journal");
1676
+ ensureDir(vfs, "/var/log/private");
649
1677
  ensureDir(vfs, "/var/tmp");
650
1678
  ensureDir(vfs, "/var/cache");
651
1679
  ensureDir(vfs, "/var/cache/apt");
652
1680
  ensureDir(vfs, "/var/cache/apt/archives");
1681
+ ensureDir(vfs, "/var/cache/apt/archives/partial");
1682
+ ensureDir(vfs, "/var/cache/debconf");
1683
+ ensureDir(vfs, "/var/cache/ldconfig");
1684
+ ensureDir(vfs, "/var/cache/fontconfig");
1685
+ ensureDir(vfs, "/var/cache/PackageKit");
653
1686
  ensureDir(vfs, "/var/lib");
654
1687
  ensureDir(vfs, "/var/lib/apt");
655
1688
  ensureDir(vfs, "/var/lib/apt/lists");
1689
+ ensureDir(vfs, "/var/lib/apt/lists/partial");
656
1690
  ensureDir(vfs, "/var/lib/dpkg");
657
1691
  ensureDir(vfs, "/var/lib/dpkg/info");
1692
+ ensureDir(vfs, "/var/lib/dpkg/updates");
1693
+ ensureDir(vfs, "/var/lib/dpkg/alternatives");
658
1694
  ensureDir(vfs, "/var/lib/misc");
1695
+ ensureDir(vfs, "/var/lib/systemd");
1696
+ ensureDir(vfs, "/var/lib/systemd/coredump");
1697
+ ensureDir(vfs, "/var/lib/pam");
1698
+ ensureDir(vfs, "/var/lib/git");
1699
+ ensureDir(vfs, "/var/lib/PackageKit");
1700
+ ensureDir(vfs, "/var/lib/python");
659
1701
  ensureDir(vfs, "/var/spool");
660
1702
  ensureDir(vfs, "/var/spool/cron");
1703
+ ensureDir(vfs, "/var/spool/mail");
661
1704
  ensureDir(vfs, "/var/mail");
1705
+ ensureDir(vfs, "/var/backups");
1706
+ ensureDir(vfs, "/var/www");
662
1707
 
663
- // dpkg status — starts empty, VirtualPackageManager populates it
664
- ensureFile(vfs, "/var/lib/dpkg/status", "");
1708
+ // dpkg status — realistic package database
1709
+ ensureFile(vfs, "/var/lib/dpkg/status", DPKG_STATUS);
665
1710
  ensureFile(vfs, "/var/lib/dpkg/available", "");
1711
+ ensureFile(vfs, "/var/lib/dpkg/lock", "");
1712
+ ensureFile(vfs, "/var/lib/dpkg/lock-frontend", "");
1713
+
1714
+ // apt state
1715
+ ensureFile(vfs, "/var/lib/apt/lists/lock", "");
1716
+ ensureFile(vfs, "/var/cache/apt/pkgcache.bin", "");
1717
+ ensureFile(vfs, "/var/cache/apt/srcpkgcache.bin", "");
666
1718
 
667
1719
  // syslog stubs
668
- ensureFile(vfs, "/var/log/syslog", `${new Date().toUTCString()} fortune kernel: Virtual container started\n`);
669
- ensureFile(vfs, "/var/log/auth.log", "");
670
- ensureFile(vfs, "/var/log/kern.log", "");
671
- ensureFile(vfs, "/var/log/dpkg.log", "");
672
- ensureFile(vfs, "/var/log/apt/history.log", "");
673
- ensureFile(vfs, "/var/log/apt/term.log", "");
674
-
675
- // /run — systemd tmpfs runtime dir (canonical on modern Debian)
676
- // /var/run is a legacy symlink to /run
1720
+ ensureFile(vfs, "/var/log/syslog",
1721
+ `${new Date().toUTCString()} ${""} kernel: Virtual container started\n`,
1722
+ );
1723
+ ensureFile(vfs, "/var/log/auth.log", "");
1724
+ ensureFile(vfs, "/var/log/kern.log", "");
1725
+ ensureFile(vfs, "/var/log/dpkg.log", "");
1726
+ ensureFile(vfs, "/var/log/apt/history.log", "");
1727
+ ensureFile(vfs, "/var/log/apt/term.log", "");
1728
+ ensureFile(vfs, "/var/log/faillog", "");
1729
+ ensureFile(vfs, "/var/log/lastlog", "");
1730
+ ensureFile(vfs, "/var/log/wtmp", "");
1731
+ ensureFile(vfs, "/var/log/btmp", "");
1732
+ ensureFile(vfs, "/var/log/alternatives.log", "");
1733
+
1734
+ // /run
677
1735
  ensureDir(vfs, "/run");
678
1736
  ensureDir(vfs, "/run/lock");
1737
+ ensureDir(vfs, "/run/lock/subsys");
679
1738
  ensureDir(vfs, "/run/systemd");
1739
+ ensureDir(vfs, "/run/systemd/ask-password");
1740
+ ensureDir(vfs, "/run/systemd/sessions");
1741
+ ensureDir(vfs, "/run/systemd/users");
680
1742
  ensureDir(vfs, "/run/user");
1743
+ ensureDir(vfs, "/run/dbus");
1744
+ ensureDir(vfs, "/run/adduser");
681
1745
  ensureFile(vfs, "/run/utmp", "");
1746
+ ensureFile(vfs, "/run/dbus/system_bus_socket", "");
682
1747
  }
683
1748
 
684
1749
  // ─── /bin + /sbin symlinks ────────────────────────────────────────────────────
685
1750
 
686
1751
  function bootstrapBin(vfs: VirtualFileSystem): void {
687
- // Modern Debian: /bin and /sbin are symlinks to /usr/bin and /usr/sbin
1752
+ // Modern Fortune Nyx: /bin and /sbin are symlinks to /usr/bin and /usr/sbin
688
1753
  if (!vfs.exists("/bin")) vfs.symlink("/usr/bin", "/bin");
689
1754
  if (!vfs.exists("/sbin")) vfs.symlink("/usr/sbin", "/sbin");
690
1755
 
@@ -695,18 +1760,30 @@ function bootstrapBin(vfs: VirtualFileSystem): void {
695
1760
  ensureDir(vfs, "/lib64");
696
1761
  ensureDir(vfs, "/lib/x86_64-linux-gnu");
697
1762
  ensureDir(vfs, "/lib/modules");
1763
+
1764
+ // lib64 symlink (standard on x86_64)
1765
+ if (!vfs.exists("/lib64/ld-linux-x86-64.so.2")) {
1766
+ ensureFile(vfs, "/lib64/ld-linux-x86-64.so.2", "", 0o755);
1767
+ }
698
1768
  }
699
1769
 
700
1770
  // ─── /tmp ─────────────────────────────────────────────────────────────────────
701
1771
 
702
1772
  function bootstrapTmp(vfs: VirtualFileSystem): void {
703
1773
  ensureDir(vfs, "/tmp", 0o1777);
1774
+ // node compile cache dir (present in real env)
1775
+ ensureDir(vfs, "/tmp/node-compile-cache", 0o1777);
704
1776
  }
705
1777
 
706
1778
  // ─── /root home ───────────────────────────────────────────────────────────────
707
1779
 
708
1780
  function bootstrapRoot(vfs: VirtualFileSystem): void {
709
1781
  ensureDir(vfs, "/root", 0o700);
1782
+ ensureDir(vfs, "/root/.ssh", 0o700);
1783
+ ensureDir(vfs, "/root/.config", 0o755);
1784
+ ensureDir(vfs, "/root/.config/pip", 0o755);
1785
+ ensureDir(vfs, "/root/.local", 0o755);
1786
+ ensureDir(vfs, "/root/.local/share", 0o755);
710
1787
  ensureFile(
711
1788
  vfs,
712
1789
  "/root/.bashrc",
@@ -714,66 +1791,71 @@ function bootstrapRoot(vfs: VirtualFileSystem): void {
714
1791
  "# root .bashrc",
715
1792
  "export PS1='\\[\\033[0;31m\\]\\u@\\h\\[\\033[0m\\]:\\[\\033[0;34m\\]\\w\\[\\033[0m\\]# '",
716
1793
  "export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
1794
+ "export LANG=en_US.UTF-8",
717
1795
  "alias ll='ls -la'",
718
1796
  "alias la='ls -A'",
1797
+ "alias l='ls -CF'",
719
1798
  ].join("\n")}\n`,
720
1799
  );
721
- ensureFile(vfs, "/root/.profile", "[ -f ~/.bashrc ] && . ~/.bashrc\n");
1800
+ ensureFile(vfs, "/root/.profile",
1801
+ "[ -f ~/.bashrc ] && . ~/.bashrc\n",
1802
+ );
1803
+ ensureFile(vfs, "/root/.bash_logout",
1804
+ "# ~/.bash_logout\n",
1805
+ );
1806
+ ensureFile(vfs, "/root/.config/pip/pip.conf",
1807
+ "[global]\nbreak-system-packages = true\n",
1808
+ );
722
1809
  }
723
1810
 
724
- // ─── /opt /srv /mnt /media /home ─────────────────────────────────────────────
1811
+ // ─── /opt /srv /mnt /media ────────────────────────────────────────────────────
725
1812
 
726
1813
  function bootstrapMisc(vfs: VirtualFileSystem, props: ShellProperties): void {
727
1814
  ensureDir(vfs, "/opt");
1815
+ ensureDir(vfs, "/opt/rclone");
728
1816
  ensureDir(vfs, "/srv");
729
1817
  ensureDir(vfs, "/mnt");
730
1818
  ensureDir(vfs, "/media");
731
- ensureDir(vfs, "/home");
732
1819
 
733
- // /boot — grub + kernel images
1820
+ // /boot — no kernel images in Firecracker containers (kernel is external),
1821
+ // but maintain the directory structure for tool compatibility
734
1822
  ensureDir(vfs, "/boot");
735
1823
  ensureDir(vfs, "/boot/grub");
736
- ensureDir(vfs, "/boot/grub/grub.cfg.d");
737
- ensureFile(
738
- vfs,
739
- "/boot/grub/grub.cfg",
1824
+ ensureFile(vfs, "/boot/grub/grub.cfg",
740
1825
  `${[
741
1826
  "# GRUB configuration (virtual)",
742
1827
  "set default=0",
743
- "set timeout=5",
1828
+ "set timeout=0",
744
1829
  "",
745
1830
  `menuentry "Fortune GNU/Linux" {`,
746
- ` linux /vmlinuz root=/dev/sda2 ro quiet splash`,
747
- ` initrd /initrd.img`,
1831
+ ` linux /vmlinuz-${props.kernel} root=/dev/vda rw console=ttyS0`,
1832
+ ` initrd /initrd.img-${props.kernel}`,
748
1833
  `}`,
749
1834
  ].join("\n")}\n`,
750
1835
  );
751
- // kernel + initrd stubs in /boot
1836
+
752
1837
  const kver = props.kernel;
753
- ensureFile(vfs, `/boot/vmlinuz-${kver}`, "", 0o644);
754
- ensureFile(vfs, `/boot/initrd.img-${kver}`, "", 0o644);
755
- ensureFile(vfs, `/boot/System.map-${kver}`, `${kver} virtual\n`, 0o644);
756
- ensureFile(vfs, `/boot/config-${kver}`, `# Linux kernel config ${kver}\n`, 0o644);
757
-
758
- // root-level symlinks (Debian convention)
759
- if (!vfs.exists("/vmlinuz")) vfs.symlink(`/boot/vmlinuz-${kver}`, "/vmlinuz");
760
- if (!vfs.exists("/vmlinuz.old")) vfs.symlink(`/boot/vmlinuz-${kver}`, "/vmlinuz.old");
761
- if (!vfs.exists("/initrd.img")) vfs.symlink(`/boot/initrd.img-${kver}`, "/initrd.img");
762
- if (!vfs.exists("/initrd.img.old")) vfs.symlink(`/boot/initrd.img-${kver}`, "/initrd.img.old");
763
-
764
- // /snap snapd mount namespace root
765
- ensureDir(vfs, "/snap");
766
- ensureDir(vfs, "/snap/bin");
767
-
768
- // /lost+found — ext4 fsck recovery dir (mode 0o700, root only)
1838
+ ensureFile(vfs, `/boot/vmlinuz-${kver}`, "", 0o644);
1839
+ ensureFile(vfs, `/boot/initrd.img-${kver}`, "", 0o644);
1840
+ ensureFile(vfs, `/boot/System.map-${kver}`, `${kver} virtual\n`, 0o644);
1841
+ ensureFile(vfs, `/boot/config-${kver}`, `# Linux kernel config ${kver}\nCONFIG_VIRTIO=y\nCONFIG_VIRTIO_BLK=y\nCONFIG_VIRTIO_NET=y\nCONFIG_KVM_GUEST=y\n`, 0o644);
1842
+
1843
+ if (!vfs.exists("/vmlinuz")) vfs.symlink(`/boot/vmlinuz-${kver}`, "/vmlinuz");
1844
+ if (!vfs.exists("/vmlinuz.old")) vfs.symlink(`/boot/vmlinuz-${kver}`, "/vmlinuz.old");
1845
+ if (!vfs.exists("/initrd.img")) vfs.symlink(`/boot/initrd.img-${kver}`,"/initrd.img");
1846
+ if (!vfs.exists("/initrd.img.old")) vfs.symlink(`/boot/initrd.img-${kver}`,"/initrd.img.old");
1847
+
1848
+ // No /snap — not present in Firecracker container
1849
+ // /proc/cmdline confirms: no snapd boot args
1850
+
1851
+ // /lost+found — ext4 recovery
769
1852
  ensureDir(vfs, "/lost+found", 0o700);
1853
+
1854
+ // /home — users managed by bootstrapRoot + syncEtcPasswd
1855
+ ensureDir(vfs, "/home");
770
1856
  }
771
1857
 
772
1858
  // ── Static rootfs snapshot cache ─────────────────────────────────────────────
773
- // The static parts of the rootfs (dev, usr, var, bin, tmp, etc, sys, misc)
774
- // are identical for all shells sharing the same hostname+props.
775
- // We build them once, serialise to VFSB binary, and clone via decodeVfs()
776
- // for each new shell — avoiding ~250 JS object allocations per instance.
777
1859
 
778
1860
  const _staticRootfsCache = new Map<string, Buffer>();
779
1861
 
@@ -791,11 +1873,10 @@ export function getStaticRootfsSnapshot(
791
1873
  hostname: string,
792
1874
  props: ShellProperties,
793
1875
  ): Buffer {
794
- const key = _staticCacheKey(hostname, props);
1876
+ const key = _staticCacheKey(hostname, props);
795
1877
  const cached = _staticRootfsCache.get(key);
796
1878
  if (cached) return cached;
797
1879
 
798
- // Build the static subset into a temporary VFS
799
1880
  const tmp = new VirtualFileSystem({ mode: "memory" });
800
1881
  bootstrapEtc(tmp, hostname, props);
801
1882
  bootstrapSys(tmp, hostname, props);
@@ -812,7 +1893,6 @@ export function getStaticRootfsSnapshot(
812
1893
  return snapshot;
813
1894
  }
814
1895
 
815
-
816
1896
  // ─── main entry point ─────────────────────────────────────────────────────────
817
1897
 
818
1898
  /**
@@ -834,15 +1914,21 @@ export function bootstrapLinuxRootfs(
834
1914
  shellStartTime: number,
835
1915
  sessions: VirtualActiveSession[] = [],
836
1916
  ): void {
837
- // Fast path: clone the cached static rootfs snapshot (VFSB decode ~0.07ms)
838
- // instead of rebuilding ~250 JS objects from scratch each time.
839
1917
  const snapshot = getStaticRootfsSnapshot(hostname, props);
840
- vfs.importRootTree(decodeVfs(snapshot));
1918
+ const hasRestoredData = vfs.getMode() === "fs" && vfs.exists("/home");
1919
+
1920
+ if (hasRestoredData) {
1921
+ // Snapshot was already restored — merge static rootfs without
1922
+ // clobbering user files and directories.
1923
+ vfs.mergeRootTree(decodeVfs(snapshot));
1924
+ } else {
1925
+ // Fresh start — replace the empty tree with the full static rootfs.
1926
+ vfs.importRootTree(decodeVfs(snapshot));
1927
+ }
841
1928
 
842
- // Dynamic parts: per-instance data injected after the static clone
843
- bootstrapRoot(vfs); // /root home dir + .bashrc
844
- refreshProc(vfs, props, hostname, shellStartTime, sessions); // /proc live data
845
- syncEtcPasswd(vfs, users); // /etc/passwd|group|shadow
1929
+ bootstrapRoot(vfs);
1930
+ refreshProc(vfs, props, hostname, shellStartTime, sessions);
1931
+ syncEtcPasswd(vfs, users);
846
1932
  }
847
1933
 
848
1934
  // ─── optional live engine ─────────────────────────────────────────────────────