typescript-virtual-container 1.2.7 → 1.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +457 -42
- package/dist/SSHMimic/executor.js +3 -5
- package/dist/VirtualFileSystem/binaryPack.d.ts +49 -0
- package/dist/VirtualFileSystem/binaryPack.d.ts.map +1 -0
- package/dist/VirtualFileSystem/binaryPack.js +193 -0
- package/dist/VirtualFileSystem/index.d.ts +7 -5
- package/dist/VirtualFileSystem/index.d.ts.map +1 -1
- package/dist/VirtualFileSystem/index.js +20 -9
- package/dist/VirtualPackageManager/index.d.ts +202 -0
- package/dist/VirtualPackageManager/index.d.ts.map +1 -0
- package/dist/VirtualPackageManager/index.js +676 -0
- package/dist/VirtualShell/index.d.ts +87 -12
- package/dist/VirtualShell/index.d.ts.map +1 -1
- package/dist/VirtualShell/index.js +83 -12
- package/dist/VirtualUserManager/index.d.ts +52 -20
- package/dist/VirtualUserManager/index.d.ts.map +1 -1
- package/dist/VirtualUserManager/index.js +54 -20
- package/dist/commands/alias.d.ts +4 -0
- package/dist/commands/alias.d.ts.map +1 -0
- package/dist/commands/alias.js +58 -0
- package/dist/commands/apt.d.ts +4 -0
- package/dist/commands/apt.d.ts.map +1 -0
- package/dist/commands/apt.js +182 -0
- package/dist/commands/cat.d.ts.map +1 -1
- package/dist/commands/cat.js +27 -8
- package/dist/commands/chmod.d.ts.map +1 -1
- package/dist/commands/chmod.js +52 -3
- package/dist/commands/command-helpers.d.ts +78 -4
- package/dist/commands/command-helpers.d.ts.map +1 -1
- package/dist/commands/command-helpers.js +78 -4
- package/dist/commands/curl.d.ts.map +1 -1
- package/dist/commands/curl.js +81 -29
- package/dist/commands/dpkg.d.ts +4 -0
- package/dist/commands/dpkg.d.ts.map +1 -0
- package/dist/commands/dpkg.js +144 -0
- package/dist/commands/echo.d.ts.map +1 -1
- package/dist/commands/echo.js +24 -12
- package/dist/commands/free.d.ts +3 -0
- package/dist/commands/free.d.ts.map +1 -0
- package/dist/commands/free.js +38 -0
- package/dist/commands/helpers.d.ts +3 -0
- package/dist/commands/helpers.d.ts.map +1 -1
- package/dist/commands/helpers.js +3 -0
- package/dist/commands/history.d.ts +3 -0
- package/dist/commands/history.d.ts.map +1 -0
- package/dist/commands/history.js +21 -0
- package/dist/commands/index.d.ts +8 -1
- package/dist/commands/index.d.ts.map +1 -1
- package/dist/commands/index.js +120 -11
- package/dist/commands/ls.d.ts.map +1 -1
- package/dist/commands/ls.js +4 -3
- package/dist/commands/lsb-release.d.ts +3 -0
- package/dist/commands/lsb-release.d.ts.map +1 -0
- package/dist/commands/lsb-release.js +50 -0
- package/dist/commands/man.d.ts +3 -0
- package/dist/commands/man.d.ts.map +1 -0
- package/dist/commands/man.js +155 -0
- package/dist/commands/neofetch.d.ts.map +1 -1
- package/dist/commands/neofetch.js +5 -0
- package/dist/commands/ping.d.ts.map +1 -1
- package/dist/commands/ping.js +5 -2
- package/dist/commands/ps.d.ts.map +1 -1
- package/dist/commands/ps.js +27 -6
- package/dist/commands/sh.d.ts.map +1 -1
- package/dist/commands/sh.js +29 -11
- package/dist/commands/source.d.ts +3 -0
- package/dist/commands/source.d.ts.map +1 -0
- package/dist/commands/source.js +31 -0
- package/dist/commands/test.d.ts +3 -0
- package/dist/commands/test.d.ts.map +1 -0
- package/dist/commands/test.js +92 -0
- package/dist/commands/type.d.ts +3 -0
- package/dist/commands/type.d.ts.map +1 -0
- package/dist/commands/type.js +34 -0
- package/dist/commands/uptime.d.ts +3 -0
- package/dist/commands/uptime.d.ts.map +1 -0
- package/dist/commands/uptime.js +40 -0
- package/dist/commands/wget.d.ts.map +1 -1
- package/dist/commands/wget.js +71 -100
- package/dist/commands/which.d.ts +3 -0
- package/dist/commands/which.d.ts.map +1 -0
- package/dist/commands/which.js +32 -0
- package/dist/index.d.ts +5 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/modules/linuxRootfs.d.ts +24 -0
- package/dist/modules/linuxRootfs.d.ts.map +1 -0
- package/dist/modules/linuxRootfs.js +297 -0
- package/dist/modules/neofetch.d.ts.map +1 -1
- package/dist/modules/neofetch.js +1 -0
- package/dist/standalone.js +4 -1
- package/package.json +2 -1
- package/src/SSHMimic/executor.ts +3 -5
- package/src/VirtualFileSystem/binaryPack.ts +219 -0
- package/src/VirtualFileSystem/index.ts +21 -11
- package/src/VirtualPackageManager/index.ts +820 -0
- package/src/VirtualShell/index.ts +104 -13
- package/src/VirtualUserManager/index.ts +55 -20
- package/src/commands/alias.ts +60 -0
- package/src/commands/apt.ts +198 -0
- package/src/commands/cat.ts +32 -8
- package/src/commands/chmod.ts +48 -3
- package/src/commands/command-helpers.ts +78 -4
- package/src/commands/curl.ts +78 -37
- package/src/commands/dpkg.ts +158 -0
- package/src/commands/echo.ts +30 -14
- package/src/commands/free.ts +40 -0
- package/src/commands/helpers.ts +8 -0
- package/src/commands/history.ts +29 -0
- package/src/commands/index.ts +116 -11
- package/src/commands/ls.ts +5 -4
- package/src/commands/lsb-release.ts +52 -0
- package/src/commands/man.ts +166 -0
- package/src/commands/neofetch.ts +5 -0
- package/src/commands/ping.ts +5 -2
- package/src/commands/ps.ts +28 -6
- package/src/commands/sh.ts +33 -11
- package/src/commands/source.ts +35 -0
- package/src/commands/test.ts +100 -0
- package/src/commands/type.ts +40 -0
- package/src/commands/uptime.ts +46 -0
- package/src/commands/wget.ts +70 -123
- package/src/commands/which.ts +34 -0
- package/src/index.ts +10 -0
- package/src/modules/linuxRootfs.ts +439 -0
- package/src/modules/neofetch.ts +1 -0
- package/src/standalone.ts +4 -1
- package/standalone.js +418 -103
- package/standalone.js.map +4 -4
- package/tests/new-features.test.ts +626 -0
package/README.md
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
# `typescript-virtual-container`
|
|
2
2
|
|
|
3
|
-
> Pure in-memory SSH/SFTP server with a virtual
|
|
3
|
+
> Pure in-memory SSH/SFTP server with a realistic Linux rootfs, a virtual package manager, a real shell interpreter, and a typed programmatic API for testing, automation, honeypots, and interactive shell scripting in TypeScript/JavaScript.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
> **Release notes — what changed in this drop**
|
|
8
|
+
>
|
|
9
|
+
> **Linux rootfs** — full `/etc`, `/proc`, `/sys`, `/dev`, `/usr`, `/var` hierarchy bootstrapped at init. `neofetch` now shows real uptime and package count. `/proc/meminfo`, `/proc/cpuinfo`, `/proc/version` populated from live system data. `/etc/passwd`, `/etc/group`, `/etc/shadow` synced from `VirtualUserManager`.
|
|
10
|
+
>
|
|
11
|
+
> **Virtual package manager** — `apt install vim git nodejs python3` works. 25 packages in the built-in registry. Dependency resolution, file installation into VFS, `/var/lib/dpkg/status` persistence, `dpkg -l|-s|-L`, `apt-cache search|show|policy`. `neofetch` reflects installed count.
|
|
12
|
+
>
|
|
13
|
+
> **`curl` / `wget` — pure `fetch()`** — host binary no longer spawned. Full flag support (`-o`, `-X`, `-d`, `-H`, `-s`, `-I`, `-L`, `-v` for curl; `-O`, `-P`, `-q` for wget). Zero host filesystem access.
|
|
14
|
+
>
|
|
15
|
+
> **New commands** — `which`, `type`, `man` (with built-in pages), `uptime`, `free`, `lsb_release`, `alias`, `unalias`.
|
|
16
|
+
>
|
|
17
|
+
> **`$(cmd)` command substitution** — `echo $(whoami)`, `mkdir /tmp/$(date +%s)`, nested subs, all work.
|
|
18
|
+
>
|
|
19
|
+
> **Alias expansion** — `alias ll='ls -la'` then `ll /etc` resolves transparently in the dispatcher.
|
|
20
|
+
>
|
|
21
|
+
> *— written by Claude because explaining every change manually is a waste of everyone's time*
|
|
22
|
+
|
|
23
|
+
---
|
|
4
24
|
|
|
5
25
|
[](https://www.npmjs.com/package/typescript-virtual-container)
|
|
6
26
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -21,11 +41,15 @@
|
|
|
21
41
|
- [VirtualSftpServer](#virtualsftpserver)
|
|
22
42
|
- [VirtualShell](#virtualshell)
|
|
23
43
|
- [VirtualFileSystem](#virtualfilesystem)
|
|
44
|
+
- [VFSB Binary Format](#vfsb-binary-format)
|
|
24
45
|
- [VirtualUserManager](#virtualusermanager)
|
|
46
|
+
- [VirtualPackageManager](#virtualpackagemanager)
|
|
25
47
|
- [HoneyPot](#honeypot)
|
|
26
48
|
- [SshClient](#sshclient-programmatic-api)
|
|
27
49
|
- [Key Types](#key-types)
|
|
28
50
|
- [Usage Examples](#usage-examples)
|
|
51
|
+
- [Linux Rootfs](#linux-rootfs)
|
|
52
|
+
- [Package Manager (apt/dpkg)](#package-manager-aptdpkg)
|
|
29
53
|
- [Built-in Commands](#built-in-commands)
|
|
30
54
|
- [Shell Scripting](#shell-scripting)
|
|
31
55
|
- [Configuration](#configuration)
|
|
@@ -46,7 +70,7 @@
|
|
|
46
70
|
|
|
47
71
|
`typescript-virtual-container` is a lightweight, fully-typed SSH/SFTP runtime written in TypeScript that provides:
|
|
48
72
|
|
|
49
|
-
- **Pure in-memory filesystem**: No disk I/O at runtime. All state lives in a fast recursive in-memory tree.
|
|
73
|
+
- **Pure in-memory filesystem**: No disk I/O at runtime. All state lives in a fast recursive in-memory tree. Persist via a compact binary snapshot format (`.vfsb`) or JSON for interoperability.
|
|
50
74
|
- **SSH + SFTP Protocol Support**: Serve SSH shell/exec sessions and SFTP file operations on configurable ports.
|
|
51
75
|
- **Password & public-key authentication**: Register SSH public keys per user alongside (or instead of) password auth.
|
|
52
76
|
- **Rate limiting / brute-force protection**: Configurable per-IP lockout after N failed auth attempts.
|
|
@@ -56,7 +80,11 @@
|
|
|
56
80
|
- **`.bashrc` support**: Loaded automatically at interactive session start from `/home/<user>/.bashrc`.
|
|
57
81
|
- **Event-Driven Architecture**: All core classes extend `EventEmitter` for lifecycle and operation tracking.
|
|
58
82
|
- **Security Auditing**: Built-in `HoneyPot` utility for comprehensive activity logging, event tracking, statistics collection, and anomaly detection across all components.
|
|
59
|
-
- **
|
|
83
|
+
- **Linux rootfs on boot**: Realistic `/etc`, `/proc`, `/sys`, `/dev`, `/usr`, `/var` hierarchy populated at startup — `os-release`, `passwd`, `hosts`, `resolv.conf`, `/proc/meminfo`, `/proc/cpuinfo`, and more.
|
|
84
|
+
- **Virtual package manager**: `apt install`, `apt remove`, `apt search`, `dpkg -l`, `dpkg -s` — 25 packages in the built-in registry (vim, git, nodejs, python3, curl, openssh, gcc…). Writes files into VFS, tracks state in `/var/lib/dpkg/status`.
|
|
85
|
+
- **90+ Built-in Commands**: Full navigation, text processing, archiving, system info, package management, and user management commands — grouped and documented in the interactive `help` system.
|
|
86
|
+
- **`$(cmd)` command substitution**: Nested command execution in any argument position.
|
|
87
|
+
- **Alias support**: `alias`, `unalias` — persisted in session environment.
|
|
60
88
|
- **Full TypeScript Support**: Complete JSDoc coverage, exported types, and first-class async/await for all operations.
|
|
61
89
|
|
|
62
90
|
---
|
|
@@ -73,10 +101,10 @@
|
|
|
73
101
|
### What This Is Not
|
|
74
102
|
|
|
75
103
|
- Not a fully isolated container runtime.
|
|
76
|
-
- Not a security sandbox — the VFS does not sandbox host filesystem access by spawned child processes (e.g. `wget`, `curl` delegate to the host binary).
|
|
77
104
|
- Not a kernel-level isolation boundary (unlike Docker/VM-based isolation).
|
|
105
|
+
- Package stubs (e.g. `node`, `python3`) write files into the VFS and are visible to `which`/`dpkg -L`, but do not execute real binaries — the shell is pure TypeScript with no `execvp`.
|
|
78
106
|
|
|
79
|
-
This project emulates shell behavior for developer workflows. It is designed for realism and
|
|
107
|
+
This project emulates shell behavior for developer workflows. `curl` and `wget` use the native `fetch()` API (no host binary). All other network and execution primitives are simulated. It is designed for realism and deployability, not kernel-level security isolation.
|
|
80
108
|
|
|
81
109
|
---
|
|
82
110
|
|
|
@@ -84,7 +112,7 @@ This project emulates shell behavior for developer workflows. It is designed for
|
|
|
84
112
|
|
|
85
113
|
This package is designed for teams that need a realistic SSH-like runtime without spinning up real containers or VMs.
|
|
86
114
|
|
|
87
|
-
- **Zero disk footprint by default**: The VFS operates entirely in memory. Opt into
|
|
115
|
+
- **Zero disk footprint by default**: The VFS operates entirely in memory. Opt into binary snapshot persistence (`.vfsb`) when you need durability — ~27% smaller and significantly faster than JSON+base64.
|
|
88
116
|
- **Deterministic test environments**: Repeatable state for CI pipelines and integration tests. Build a fixture snapshot once, hydrate for each test.
|
|
89
117
|
- **Low operational overhead**: No Docker daemon, no kernel namespaces, no privileged setup.
|
|
90
118
|
- **Fast feedback loops**: Programmatic API for command execution and filesystem assertions.
|
|
@@ -236,7 +264,7 @@ ssh.stop();
|
|
|
236
264
|
│VirtualFileSystem│ │ VirtualUserManager │
|
|
237
265
|
│ in-memory tree │ │ scrypt · sudoers │
|
|
238
266
|
│ gzip · symlinks │ │ publickey auth │
|
|
239
|
-
│
|
|
267
|
+
│ .vfsb binary │ │ quotas · sessions │
|
|
240
268
|
│ mode:memory|fs │ └─────────────────────-┘
|
|
241
269
|
└─────────────────┘
|
|
242
270
|
│
|
|
@@ -381,12 +409,14 @@ new VirtualShell(
|
|
|
381
409
|
|
|
382
410
|
```typescript
|
|
383
411
|
interface ShellProperties {
|
|
384
|
-
kernel: string; //
|
|
385
|
-
os: string; //
|
|
386
|
-
arch: string; //
|
|
412
|
+
kernel: string; // Kernel version string — shown by uname, neofetch, /proc/version
|
|
413
|
+
os: string; // Full OS description — shown by neofetch, /etc/os-release, lsb_release
|
|
414
|
+
arch: string; // CPU architecture label — shown by uname -m, neofetch
|
|
387
415
|
}
|
|
388
416
|
```
|
|
389
417
|
|
|
418
|
+
Fields map directly to the values reported by `uname -a`, `neofetch`, `lsb_release`, `/etc/os-release`, and `/proc/version` inside the shell. Changing them after construction has no effect — pass them to the constructor.
|
|
419
|
+
|
|
390
420
|
**Example:**
|
|
391
421
|
|
|
392
422
|
```typescript
|
|
@@ -406,13 +436,26 @@ const shell = new VirtualShell("typescript-vm", {
|
|
|
406
436
|
|--------|-------------|
|
|
407
437
|
| `ensureInitialized(): Promise<void>` | Await this before using the shell programmatically. |
|
|
408
438
|
| `addCommand(name, params, callback)` | Register a custom shell command. |
|
|
409
|
-
| `executeCommand(rawInput, authUser, cwd)` | Run a raw command string. |
|
|
410
|
-
| `startInteractiveSession(stream, authUser, sessionId, remoteAddress, terminalSize)` |
|
|
411
|
-
| `writeFileAsUser(authUser, path, content)` | Write a file with quota enforcement. |
|
|
439
|
+
| `executeCommand(rawInput, authUser, cwd)` | Run a raw command string (supports `&&`, `\|`, `$(cmd)`, aliases). |
|
|
440
|
+
| `startInteractiveSession(stream, authUser, sessionId, remoteAddress, terminalSize)` | Attach an interactive PTY session to this shell. |
|
|
441
|
+
| `writeFileAsUser(authUser, path, content)` | Write a file on behalf of a user with quota enforcement. |
|
|
442
|
+
| `refreshProcFs(): void` | Refresh `/proc/uptime`, `/proc/meminfo`, `/proc/cpuinfo`, etc. from live host data. |
|
|
443
|
+
| `syncPasswd(): void` | Sync `/etc/passwd`, `/etc/group`, `/etc/shadow` from `VirtualUserManager` state. |
|
|
412
444
|
| `getVfs(): VirtualFileSystem \| null` | Access the VFS instance. |
|
|
413
445
|
| `getUsers(): VirtualUserManager \| null` | Access the user manager. |
|
|
414
446
|
| `getHostname(): string` | Returns the configured hostname. |
|
|
415
447
|
|
|
448
|
+
#### Public fields
|
|
449
|
+
|
|
450
|
+
| Field | Type | Description |
|
|
451
|
+
|-------|------|-------------|
|
|
452
|
+
| `vfs` | `VirtualFileSystem` | Backing virtual filesystem — use for direct path operations. |
|
|
453
|
+
| `users` | `VirtualUserManager` | Virtual user database — auth, quotas, and session tracking. |
|
|
454
|
+
| `packageManager` | `VirtualPackageManager` | APT/dpkg package manager backed by the built-in registry. |
|
|
455
|
+
| `hostname` | `string` | Hostname shown in the shell prompt and SSH ident string. |
|
|
456
|
+
| `properties` | `ShellProperties` | Distro identity strings surfaced by `uname`, `neofetch`, etc. |
|
|
457
|
+
| `startTime` | `number` | Unix ms timestamp of shell creation — used by `uptime` and `/proc/uptime`. |
|
|
458
|
+
|
|
416
459
|
**Custom command example:**
|
|
417
460
|
|
|
418
461
|
```typescript
|
|
@@ -444,17 +487,17 @@ Two persistence modes are available via the `VfsOptions` constructor argument:
|
|
|
444
487
|
const vfs = new VirtualFileSystem();
|
|
445
488
|
const vfs = new VirtualFileSystem({ mode: "memory" });
|
|
446
489
|
|
|
447
|
-
// FS mode —
|
|
490
|
+
// FS mode — binary snapshot (.vfsb) auto-saved to disk on flushMirror()
|
|
448
491
|
const vfs = new VirtualFileSystem({
|
|
449
492
|
mode: "fs",
|
|
450
|
-
snapshotPath: "./data", // writes ./data/vfs-snapshot.
|
|
493
|
+
snapshotPath: "./data", // writes ./data/vfs-snapshot.vfsb
|
|
451
494
|
});
|
|
452
495
|
await vfs.restoreMirror(); // load from disk (silent no-op if no file yet)
|
|
453
496
|
// ... use vfs ...
|
|
454
497
|
await vfs.flushMirror(); // persist to disk
|
|
455
498
|
```
|
|
456
499
|
|
|
457
|
-
Both modes expose exactly the same API. The tree always lives in memory; `"fs"` mode adds a
|
|
500
|
+
Both modes expose exactly the same API. The tree always lives in memory; `"fs"` mode adds a binary round-trip on `restoreMirror` / `flushMirror`. See [VFSB Binary Format](#vfsb-binary-format) for details.
|
|
458
501
|
|
|
459
502
|
#### Constructor
|
|
460
503
|
|
|
@@ -530,7 +573,8 @@ import { writeFileSync, readFileSync } from "node:fs";
|
|
|
530
573
|
const vfs = new VirtualFileSystem(); // mode: "memory"
|
|
531
574
|
vfs.writeFile("/etc/config.json", JSON.stringify({ debug: true }));
|
|
532
575
|
|
|
533
|
-
// Export to disk manually
|
|
576
|
+
// Export to disk manually as JSON (portable, human-readable)
|
|
577
|
+
// For binary format, use "fs" mode instead — see VFSB Binary Format.
|
|
534
578
|
writeFileSync("vfs-snapshot.json", JSON.stringify(vfs.toSnapshot()));
|
|
535
579
|
|
|
536
580
|
// Restore into a new instance
|
|
@@ -551,10 +595,94 @@ const shell = new VirtualShell("my-vm", undefined, {
|
|
|
551
595
|
|
|
552
596
|
const ssh = new VirtualSshServer({ port: 2222, shell });
|
|
553
597
|
await ssh.start();
|
|
554
|
-
// VFS is restored from ./vfs-data/vfs-snapshot.
|
|
598
|
+
// VFS is restored from ./vfs-data/vfs-snapshot.vfsb on start (if it exists).
|
|
555
599
|
// flushMirror() is called after each write, persisting state to disk automatically.
|
|
556
600
|
```
|
|
557
601
|
|
|
602
|
+
---
|
|
603
|
+
|
|
604
|
+
## VFSB Binary Format
|
|
605
|
+
|
|
606
|
+
When `mode: "fs"` is configured, the VFS persists its state to disk as a compact binary file (`vfs-snapshot.vfsb`) rather than JSON. This is the default and only on-disk format for `"fs"` mode.
|
|
607
|
+
|
|
608
|
+
### Why not JSON?
|
|
609
|
+
|
|
610
|
+
The JSON+base64 approach has two compounding costs: file content is base64-encoded (33% size bloat), and the entire tree must be stringified and parsed on every save/load. For a 10 MB VFS, that means writing ~13.3 MB of base64 data wrapped in JSON — and parsing all of it as a string on restart.
|
|
611
|
+
|
|
612
|
+
The VFSB format eliminates both costs.
|
|
613
|
+
|
|
614
|
+
### Wire format
|
|
615
|
+
|
|
616
|
+
All multi-byte integers are little-endian. The file starts with a 5-byte header, followed by a single recursive node tree.
|
|
617
|
+
|
|
618
|
+
```
|
|
619
|
+
File header
|
|
620
|
+
[4] magic = 0x56 0x46 0x53 0x21 ("VFS!")
|
|
621
|
+
[1] version = 0x01
|
|
622
|
+
|
|
623
|
+
Node (recursive)
|
|
624
|
+
[1] type = 0x01 (file) | 0x02 (directory)
|
|
625
|
+
[2] name length (uint16)
|
|
626
|
+
[N] name bytes (UTF-8)
|
|
627
|
+
[4] mode (uint32)
|
|
628
|
+
[8] createdAt ms (float64 big-endian)
|
|
629
|
+
[8] updatedAt ms (float64 big-endian)
|
|
630
|
+
|
|
631
|
+
File node extra
|
|
632
|
+
[1] compressed flag (0x00 | 0x01)
|
|
633
|
+
[4] content length (uint32)
|
|
634
|
+
[N] content bytes (raw — no base64)
|
|
635
|
+
|
|
636
|
+
Directory node extra
|
|
637
|
+
[4] children count (uint32)
|
|
638
|
+
[N] children nodes (recursive)
|
|
639
|
+
```
|
|
640
|
+
|
|
641
|
+
### Performance
|
|
642
|
+
|
|
643
|
+
Measured on a VFS tree with ~50 nodes and mixed file content:
|
|
644
|
+
|
|
645
|
+
| Metric | JSON+base64 | VFSB binary |
|
|
646
|
+
|--------|-------------|-------------|
|
|
647
|
+
| File size (10 MB of content) | ~13.7 MB | ~10.0 MB |
|
|
648
|
+
| Encode time | ~12 ms | ~0.04 ms |
|
|
649
|
+
| Decode time | ~18 ms | ~0.07 ms |
|
|
650
|
+
| External dependencies | none | none |
|
|
651
|
+
|
|
652
|
+
Size reduction comes from eliminating base64 encoding (33% overhead on raw bytes) and JSON string wrapping. Speed improvement comes from sequential buffer reads/writes instead of string parsing.
|
|
653
|
+
|
|
654
|
+
### Backward compatibility
|
|
655
|
+
|
|
656
|
+
If a legacy JSON snapshot file is found at the configured `snapshotPath`, it is automatically detected by the absence of the `VFS!` magic bytes and parsed as JSON. A migration notice is logged to `console.info`. On the next `flushMirror()` call, the file is rewritten in VFSB format — no manual migration step needed.
|
|
657
|
+
|
|
658
|
+
```typescript
|
|
659
|
+
// This just works — auto-migrates any existing JSON snapshot on first flush
|
|
660
|
+
const vfs = new VirtualFileSystem({ mode: "fs", snapshotPath: "./data" });
|
|
661
|
+
await vfs.restoreMirror(); // reads JSON if .vfsb contains JSON, binary otherwise
|
|
662
|
+
await vfs.flushMirror(); // always writes VFSB binary
|
|
663
|
+
```
|
|
664
|
+
|
|
665
|
+
### Using the binary API directly
|
|
666
|
+
|
|
667
|
+
The encoder and decoder are exported from the VFS module internals for advanced use cases (e.g. replication, custom storage backends):
|
|
668
|
+
|
|
669
|
+
```typescript
|
|
670
|
+
import { encodeVfs, decodeVfs, isBinarySnapshot } from "typescript-virtual-container/src/VirtualFileSystem/binaryPack";
|
|
671
|
+
|
|
672
|
+
// Encode the current tree to a Buffer
|
|
673
|
+
const buf = encodeVfs(vfs.root);
|
|
674
|
+
|
|
675
|
+
// Detect format
|
|
676
|
+
isBinarySnapshot(buf); // true — starts with "VFS!" magic
|
|
677
|
+
isBinarySnapshot(jsonBuf); // false — JSON or other format
|
|
678
|
+
|
|
679
|
+
// Restore from a Buffer
|
|
680
|
+
const root = decodeVfs(buf);
|
|
681
|
+
```
|
|
682
|
+
|
|
683
|
+
These are low-level APIs. For normal usage, `flushMirror()` and `restoreMirror()` are all you need.
|
|
684
|
+
|
|
685
|
+
|
|
558
686
|
---
|
|
559
687
|
|
|
560
688
|
### `VirtualUserManager`
|
|
@@ -579,25 +707,27 @@ new VirtualUserManager(
|
|
|
579
707
|
|--------|-------------|
|
|
580
708
|
| `initialize(): Promise<void>` | Load users/sudoers from VFS, ensure root exists. Call once on startup. |
|
|
581
709
|
| `verifyPassword(username, password): boolean` | Check plaintext password against stored hash. |
|
|
582
|
-
| `hasPassword(username): boolean` | Returns true if a password is set for the user. |
|
|
583
|
-
| `hashPassword(password): string` | Hash a password using
|
|
710
|
+
| `hasPassword(username): boolean` | Returns `true` if a non-empty password is set for the user. |
|
|
711
|
+
| `hashPassword(password): string` | Hash a password using scrypt (or SHA-256 with `SSH_MIMIC_FAST_PASSWORD_HASH=1`). |
|
|
712
|
+
| `getPasswordHash(username): string \| null` | Returns the raw stored hash for a user, or `null` if not found. |
|
|
584
713
|
| `addUser(username, password): Promise<void>` | Create user with home directory. |
|
|
585
|
-
| `deleteUser(username): Promise<void>` | Delete user.
|
|
586
|
-
| `setPassword(username, password): Promise<void>` | Update password for an existing user. |
|
|
587
|
-
| `isSudoer(username): boolean` |
|
|
588
|
-
| `addSudoer(username): Promise<void>` | Grant sudo privileges. |
|
|
589
|
-
| `removeSudoer(username): Promise<void>` | Revoke sudo privileges.
|
|
714
|
+
| `deleteUser(username): Promise<void>` | Delete user. Throws when user is `root` or does not exist. |
|
|
715
|
+
| `setPassword(username, password): Promise<void>` | Update password for an existing user. Throws when user does not exist. |
|
|
716
|
+
| `isSudoer(username): boolean` | Returns `true` when the user has sudo privileges. |
|
|
717
|
+
| `addSudoer(username): Promise<void>` | Grant sudo privileges. Throws when user does not exist. |
|
|
718
|
+
| `removeSudoer(username): Promise<void>` | Revoke sudo privileges. Throws when username is `root`. |
|
|
590
719
|
| `setQuotaBytes(username, maxBytes): Promise<void>` | Set per-user write quota (bytes under `/home/<user>`). |
|
|
591
|
-
| `clearQuota(username): Promise<void>` | Remove quota limit. |
|
|
592
|
-
| `getQuotaBytes(username): number \| null` | Returns quota in bytes, or null if unlimited. |
|
|
720
|
+
| `clearQuota(username): Promise<void>` | Remove quota limit for a user. |
|
|
721
|
+
| `getQuotaBytes(username): number \| null` | Returns quota in bytes, or `null` if unlimited. |
|
|
593
722
|
| `getUsageBytes(username): number` | Returns current usage in bytes under `/home/<user>`. |
|
|
594
723
|
| `assertWriteWithinQuota(username, path, content)` | Throws if the write would exceed the user's quota. |
|
|
724
|
+
| `listUsers(): string[]` | Returns a sorted list of all registered usernames. |
|
|
595
725
|
| `addAuthorizedKey(username, algo, data)` | Register an SSH public key for the user. |
|
|
596
726
|
| `getAuthorizedKeys(username)` | Returns the list of authorized keys for a user. |
|
|
597
727
|
| `removeAuthorizedKeys(username)` | Revoke all authorized keys for a user. |
|
|
598
|
-
| `registerSession(username, remoteAddress): VirtualActiveSession` |
|
|
599
|
-
| `unregisterSession(sessionId): void` |
|
|
600
|
-
| `updateSession(sessionId, username, remoteAddress): void` | Update session metadata
|
|
728
|
+
| `registerSession(username, remoteAddress): VirtualActiveSession` | Register an active session and allocate a virtual TTY. |
|
|
729
|
+
| `unregisterSession(sessionId): void` | Remove a session record on disconnect. Safe to call with `null`. |
|
|
730
|
+
| `updateSession(sessionId, username, remoteAddress): void` | Update session metadata after `su`/`sudo` identity change. |
|
|
601
731
|
| `listActiveSessions(): VirtualActiveSession[]` | Returns all active sessions sorted by start time. |
|
|
602
732
|
|
|
603
733
|
#### Events
|
|
@@ -626,6 +756,54 @@ users.on("session:register", ({ sessionId, username, remoteAddress }) => {
|
|
|
626
756
|
|
|
627
757
|
---
|
|
628
758
|
|
|
759
|
+
|
|
760
|
+
### `VirtualPackageManager`
|
|
761
|
+
|
|
762
|
+
Simulates APT/dpkg package management backed by a built-in registry. Accessed via `shell.packageManager`.
|
|
763
|
+
|
|
764
|
+
#### Constructor
|
|
765
|
+
|
|
766
|
+
Instantiated automatically by `VirtualShell`. Not constructed directly.
|
|
767
|
+
|
|
768
|
+
#### Methods
|
|
769
|
+
|
|
770
|
+
| Method | Description |
|
|
771
|
+
|--------|-------------|
|
|
772
|
+
| `load()` | Load installed packages from `/var/lib/dpkg/status` (called on shell init) |
|
|
773
|
+
| `install(names, opts?)` | Install packages (resolves deps, writes files to VFS). Returns `{ output, exitCode }` |
|
|
774
|
+
| `remove(names, opts?)` | Remove packages. `opts.purge` also removes config files |
|
|
775
|
+
| `search(term)` | Search available packages by name or description |
|
|
776
|
+
| `show(name)` | Show dpkg-style metadata block for a package |
|
|
777
|
+
| `listInstalled()` | List all installed packages as `InstalledPackage[]` |
|
|
778
|
+
| `listAvailable()` | List all packages in the registry |
|
|
779
|
+
| `isInstalled(name)` | Returns `true` if package is installed |
|
|
780
|
+
| `installedCount()` | Count of installed packages (used by `neofetch`) |
|
|
781
|
+
| `findInRegistry(name)` | Look up a `PackageDefinition` by name |
|
|
782
|
+
|
|
783
|
+
#### Types
|
|
784
|
+
|
|
785
|
+
```ts
|
|
786
|
+
interface PackageDefinition {
|
|
787
|
+
name: string;
|
|
788
|
+
version: string;
|
|
789
|
+
description: string;
|
|
790
|
+
files?: PackageFile[]; // written to VFS on install
|
|
791
|
+
depends?: string[]; // resolved recursively
|
|
792
|
+
onInstall?: (vfs, users) => void;
|
|
793
|
+
onRemove?: (vfs) => void;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
interface InstalledPackage {
|
|
797
|
+
name: string;
|
|
798
|
+
version: string;
|
|
799
|
+
architecture: string;
|
|
800
|
+
installedAt: string; // ISO-8601
|
|
801
|
+
files: string[]; // paths written to VFS
|
|
802
|
+
}
|
|
803
|
+
```
|
|
804
|
+
|
|
805
|
+
---
|
|
806
|
+
|
|
629
807
|
### `HoneyPot`
|
|
630
808
|
|
|
631
809
|
Comprehensive security auditing and event tracking utility. Attaches listeners to all core components to log activity, track statistics, and detect anomalies.
|
|
@@ -880,6 +1058,49 @@ interface VfsSnapshot {
|
|
|
880
1058
|
|
|
881
1059
|
---
|
|
882
1060
|
|
|
1061
|
+
### Command Helpers
|
|
1062
|
+
|
|
1063
|
+
Three utility functions are exported from `typescript-virtual-container` to assist with argument parsing inside custom command handlers.
|
|
1064
|
+
|
|
1065
|
+
#### `ifFlag(args, flags): boolean`
|
|
1066
|
+
|
|
1067
|
+
Returns `true` when any of the given flags appear in the argument array. Matches both standalone tokens (`-s`, `--silent`) and inline forms (`--output=file`).
|
|
1068
|
+
|
|
1069
|
+
```typescript
|
|
1070
|
+
import { ifFlag } from "typescript-virtual-container";
|
|
1071
|
+
|
|
1072
|
+
const recursive = ifFlag(args, ["-r", "--recursive"]);
|
|
1073
|
+
const silent = ifFlag(args, "-s");
|
|
1074
|
+
```
|
|
1075
|
+
|
|
1076
|
+
#### `getFlag(args, flags): string | true | undefined`
|
|
1077
|
+
|
|
1078
|
+
Returns the value of a flag, `true` if the flag has no value, or `undefined` if absent.
|
|
1079
|
+
|
|
1080
|
+
```typescript
|
|
1081
|
+
import { getFlag } from "typescript-virtual-container";
|
|
1082
|
+
|
|
1083
|
+
const output = getFlag(args, ["-o", "--output"]);
|
|
1084
|
+
// args = ["--output", "file.txt"] → "file.txt"
|
|
1085
|
+
// args = ["--output=file.txt"] → "file.txt"
|
|
1086
|
+
// args = ["--verbose"] → true (when "verbose" is in flags list)
|
|
1087
|
+
// args = [] → undefined
|
|
1088
|
+
```
|
|
1089
|
+
|
|
1090
|
+
#### `getArg(args, index, options?): string | undefined`
|
|
1091
|
+
|
|
1092
|
+
Returns the positional argument at a given zero-based index, skipping known flags and their values.
|
|
1093
|
+
|
|
1094
|
+
```typescript
|
|
1095
|
+
import { getArg } from "typescript-virtual-container";
|
|
1096
|
+
|
|
1097
|
+
// args = ["-r", "src", "dest"]
|
|
1098
|
+
const src = getArg(args, 0, { flags: ["-r"] }); // "src"
|
|
1099
|
+
const dest = getArg(args, 1, { flags: ["-r"] }); // "dest"
|
|
1100
|
+
```
|
|
1101
|
+
|
|
1102
|
+
---
|
|
1103
|
+
|
|
883
1104
|
## Usage Examples
|
|
884
1105
|
|
|
885
1106
|
### Example 1: Basic SSH Server
|
|
@@ -973,6 +1194,7 @@ import { writeFileSync, readFileSync } from "node:fs";
|
|
|
973
1194
|
const vfs = new VirtualFileSystem();
|
|
974
1195
|
vfs.writeFile("/data/report.txt", "Baseline data");
|
|
975
1196
|
|
|
1197
|
+
// JSON — portable/human-readable. For binary persistence use mode: "fs".
|
|
976
1198
|
writeFileSync("snapshot.json", JSON.stringify(vfs.toSnapshot()));
|
|
977
1199
|
|
|
978
1200
|
const snapshot = JSON.parse(readFileSync("snapshot.json", "utf8"));
|
|
@@ -1229,6 +1451,145 @@ const [r1, r2] = await Promise.all([
|
|
|
1229
1451
|
]);
|
|
1230
1452
|
```
|
|
1231
1453
|
|
|
1454
|
+
---
|
|
1455
|
+
|
|
1456
|
+
## Linux Rootfs
|
|
1457
|
+
|
|
1458
|
+
On every `VirtualShell` init, a realistic Linux directory hierarchy is bootstrapped into the VFS. All paths are created idempotently — FS-mode snapshots survive restarts without duplication.
|
|
1459
|
+
|
|
1460
|
+
### Directory layout
|
|
1461
|
+
|
|
1462
|
+
```
|
|
1463
|
+
/
|
|
1464
|
+
├── bin -> /usr/bin (symlink, Debian-style)
|
|
1465
|
+
├── dev/ null, zero, random, urandom, pts/, shm/
|
|
1466
|
+
├── etc/
|
|
1467
|
+
│ ├── apt/sources.list Fortune package sources
|
|
1468
|
+
│ ├── debian_version
|
|
1469
|
+
│ ├── group synced from VirtualUserManager
|
|
1470
|
+
│ ├── hostname
|
|
1471
|
+
│ ├── hosts 127.0.0.1 localhost + VM hostname
|
|
1472
|
+
│ ├── motd
|
|
1473
|
+
│ ├── os-release NAME="Fortune GNU/Linux" + ShellProperties
|
|
1474
|
+
│ ├── passwd synced from VirtualUserManager
|
|
1475
|
+
│ ├── resolv.conf 1.1.1.1 + 8.8.8.8
|
|
1476
|
+
│ └── shadow (mode 0o640)
|
|
1477
|
+
├── proc/
|
|
1478
|
+
│ ├── cpuinfo real host CPU info
|
|
1479
|
+
│ ├── loadavg
|
|
1480
|
+
│ ├── meminfo real host memory
|
|
1481
|
+
│ ├── net/dev eth0 + lo
|
|
1482
|
+
│ ├── uptime shell uptime in seconds
|
|
1483
|
+
│ └── version kernel from ShellProperties
|
|
1484
|
+
├── root/ root home + .bashrc
|
|
1485
|
+
├── sys/devices/virtual/dmi/id/
|
|
1486
|
+
│ ├── sys_vendor "Fortune Systems"
|
|
1487
|
+
│ └── product_name "VirtualContainer v1"
|
|
1488
|
+
├── tmp/ (mode 0o1777 sticky)
|
|
1489
|
+
├── usr/bin/ stubs for all built-in commands
|
|
1490
|
+
├── var/
|
|
1491
|
+
│ ├── lib/dpkg/status managed by VirtualPackageManager
|
|
1492
|
+
│ └── log/ syslog, auth.log, dpkg.log, apt/
|
|
1493
|
+
└── ...
|
|
1494
|
+
```
|
|
1495
|
+
|
|
1496
|
+
### API
|
|
1497
|
+
|
|
1498
|
+
```ts
|
|
1499
|
+
shell.refreshProcFs(); // refresh /proc/* with current system state
|
|
1500
|
+
shell.syncPasswd(); // sync /etc/passwd|group|shadow from VirtualUserManager
|
|
1501
|
+
```
|
|
1502
|
+
|
|
1503
|
+
`syncPasswd()` is called automatically on `bootstrapLinuxRootfs`. Call it again after `users.addUser()` / `users.deleteUser()` to keep `/etc/passwd` consistent.
|
|
1504
|
+
|
|
1505
|
+
---
|
|
1506
|
+
|
|
1507
|
+
## Package Manager (apt/dpkg)
|
|
1508
|
+
|
|
1509
|
+
A pure-TypeScript APT/dpkg simulation backed by a built-in package registry. No external process is spawned.
|
|
1510
|
+
|
|
1511
|
+
### Workflow
|
|
1512
|
+
|
|
1513
|
+
```
|
|
1514
|
+
apt install <pkg> → resolves deps → writes files to VFS → updates /var/lib/dpkg/status
|
|
1515
|
+
apt remove <pkg> → removes VFS files → updates status
|
|
1516
|
+
dpkg -l → reads installed packages from VirtualPackageManager
|
|
1517
|
+
```
|
|
1518
|
+
|
|
1519
|
+
### Built-in package registry (25 packages)
|
|
1520
|
+
|
|
1521
|
+
| Package | Version | Section |
|
|
1522
|
+
|---------|---------|---------|
|
|
1523
|
+
| `vim` | 2:9.0.1378-2 | editors |
|
|
1524
|
+
| `git` | 1:2.39.2-1 | vcs |
|
|
1525
|
+
| `python3` | 3.11.2-1 | python |
|
|
1526
|
+
| `nodejs` | 18.19.0 | javascript |
|
|
1527
|
+
| `npm` | 9.2.0 | javascript |
|
|
1528
|
+
| `curl` | 7.88.1-10 | web |
|
|
1529
|
+
| `wget` | 1.21.3-1 | web |
|
|
1530
|
+
| `htop` | 3.2.2-2 | utils |
|
|
1531
|
+
| `openssh-client` | 1:9.2p1-2 | net |
|
|
1532
|
+
| `openssh-server` | 1:9.2p1-2 | net |
|
|
1533
|
+
| `net-tools` | 2.10-0.1 | net |
|
|
1534
|
+
| `iputils-ping` | 3:20221126-1 | net |
|
|
1535
|
+
| `jq` | 1.6-2.1 | utils |
|
|
1536
|
+
| `build-essential` | 12.9 | devel |
|
|
1537
|
+
| `gcc` | 4:12.2.0-3 | devel |
|
|
1538
|
+
| `g++` | 4:12.2.0-3 | devel |
|
|
1539
|
+
| `make` | 4.3-4.1 | devel |
|
|
1540
|
+
| `less` | 590-2 | text |
|
|
1541
|
+
| `unzip` | 6.0-28 | utils |
|
|
1542
|
+
| `rsync` | 3.2.7-1 | net |
|
|
1543
|
+
| `tmux` | 3.3a-3 | utils |
|
|
1544
|
+
| `tree` | 2.1.0-1 | utils |
|
|
1545
|
+
| `ca-certificates` | 20230311 | misc |
|
|
1546
|
+
| `sudo` | 1.9.13p3-1 | admin |
|
|
1547
|
+
| `systemd` | 252.22-1 | admin |
|
|
1548
|
+
|
|
1549
|
+
> **Note on package stubs**: installing `nodejs` or `python3` writes stubs into `/usr/bin/` and makes them discoverable via `which` and `dpkg -L`. The stubs print a version line but do not execute real binaries — the shell runtime is pure TypeScript with no `execvp`. This makes them useful for testing install workflows and `which`/`dpkg -L` queries, not for running actual scripts.
|
|
1550
|
+
|
|
1551
|
+
### `VirtualPackageManager` API
|
|
1552
|
+
|
|
1553
|
+
```ts
|
|
1554
|
+
// Access via shell instance
|
|
1555
|
+
const pm = shell.packageManager;
|
|
1556
|
+
|
|
1557
|
+
pm.install(["vim", "git"], { quiet: false }) // → { output, exitCode }
|
|
1558
|
+
pm.remove(["vim"], { purge: false }) // → { output, exitCode }
|
|
1559
|
+
pm.search("editor") // → PackageDefinition[]
|
|
1560
|
+
pm.show("vim") // → string (dpkg-style block)
|
|
1561
|
+
pm.listInstalled() // → InstalledPackage[]
|
|
1562
|
+
pm.listAvailable() // → PackageDefinition[]
|
|
1563
|
+
pm.isInstalled("vim") // → boolean
|
|
1564
|
+
pm.installedCount() // → number
|
|
1565
|
+
pm.findInRegistry("nodejs") // → PackageDefinition | undefined
|
|
1566
|
+
```
|
|
1567
|
+
|
|
1568
|
+
### Registering custom packages
|
|
1569
|
+
|
|
1570
|
+
```ts
|
|
1571
|
+
import { VirtualPackageManager } from "typescript-virtual-container";
|
|
1572
|
+
|
|
1573
|
+
// Custom packages can be added to the registry before shell init
|
|
1574
|
+
// by extending PACKAGE_REGISTRY or by calling install() with custom defs.
|
|
1575
|
+
|
|
1576
|
+
// Or: register a post-install hook via onInstall
|
|
1577
|
+
const customPkg = {
|
|
1578
|
+
name: "myapp",
|
|
1579
|
+
version: "1.0.0",
|
|
1580
|
+
description: "My application",
|
|
1581
|
+
files: [
|
|
1582
|
+
{ path: "/usr/bin/myapp", content: "#!/bin/sh\necho myapp v1.0.0\n", mode: 0o755 },
|
|
1583
|
+
{ path: "/etc/myapp/config.json", content: JSON.stringify({ port: 3000 }) },
|
|
1584
|
+
],
|
|
1585
|
+
onInstall: (vfs) => {
|
|
1586
|
+
vfs.mkdir("/var/lib/myapp", 0o755);
|
|
1587
|
+
vfs.mkdir("/var/log/myapp", 0o755);
|
|
1588
|
+
},
|
|
1589
|
+
};
|
|
1590
|
+
```
|
|
1591
|
+
|
|
1592
|
+
|
|
1232
1593
|
---
|
|
1233
1594
|
|
|
1234
1595
|
## Built-in Commands
|
|
@@ -1240,7 +1601,7 @@ All commands are available in SSH shell mode and via `SshClient.exec()`. Type `h
|
|
|
1240
1601
|
| Command | Flags | Description |
|
|
1241
1602
|
|---------|-------|-------------|
|
|
1242
1603
|
| `cd <path>` | | Change directory |
|
|
1243
|
-
| `ls [path]` | `-l` | List directory contents |
|
|
1604
|
+
| `ls [path]` | `-l` `-a` | List directory contents (`-a` shows dotfiles) |
|
|
1244
1605
|
| `pwd` | | Print working directory |
|
|
1245
1606
|
| `tree [path]` | | ASCII directory tree |
|
|
1246
1607
|
|
|
@@ -1248,8 +1609,8 @@ All commands are available in SSH shell mode and via `SshClient.exec()`. Type `h
|
|
|
1248
1609
|
|
|
1249
1610
|
| Command | Flags | Description |
|
|
1250
1611
|
|---------|-------|-------------|
|
|
1251
|
-
| `cat <path
|
|
1252
|
-
| `chmod <mode> <file>` | | Change file permissions (
|
|
1612
|
+
| `cat <path...>` | `-n` `-b` | Concatenate and print files; `-n` numbers all lines, `-b` numbers non-blank lines |
|
|
1613
|
+
| `chmod <mode> <file>` | | Change file permissions — octal (`755`) or symbolic (`+x`, `u+x`, `go-w`, `a=rx`) |
|
|
1253
1614
|
| `cp <src> <dest>` | `-r` | Copy file or directory |
|
|
1254
1615
|
| `find [path]` | `-name <pat>` `-type f\|d` | Search for files |
|
|
1255
1616
|
| `ln <target> <link>` | `-s` | Create hard or symbolic link |
|
|
@@ -1297,9 +1658,12 @@ All commands are available in SSH shell mode and via `SshClient.exec()`. Type `h
|
|
|
1297
1658
|
| `htop` | | System monitor (mock) |
|
|
1298
1659
|
| `id [user]` | | Print user identity (uid/gid/groups) |
|
|
1299
1660
|
| `kill [-9] <pid>` | | Send signal to process (mock) |
|
|
1300
|
-
| `
|
|
1661
|
+
| `free` | `-h` `-m` `-g` | Display free and used memory |
|
|
1662
|
+
| `lsb_release` | `-a` `-i` `-d` `-r` `-c` | Print distribution information |
|
|
1663
|
+
| `neofetch` | | System info display (shows real packages/uptime) |
|
|
1664
|
+
| `uptime` | `-p` `-s` | Tell how long the system has been running |
|
|
1301
1665
|
| `ping [-c <n>] <host>` | | Send ICMP ECHO_REQUEST (mock) |
|
|
1302
|
-
| `ps` | `-a` `-u` `-x` | Report process status |
|
|
1666
|
+
| `ps` | `-a` `-u` `-x` `aux` | Report process status; `-u` / `aux` shows USER/PID/%CPU/%MEM columns |
|
|
1303
1667
|
| `sleep <seconds>` | | Delay execution |
|
|
1304
1668
|
| `uname` | `-a` `-r` `-m` | Print system information |
|
|
1305
1669
|
| `who` | | List active sessions |
|
|
@@ -1309,23 +1673,50 @@ All commands are available in SSH shell mode and via `SshClient.exec()`. Type `h
|
|
|
1309
1673
|
|
|
1310
1674
|
| Command | Flags | Description |
|
|
1311
1675
|
|---------|-------|-------------|
|
|
1312
|
-
| `curl <url>` | | HTTP client (
|
|
1313
|
-
| `wget <url>` | | File downloader (
|
|
1676
|
+
| `curl <url>` | `-o` `-X` `-d` `-H` `-s` `-I` `-L` `-v` | HTTP client (pure `fetch()`, no host binary) |
|
|
1677
|
+
| `wget <url>` | `-O` `-P` `-q` | File downloader (pure `fetch()`, no host binary) |
|
|
1314
1678
|
|
|
1315
1679
|
### Shell
|
|
1316
1680
|
|
|
1317
1681
|
| Command | Flags | Description |
|
|
1318
1682
|
|---------|-------|-------------|
|
|
1683
|
+
| `alias [name=value]` | | Define or display aliases |
|
|
1319
1684
|
| `clear` | | Clear terminal screen (full ANSI reset) |
|
|
1320
|
-
| `echo <text>` | | Display text |
|
|
1685
|
+
| `echo <text>` | `-n` `-e` | Display text; `-n` suppresses newline, `-e` interprets escape sequences (`\n`, `\t`, `\r`, `\\`) |
|
|
1321
1686
|
| `env` | | Print session environment variables |
|
|
1322
1687
|
| `exit [code]` | | Exit session |
|
|
1323
1688
|
| `export NAME=VALUE` | | Set shell variable in current session |
|
|
1324
1689
|
| `help [command]` | | List commands (grouped) or show command details |
|
|
1325
1690
|
| `set [VAR=val]` | | Display or set shell variables |
|
|
1326
|
-
| `sh` | `-c <script>` `[file]` | Execute shell script (supports if/for/while) |
|
|
1691
|
+
| `sh` | `-c <script>` `[file]` | Execute shell script (supports if/for/while/do/done); `$(cmd)` substitution inside single-quoted args is preserved |
|
|
1692
|
+
| `history [n]` | | Display command history (last N entries) |
|
|
1693
|
+
| `source <file>` | | Execute file in current shell environment (aliases and exports persist) |
|
|
1694
|
+
| `. <file>` | | Alias for `source` |
|
|
1695
|
+
| `test <expr>` | | Evaluate conditional expression |
|
|
1696
|
+
| `[ <expr> ]` | | Alias for `test` — supports `-f`, `-d`, `-e`, `-z`, `-n`, `=`, `!=`, `-eq`, `-lt`, `-gt`, etc. |
|
|
1697
|
+
| `unalias <name>` | `-a` | Remove alias definitions |
|
|
1327
1698
|
| `unset <VAR>` | | Remove shell variable |
|
|
1328
1699
|
|
|
1700
|
+
### Package Management
|
|
1701
|
+
|
|
1702
|
+
| Command | Flags | Description |
|
|
1703
|
+
|---------|-------|-------------|
|
|
1704
|
+
| `apt <cmd> [pkg...]` | | Package manager (`install`, `remove`, `purge`, `update`, `upgrade`, `search`, `show`, `list`) |
|
|
1705
|
+
| `apt-get <cmd>` | | Alias for `apt` |
|
|
1706
|
+
| `apt-cache <cmd>` | | Query package cache (`search`, `show`, `policy`) |
|
|
1707
|
+
| `dpkg` | `-l` `-s` `-L` `-r` `-P` | Debian package manager low-level tool |
|
|
1708
|
+
| `dpkg-query` | `-W` `-l` | Show information about installed packages |
|
|
1709
|
+
|
|
1710
|
+
### Shell (extended)
|
|
1711
|
+
|
|
1712
|
+
| Command | Flags | Description |
|
|
1713
|
+
|---------|-------|-------------|
|
|
1714
|
+
| `alias [name=value]` | | Define or display aliases |
|
|
1715
|
+
| `man <command>` | | Display command manual page |
|
|
1716
|
+
| `type <command>` | | Describe how command is interpreted |
|
|
1717
|
+
| `unalias <name>` | `-a` | Remove alias definitions |
|
|
1718
|
+
| `which <command>` | | Locate command in PATH |
|
|
1719
|
+
|
|
1329
1720
|
### Users & Permissions
|
|
1330
1721
|
|
|
1331
1722
|
| Command | Flags | Description |
|
|
@@ -1575,10 +1966,10 @@ No. It emulates SSH sessions, users, and filesystem behavior in a virtual runtim
|
|
|
1575
1966
|
You can use it in production-like automation contexts (sandboxed command runners, test harnesses, training environments, honeypots). It is not a security boundary like a real container/VM.
|
|
1576
1967
|
|
|
1577
1968
|
**Does the VFS touch the host filesystem?**
|
|
1578
|
-
In the default `"memory"` mode: no, all data lives in memory. In `"fs"` mode, it reads/writes a single
|
|
1969
|
+
In the default `"memory"` mode: no, all data lives in memory. In `"fs"` mode, it reads/writes a single binary file (`vfs-snapshot.vfsb`) inside the configured `snapshotPath` directory. No other host paths are accessed. See [VFSB Binary Format](#vfsb-binary-format).
|
|
1579
1970
|
|
|
1580
1971
|
**Does data persist between restarts?**
|
|
1581
|
-
Only if you explicitly use `"fs"` mode or call `toSnapshot()` / `fromSnapshot()` manually. Memory mode is ephemeral.
|
|
1972
|
+
Only if you explicitly use `"fs"` mode or call `toSnapshot()` / `fromSnapshot()` manually. Memory mode is ephemeral. In `"fs"` mode, the snapshot is stored as a binary `.vfsb` file — see [VFSB Binary Format](#vfsb-binary-format).
|
|
1582
1973
|
|
|
1583
1974
|
**Can I run multiple isolated shells?**
|
|
1584
1975
|
Yes. Each `new VirtualShell(...)` creates a completely independent VFS, user manager, and shell environment.
|
|
@@ -1701,9 +2092,33 @@ MIT — see [LICENSE](./LICENSE).
|
|
|
1701
2092
|
- [x] Grouped, colorized `help` with per-command detail
|
|
1702
2093
|
- [x] New commands: `sort`, `uniq`, `tee`, `cut`, `tr`, `xargs`, `diff`, `sed`, `awk`, `tar`, `gzip`, `gunzip`, `base64`, `date`, `sleep`, `id`, `groups`, `uname`, `ps`, `kill`, `df`, `du`, `ping`
|
|
1703
2094
|
- [x] Structured event hooks (session open/close, file write, sudo challenge)
|
|
2095
|
+
- [x] Binary snapshot format (VFSB) — replaces JSON+base64, ~27% smaller, no string parsing overhead, backward-compatible JSON migration
|
|
2096
|
+
- [x] Linux rootfs on boot — `/etc`, `/proc`, `/sys`, `/dev`, `/usr`, `/var` populated at init; `os-release`, `passwd`, `hosts`, `/proc/meminfo`, `/proc/cpuinfo`, `/proc/version`, `/proc/uptime`, `/sys/devices/virtual/dmi/`, symlinks `/bin`→`/usr/bin`
|
|
2097
|
+
- [x] Virtual package manager — `apt install/remove/purge/update/upgrade/search/show/list`, `apt-get`, `apt-cache`, `dpkg`, `dpkg-query`; 25 packages in registry; writes files to VFS; `/var/lib/dpkg/status` persistence
|
|
2098
|
+
- [x] `curl` / `wget` reimplemented as pure `fetch()` — no host binary spawned, full isolation
|
|
2099
|
+
- [x] New commands: `which`, `type`, `man`, `uptime`, `free`, `lsb_release`, `alias`, `unalias`
|
|
2100
|
+
- [x] `$(cmd)` command substitution in variable expansion
|
|
2101
|
+
- [x] Alias expansion in command dispatch
|
|
2102
|
+
- [x] `neofetch` shows real package count and shell uptime
|
|
2103
|
+
- [x] `syncPasswd()` / `refreshProcFs()` public API on `VirtualShell`
|
|
2104
|
+
- [x] `test` / `[` — full POSIX conditional expressions (`-f`, `-d`, `-e`, `-z`, `-n`, `-x`, `-s`, `-L`, `=`, `!=`, `-eq`, `-lt`, `-gt`, `-le`, `-ge`, `!`, `-a`, `-o`)
|
|
2105
|
+
- [x] `source` / `.` — execute file in current shell env (aliases and exports persist across commands)
|
|
2106
|
+
- [x] `history [n]` — display command history from VFS `.bash_history`
|
|
2107
|
+
- [x] `echo -e` / `echo -n` — escape sequence interpretation and newline suppression
|
|
2108
|
+
- [x] `ls -a` — show dotfiles
|
|
2109
|
+
- [x] `chmod` symbolic modes — `+x`, `u+x`, `go-w`, `a=rx`, comma-separated
|
|
2110
|
+
- [x] `cat -n` / `-b` — line numbering, multi-file concatenation, stdin support
|
|
2111
|
+
- [x] `ping -c N` — respects packet count flag
|
|
2112
|
+
- [x] `ps -u` / `ps aux` — extended format with USER/PID/%CPU/%MEM/VSZ/RSS columns
|
|
2113
|
+
- [x] `$(cmd)` in single-quoted args preserved — `sh -c 'echo $(whoami)'` now works correctly
|
|
2114
|
+
- [x] Pipeline executor: `runCommandDirect` — args with `;`, `|`, `>` no longer re-parsed
|
|
2115
|
+
- [x] `>>` append redirect fixed — was broken because `echo` lacked terminal newline
|
|
2116
|
+
- [x] `export A=1 && echo $A` — env vars visible to subsequent commands in same pipeline
|
|
2117
|
+
- [x] `echo` uses session `env.vars` (not stale global store)
|
|
1704
2118
|
- [ ] Snapshot diff tooling for test assertions
|
|
1705
2119
|
- [ ] WebSocket-based remote shell client (experimental)
|
|
1706
|
-
- [ ]
|
|
2120
|
+
- [ ] Package stubs that simulate REPL behavior (node, python3)
|
|
2121
|
+
- [ ] `/proc/self` and `/proc/<pid>` per-session process entries
|
|
1707
2122
|
|
|
1708
2123
|
---
|
|
1709
2124
|
|