@tikoci/quickchr 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/DESIGN.md ADDED
@@ -0,0 +1,193 @@
1
+ # quickchr Design
2
+
3
+ > Architecture and rationale. For the user-facing reference (every CLI
4
+ > command, every library API, provisioning, channels, networking, errors)
5
+ > see **[MANUAL.md](./MANUAL.md)**.
6
+
7
+ ## Architecture
8
+
9
+ quickchr is a TypeScript/Bun CLI + importable library to manage MikroTik CHR virtual machines via QEMU.
10
+
11
+ ### Layers
12
+
13
+ ```text
14
+ CLI (src/cli/) ← Arg parsing, wizard, formatting
15
+ ↓
16
+ Library API (src/lib/quickchr.ts) ← QuickCHR class, ChrInstance
17
+ ↓
18
+ Modules (src/lib/) ← qemu, images, versions, network, state, ...
19
+ ```
20
+
21
+ - **CLI** — git-style subcommands + interactive wizard. Thin layer over the library.
22
+ - **Library** — `QuickCHR` class with static methods: `start()`, `list()`, `get()`, `doctor()`. Returns `ChrInstance` handles with `stop()`, `remove()`, `rest()`, `monitor()`, etc.
23
+ - **Modules** — Pure functions for QEMU arg building, image download, port allocation, state persistence.
24
+
25
+ ### Key Design Decisions
26
+
27
+ 1. **JSON state, not SQLite** — Portable to Windows without native deps. Each machine gets a `machine.json` file in `~/.local/share/quickchr/machines/<name>/`.
28
+
29
+ 2. **Port block allocation** — 10 ports per instance (base + 0-9). Default starts at 9100. Avoids conflicts by scanning existing machines and probe-binding.
30
+
31
+ 3. **No shell scripts** — QEMU args built entirely in TypeScript. Enables Windows support and testability.
32
+
33
+ 4. **Optional qcow2** — Default boot disk uses raw `.img` (MikroTik provides them). Users can opt into `qcow2` format for boot resize and QEMU snapshot/restore support. Requires `qemu-img` when enabled.
34
+
35
+ 5. **ARM64 VirtIO rule** — Never use `if=virtio` on aarch64 `virt` machine. Always explicit `-device virtio-blk-pci,drive=drive0`.
36
+
37
+ 6. **Class-based API** — `QuickCHR` is a class with static methods for clean namespacing. `ChrInstance` is an interface implemented as a plain object with closures.
38
+
39
+ ## Port Layout
40
+
41
+ | Offset | Service | Guest Port |
42
+ |--------|------------|------------|
43
+ | +0 | HTTP/REST | 80 |
44
+ | +1 | HTTPS | 443 |
45
+ | +2 | SSH | 22 |
46
+ | +3 | API | 8728 |
47
+ | +4 | API-SSL | 8729 |
48
+ | +5 | WinBox | 8291 |
49
+ | +6—9 | Custom | — |
50
+
51
+ ## Storage Layout
52
+
53
+ ```text
54
+ ~/.local/share/quickchr/
55
+ ├── cache/ # Downloaded images
56
+ │ ├── chr-7.22.1.img.zip
57
+ │ ├── chr-7.22.1.img
58
+ │ └── ...
59
+ ├── machines/
60
+ │ └── 7.22.1-arm64-1/
61
+ │ ├── machine.json # Config + state
62
+ │ ├── disk.img # Working copy
63
+ │ ├── efi-vars.fd # UEFI vars (arm64)
64
+ │ ├── monitor.sock # QEMU monitor
65
+ │ ├── serial.sock # Serial console
66
+ │ ├── qga.sock # QGA (x86 only)
67
+ │ ├── qemu.pid # PID file
68
+ │ └── qemu.log # Output log
69
+ └── config.json # Global config
70
+ ```
71
+
72
+ ## Platform Support
73
+
74
+ | Platform | x86 CHR | arm64 CHR | Notes |
75
+ |------------------------|---------|-----------|-------|
76
+ | macOS x86_64 | HVF | TCG | Intel Mac |
77
+ | macOS arm64 (native) | HVF | HVF | Apple Silicon, bun is arm64 |
78
+ | macOS arm64 (Rosetta) | HVF | TCG | bun is x86_64; arm64 HVF skipped |
79
+ | Linux x86_64 | KVM | TCG | KVM requires `/dev/kvm` writable |
80
+ | Linux aarch64 | TCG | KVM | x86 TCG on arm64 Linux |
81
+ | Windows x86_64 | TCG | TCG | HVF/KVM not available |
82
+
83
+ **Acceleration detection** (`detectAccel`):
84
+ - macOS: checks `kern.hv_support` via sysctl; for arm64 guest additionally checks `process.arch === "arm64"` (native bun = Apple Silicon).
85
+ - Linux: checks `/dev/kvm` writability.
86
+ - Falling back to TCG is always safe, just slower (~20s x86 TCG boot on Apple Silicon; ~2 min arm64 TCG on Intel).
87
+
88
+ ## CI System
89
+
90
+ **Workflow**: `.github/workflows/ci.yml`
91
+
92
+ Three jobs — lint and unit-tests run in parallel; integration is gated on both:
93
+
94
+ ```
95
+ lint unit-tests windows-unit-tests
96
+ Biome + tsc --noEmit bun test test/unit/ bun test test/unit/
97
+ --coverage (windows-latest, always)
98
+ ↘ ↙
99
+ integration (matrix)
100
+ linux/x86_64 (always)
101
+ linux/aarch64 (always)
102
+ macos/arm64 (workflow_dispatch: macos=true)
103
+ macos/x86_64 (workflow_dispatch: macos=true)
104
+ ```
105
+
106
+ **Integration matrix**: Each runner boots a CHR matching its native arch. `detectAccel()`
107
+ selects KVM/HVF/TCG automatically — no per-runner overrides needed.
108
+
109
+ **Coverage**: `bun test test/unit/ --coverage` output is parsed and compared against
110
+ thresholds (default 75% functions, 60% lines). Failures emit `::warning::` annotations
111
+ but do NOT block merges (`continue-on-error: true`). Thresholds are overridable via
112
+ dispatch inputs `min-funcs` / `min-lines`.
113
+
114
+ **Artifacts** (details in `.github/instructions/ci.instructions.md`):
115
+ - `coverage-report` — full per-file coverage table (14 days)
116
+ - `integration-logs-{platform}` — bun test output + machine.json + qemu.log (7 days)
117
+
118
+ **Publish workflow** (`.github/workflows/publish.yml`): triggers on `v*` tags;
119
+ runs lint + typecheck + unit tests before npm publish.
120
+
121
+ ## Design Principles
122
+
123
+ ### Scope Boundary — QEMU Expert, Not Orchestrator
124
+
125
+ quickchr manages individual CHR instances. Multi-router topologies, test matrices, and workflow orchestration are **out of scope** for the CLI and library. Provide `examples/` with Makefiles and `bun:test` scripts to inspire, but don't build a framework. Users (and their AI agents) compose quickchr instances into whatever topology they need — we give them reliable building blocks.
126
+
127
+ ### Networking — Discover, Don't Configure
128
+
129
+ For advanced networking (TAP interfaces, bridges), quickchr **discovers and presents options** but does not manage OS-level network configuration. On macOS, vmnet is straightforward (root + a QEMU flag). On Linux, TAP requires editing system files that vary by distro and network manager — that's the user's domain. quickchr will enumerate available interfaces, generate the correct QEMU flags, and link to tikoci docs for setup guides.
130
+
131
+ #### SLiRP hostfwd — Why User-Mode Must Be ether1
132
+
133
+ QEMU SLiRP (`-netdev user`) hostfwd **requires** the guest to have an IP address (default `10.0.2.15`) on the SLiRP-connected interface. Without it, `hostfwd` accepts TCP connections on the host side (creating a half-open state) but the guest never receives data — HTTP requests hang until timeout.
134
+
135
+ RouterOS auto-creates a DHCP client only on ether1. SLiRP includes a DHCP server that assigns `10.0.2.15`. Therefore **SLiRP must be ether1** for zero-config provisioning. This is why `user` is always the first network in multi-NIC configurations.
136
+
137
+ When adding shared/bridged as ether2+, a manual DHCP client is needed:
138
+ ```
139
+ POST /rest/ip/dhcp-client/add
140
+ {"interface":"ether2","use-peer-dns":"yes","add-default-route":"yes","default-route-distance":"2"}
141
+ ```
142
+
143
+ The `default-route-distance=2` ensures the shared route is backup — SLiRP ether1 remains the primary gateway, avoiding ECMP dual-gateway side effects.
144
+
145
+ **TCG hazard:** SLiRP half-open connections (TCP connect succeeds, data never flows) burn the full per-probe HTTP timeout in `waitForBoot`. Under cross-arch TCG where TCP round-trips are slow, this compounds badly. Lab: `test/lab/slirp-hostfwd/`.
146
+
147
+ ### Platform Priority
148
+
149
+ macOS → Linux → Windows. Mac and Linux share most code paths with minor `#ifdef`-style branches. Windows is tracked but lower priority — larger RouterOS admin audience there, but fewer recipes and harder to test. Windows CI runner planned after the existing macOS/Linux matrix is stable.
150
+
151
+ ### CLI Design
152
+
153
+ Tighten before expanding. New subcommands (`logs`, `exec`, `console`) wait for a full command tree review. The CLI should be discoverable without paging `--help` — shell completions help more than a long command list. Use multipass and virsh as reference points for symmetry, not to copy.
154
+
155
+ ### RouterOS Verification
156
+
157
+ Always read back what we write. One extra REST API call after a provisioning action (license, user, package) catches version-specific command drift early. Surface errors with actionable hints rather than silent failures.
158
+
159
+ ### Provisioning Scope
160
+
161
+ quickchr provisions at first boot and (optionally) on restart:
162
+ - **User creation** — create user, set password, optionally disable admin
163
+ - **Package install** — SCP `.npk` files, reboot to activate
164
+ - **License** — `/system/license/renew` for trial
165
+ - **Device-mode** — `/system/device-mode/update mode=rose container=yes ...` for restricted features (containers, traffic-gen, routerboard). Opt-in: not configured unless explicitly requested via CLI `--device-mode` or API `deviceMode` option. CHR ships with `mode=advanced` which is sufficient for most use cases. Device-mode requires a hard QEMU power-cycle to confirm changes — this is the MikroTik-mandated confirmation mechanism (physical button press on real hardware, cold reboot on VM). The wizard defaults to `rose` when the user opts in, since it enables containers. See: https://help.mikrotik.com/docs/spaces/ROS/pages/93749258/Device-mode
166
+ - **Config import** — planned: load `.rsc` or `.backup` at creation time
167
+
168
+ Provisioning via REST API is preferred (simple HTTP calls). Serial console provisioning (prompt detection + buffer tracking, as in chr-armed) is a fallback for locked environments. Key lessons from chr-armed serial work: use `\r` not `\r\n` on PTY; accumulate buffer with offset tracking to prevent re-matching; detect prompts dynamically, don't use fixed delays.
169
+
170
+ ### Exec Transport Design
171
+
172
+ `quickchr exec` supports multiple transports via `--via=auto|ssh|rest|qga`:
173
+ - **auto** (default) — currently REST only; future: try SSH first, fall back to REST `/execute`
174
+ - **rest** (implemented) — POST to `/rest/execute` with `{"script": "<command>"}` (RouterOS 7.1+). No SSH needed. 60-second server-side timeout. Uses `resolveAuth()` for smart credential resolution.
175
+ - **ssh** (planned) — full RouterOS CLI, supports interactive commands, requires `sshpass`
176
+ - **qga** (implemented) — QEMU Guest Agent commands (x86 only today, ARM64 pending MikroTik fix)
177
+
178
+ **Credential resolution** (`src/lib/auth.ts`): Priority order is (1) explicit `--user`/`--password` override, (2) provisioned user from `machine.json` (`state.user`), (3) CHR default `admin:` (empty password). Both `exec()` and `rest()` on ChrInstance use this.
179
+
180
+ Output formatting via `--json` flag. RouterOS trick for structured output: wrap commands in `[:serialize to=json [<routeros-cmd>]]` to get JSON from any CLI command. For REST-to-CLI mapping, see tikoci/restraml `lookup.html`.
181
+
182
+ ### Examples Philosophy
183
+
184
+ Three representations of each scenario, targeting different audiences:
185
+ - **Makefile** — recipe-driven, targets as documentation (tikoci tradition, see tikoci/netinstall). Agents read targets; humans run `make`.
186
+ - **bun:test** — library API, TypeScript. First-class integration tests. The "source of truth."
187
+ - **Python** — subprocess around CLI. The language agents and network engineers both reach for. Demonstrates quickchr is a real tool, not just a library.
188
+
189
+ Building examples early is a form of "anchor testing" for the CLI surface — it finds ergonomic issues before we commit to new commands.
190
+
191
+ ### Document Maintenance
192
+
193
+ DESIGN.md and BACKLOG.md are living documents. At the end of any significant work session, agents should review whether new implementation details, design decisions, or discovered constraints belong in DESIGN.md, and whether completed/new work should update BACKLOG.md. Treat this as a lightweight checklist, not a gate.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 tikoci and contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,334 @@
1
+ # quickchr
2
+
3
+ > **Experimental** — quickchr is under active development. Most testing has been on
4
+ > macOS (Apple Silicon + Intel), with CI on Linux (x86_64 + aarch64). Windows support
5
+ > is early-stage: unit tests pass on `windows-latest` CI, but integration tests
6
+ > (running a real CHR via QEMU for Windows) are not yet automated. Expect rough edges
7
+ > on Windows — bug reports welcome.
8
+
9
+ CLI and library to download, launch, and manage MikroTik CHR virtual machines via QEMU.
10
+
11
+ ## Quick Start
12
+
13
+ ### Prerequisites
14
+
15
+ - [Bun](https://bun.sh) runtime
16
+ - [QEMU](https://www.qemu.org) with `qemu-system-x86_64` and/or `qemu-system-aarch64`
17
+ - `qemu-img` for disk resize and extra-disk features (`--boot-size`, `--add-disk`, `quickchr disk`)
18
+ - UEFI firmware (edk2) for arm64 CHR
19
+
20
+ Install QEMU:
21
+
22
+ ```bash
23
+ # macOS
24
+ brew install qemu
25
+
26
+ # Ubuntu/Debian
27
+ sudo apt install qemu-system-x86 qemu-system-arm qemu-efi-aarch64 qemu-utils
28
+
29
+ # Fedora/RHEL
30
+ sudo dnf install qemu-kvm qemu-system-aarch64 edk2-aarch64 qemu-img
31
+
32
+ # Arch
33
+ sudo pacman -S qemu-full
34
+ ```
35
+
36
+ ```powershell
37
+ # Windows — install QEMU (qemu-img is included)
38
+ winget install SoftwareFreedomConservancy.QEMU
39
+
40
+ # Install Bun (PowerShell — matches bun.sh official install docs)
41
+ powershell -c "irm bun.sh/install.ps1 | iex"
42
+ # Alternative: winget install Oven-sh.Bun
43
+ ```
44
+
45
+ ### Install
46
+
47
+ CLI:
48
+
49
+ ```bash
50
+ bun install -g @tikoci/quickchr
51
+ quickchr doctor
52
+ ```
53
+
54
+ Library:
55
+
56
+ ```bash
57
+ bun add @tikoci/quickchr
58
+ ```
59
+
60
+ ### CLI Usage
61
+
62
+ The easiest way to start is the interactive wizard — it walks through
63
+ every option and starts the CHR for you:
64
+
65
+ ```bash
66
+ quickchr setup
67
+ ```
68
+
69
+ ![quickchr setup wizard walkthrough](./docs/images/wizard-demo.gif)
70
+
71
+ Or run `quickchr` with no arguments on a TTY to get the same wizard
72
+ automatically. See [MANUAL.md §3 setup](./MANUAL.md#setup) for a
73
+ step-by-step breakdown of every wizard prompt.
74
+
75
+ All wizard options are also available as flags for scripting:
76
+
77
+ ```bash
78
+ # Create a machine without starting it
79
+ quickchr add --name my-chr --channel stable --arch arm64
80
+
81
+ # Create with a resized boot disk and extra blank disks
82
+ quickchr add --name lab --boot-size 1G --add-disk 512M --add-disk 2G
83
+
84
+ # Start an existing machine
85
+ quickchr start my-chr
86
+
87
+ # Create and start in one step
88
+ quickchr start --name throwaway --version 7.22.1 --boot-size 2G
89
+
90
+ # List instances (table view)
91
+ quickchr list
92
+
93
+ # Detailed status for one instance
94
+ quickchr status my-chr
95
+
96
+ # Stop an instance
97
+ quickchr stop my-chr
98
+
99
+ # Stop all instances
100
+ quickchr stop --all
101
+
102
+ # Run a RouterOS CLI command on a running instance
103
+ quickchr exec my-chr /system/resource/print
104
+ quickchr exec my-chr ":put [/system/routerboard/get serial-number]"
105
+
106
+ # Attach to serial console of a running instance (exit: Ctrl-A X)
107
+ quickchr console my-chr
108
+
109
+ # Tail QEMU log
110
+ quickchr logs my-chr
111
+ quickchr logs my-chr --follow
112
+
113
+ # Query live machine config (license, device-mode, admin users)
114
+ quickchr get my-chr
115
+ quickchr get my-chr license
116
+ quickchr get my-chr device-mode --json
117
+
118
+ # Manage snapshots (requires qcow2 boot disk)
119
+ quickchr snapshot my-chr list
120
+ quickchr snapshot my-chr save before-upgrade
121
+ quickchr snapshot my-chr load before-upgrade
122
+ quickchr snapshot my-chr delete before-upgrade
123
+
124
+ # Apply or renew a trial license
125
+ quickchr license my-chr
126
+
127
+ # Network discovery and virtual socket management
128
+ quickchr networks
129
+ quickchr networks sockets
130
+
131
+ # Set up shell completions (bash/zsh/fish)
132
+ quickchr completions
133
+
134
+ # Reset disk to fresh image
135
+ quickchr clean my-chr
136
+
137
+ # Inspect disk layout
138
+ quickchr disk lab
139
+
140
+ # Remove instance entirely
141
+ quickchr remove my-chr
142
+
143
+ # Check prerequisites
144
+ quickchr doctor
145
+ ```
146
+
147
+ ### Create / Start Options
148
+
149
+ | Flag | Description | Default |
150
+ |------|-------------|---------|
151
+ | `--version <ver>` | RouterOS version (e.g., 7.22.1) | Latest stable |
152
+ | `--channel <ch>` | stable, long-term, testing, development | stable |
153
+ | `--arch <arch>` | arm64 or x86 | Host native |
154
+ | `--name <name>` | Instance name | Auto-generated |
155
+ | `--cpu <n>` | vCPU count | 1 |
156
+ | `--mem <mb>` | Memory in MB | 512 |
157
+ | `--boot-disk-format <f>` | Boot disk format: qcow2\|raw | qcow2 |
158
+ | `--boot-size <size>` | Resize boot disk (e.g., 512M, 2G). Requires `qemu-img`. | |
159
+ | `--add-disk <size>` | Attach an extra blank qcow2 disk. Repeatable. Requires `qemu-img`. | |
160
+ | `--bg` / `--background` | Run in background (default) | true |
161
+ | `--fg` / `--foreground` | Run in foreground — serial console on stdio | |
162
+ | `--add-package <pkg>` | Extra package to install (repeatable) | |
163
+ | `--install-all-packages` | Install all packages from `all_packages.zip` | |
164
+ | `--add-user <user:pass>` | Create a user after boot | |
165
+ | `--disable-admin` | Disable the default admin account | |
166
+ | `--no-secure-login` | Keep admin with no password (skip managed account creation) | |
167
+ | `--add-network <spec>` | Add a network NIC (repeatable). Specs: `user`, `shared`, `bridged:<iface>`, `socket::<name>`, `tap:<iface>`. Default: single user NIC. | |
168
+ | `--no-network` | Start with no NICs (headless) | |
169
+ | `--no-winbox` | Exclude WinBox port mapping | |
170
+ | `--no-api-ssl` | Exclude API-SSL port mapping | |
171
+ | `--license-level <l>` | Apply trial license: p1, p10, unlimited | |
172
+ | `--license-account <a>` | MikroTik account email | env `MIKROTIK_WEB_ACCOUNT` |
173
+ | `--license-password <p>` | MikroTik account password | env `MIKROTIK_WEB_PASSWORD` |
174
+ | `--device-mode <m>` | Configure device-mode: rose\|advanced\|basic\|home\|auto\|skip | |
175
+ | `--device-mode-enable <f>` | Set one or more device-mode flags to yes | |
176
+ | `--device-mode-disable <f>` | Set one or more device-mode flags to no | |
177
+ | `--port-base <port>` | Starting port number | Auto (9100+) |
178
+ | `--timeout-extra <s>` | Add extra seconds to the auto-computed boot timeout | |
179
+ | `--dry-run` | Print what would run without executing | |
180
+
181
+ ### RouterOS Provisioning Support Policy
182
+
183
+ quickchr separates **boot-only** machine management from **post-boot provisioning**.
184
+
185
+ - **Boot-only / QEMU-local features work on older RouterOS 7.x builds:** image download, boot/start, disk resize, extra disks, network attachment, and port mappings.
186
+ - **Post-boot provisioning is validated/tested on RouterOS 7.20.8+ only:** package install, managed login or custom user creation, disabling admin, CHR license operations, and device-mode changes.
187
+ - This is a **quickchr support policy**, not a RouterOS claim that older versions can never work. We intentionally stop at the first long-term baseline to avoid version-specific provisioning traps that are not covered by tests.
188
+ - If you plan to provision, prefer **`--channel long-term`** or an explicit version **`>= 7.20.8`**.
189
+
190
+ | Feature | Minimum RouterOS | Notes |
191
+ |---------|------------------|-------|
192
+ | Boot / start / stop | Any RouterOS 7.x | Boot-only path; no post-boot RouterOS mutations |
193
+ | Disk resize / extra disks / disk inspection | Any RouterOS 7.x | Requires host `qemu-img`; QEMU-local only |
194
+ | Network attachment / port mappings | Any RouterOS 7.x | QEMU networking setup; not provisioning |
195
+ | Managed login / custom user / disable-admin | 7.20.8+ | Post-boot provisioning |
196
+ | Package install / install-all-packages | 7.20.8+ | Post-boot provisioning |
197
+ | License apply / renew | 7.20.8+ | Post-boot provisioning |
198
+ | Device-mode changes | 7.20.8+ | Post-boot provisioning; intentionally unsupported below the baseline |
199
+
200
+ ### Background vs Foreground Mode
201
+
202
+ By default `quickchr start` runs QEMU in the **background**: QEMU is spawned as a detached process and the command returns once CHR has booted. Use `quickchr list`, `quickchr status`, and `quickchr stop` to manage it.
203
+
204
+ ```bash
205
+ # Background (default) — returns after CHR finishes booting
206
+ quickchr start --channel stable
207
+
208
+ # Foreground — serial console attached to your terminal
209
+ quickchr start --channel stable --fg
210
+ ```
211
+
212
+ In **foreground** mode, your terminal becomes the CHR serial console. Use these key sequences:
213
+
214
+ | Key | Action |
215
+ |-----|--------|
216
+ | `Ctrl-A X` | Exit QEMU and return to shell |
217
+ | `Ctrl-A C` | Toggle QEMU monitor (`quit` to force-stop) |
218
+ | `Ctrl-A H` | List all key shortcuts |
219
+
220
+ > **Note:** Background QEMU processes are true OS-level orphans — `quickchr` does not use shell job control (`&`). After the command returns you can close the terminal and QEMU keeps running. Use `quickchr stop <name>` to shut it down cleanly.
221
+
222
+ ### Disk Support
223
+
224
+ - The CHR boot image starts as raw. Using `--boot-size` converts it to qcow2 and resizes it before first boot.
225
+ - Extra disks from `--add-disk` are always created as blank qcow2 images.
226
+ - `quickchr clean <name>` removes any resized boot disk and extra disks, then recreates them from the saved machine config.
227
+ - `quickchr disk <name>` shows the stored disk layout. If `qemu-img` is installed, it also shows virtual and actual sizes.
228
+ - `quickchr snapshot` manages qcow2 internal snapshots (requires a qcow2 boot disk).
229
+
230
+ ### Multi-NIC Networking
231
+
232
+ By default each CHR gets a single user-mode NIC (SLIRP). Use `--add-network` to add more NICs or replace the default:
233
+
234
+ ```bash
235
+ # Two CHRs on a shared L2 segment via socket_vmnet (macOS) or bridge (Linux)
236
+ quickchr start --name router1 --add-network user --add-network shared
237
+ quickchr start --name router2 --add-network user --add-network shared
238
+
239
+ # Named virtual socket (L2 tunnel between CHRs)
240
+ quickchr networks sockets create lab-switch
241
+ quickchr start --name r1 --add-network socket::lab-switch
242
+ quickchr start --name r2 --add-network socket::lab-switch
243
+
244
+ # Bridge to a physical interface
245
+ quickchr start --name gw --add-network user --add-network bridged:en0
246
+ ```
247
+
248
+ ### Port Layout
249
+
250
+ Each instance gets a block of 10 ports. With the default base of 9100, the first instance maps:
251
+
252
+ | Offset | Service | Default port |
253
+ |--------|---------|-------------|
254
+ | +0 | HTTP / REST / WebFig | 9100 |
255
+ | +1 | HTTPS | 9101 |
256
+ | +2 | SSH | 9102 |
257
+ | +3 | RouterOS API | 9103 |
258
+ | +4 | RouterOS API-SSL | 9104 |
259
+ | +5 | WinBox | 9105 |
260
+ | +6–+9 | Reserved (spare) | 9106–9109 |
261
+
262
+ The second instance gets 9110–9119, and so on.
263
+
264
+ ### Library Usage
265
+
266
+ ```typescript
267
+ import { QuickCHR } from "@tikoci/quickchr";
268
+
269
+ // Start a CHR instance
270
+ const chr = await QuickCHR.start({
271
+ name: "disk-lab",
272
+ channel: "stable",
273
+ arch: "arm64",
274
+ mem: 512,
275
+ bootSize: "1G",
276
+ extraDisks: ["512M", "2G"],
277
+ });
278
+
279
+ // Use REST API
280
+ const info = await chr.rest("/system/resource");
281
+ console.log(info);
282
+
283
+ // Run a RouterOS CLI command
284
+ const result = await chr.exec("/system/resource/print");
285
+ console.log(result);
286
+
287
+ // Stop (or remove)
288
+ await chr.stop();
289
+ await chr.remove();
290
+
291
+ console.log(chr.state.bootDiskFormat); // "qcow2"
292
+ console.log(chr.state.extraDisks); // ["512M", "2G"]
293
+ ```
294
+
295
+ ### Use in Tests
296
+
297
+ ```typescript
298
+ import { describe, test, afterAll, expect } from "bun:test";
299
+ import { QuickCHR } from "@tikoci/quickchr";
300
+
301
+ let chr: Awaited<ReturnType<typeof QuickCHR.start>>;
302
+
303
+ test("boot CHR and check version", async () => {
304
+ chr = await QuickCHR.start({ channel: "stable" });
305
+
306
+ const resource = await chr.rest("/system/resource");
307
+ expect(resource["board-name"]).toBe("CHR");
308
+ }, 120_000);
309
+
310
+ afterAll(async () => {
311
+ if (chr) await chr.remove();
312
+ });
313
+ ```
314
+
315
+ ## Environment Variables
316
+
317
+ | Variable | Description |
318
+ |----------|-------------|
319
+ | `MIKROTIK_WEB_ACCOUNT` | MikroTik.com account email (for license renewal) |
320
+ | `MIKROTIK_WEB_PASSWORD` | MikroTik.com password (for license renewal) |
321
+ | `QUICKCHR_INTEGRATION` | Set to `1` to run integration tests |
322
+
323
+ ## Documentation
324
+
325
+ - **[MANUAL.md](./MANUAL.md)** — complete reference: every CLI command,
326
+ every library API, provisioning, channels, networking, storage,
327
+ errors. Source-checked.
328
+ - **[DESIGN.md](./DESIGN.md)** — architecture, layers, design principles.
329
+ - **[CONTRIBUTING.md](./CONTRIBUTING.md)** — dev setup and `bun run check`.
330
+ - **[CHANGELOG.md](./CHANGELOG.md)** — release history.
331
+
332
+ ## License
333
+
334
+ MIT
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@tikoci/quickchr",
3
+ "version": "0.2.0",
4
+ "description": "CLI and library to download, launch, and manage MikroTik CHR virtual machines via QEMU",
5
+ "module": "src/index.ts",
6
+ "main": "src/index.ts",
7
+ "types": "src/index.ts",
8
+ "bin": {
9
+ "quickchr": "src/cli/index.ts"
10
+ },
11
+ "exports": {
12
+ ".": {
13
+ "import": "./src/index.ts",
14
+ "types": "./src/index.ts"
15
+ }
16
+ },
17
+ "files": [
18
+ "src",
19
+ "README.md",
20
+ "DESIGN.md"
21
+ ],
22
+ "scripts": {
23
+ "check": "bun run lint:biome && bun run lint:typecheck && bun run lint:markdown",
24
+ "lint": "biome check . && tsc --noEmit",
25
+ "lint:biome": "biome check .",
26
+ "lint:typecheck": "tsc --noEmit",
27
+ "lint:markdown": "bunx markdownlint-cli2 '**/*.md'",
28
+ "test": "bun test test/unit/",
29
+ "test:examples": "bun test examples/",
30
+ "test:integration": "QUICKCHR_INTEGRATION=1 bun test test/integration/",
31
+ "test:all": "QUICKCHR_INTEGRATION=1 bun test test/unit/ test/integration/ examples/",
32
+ "dev": "bun run src/cli/index.ts",
33
+ "release": "bun scripts/release.ts",
34
+ "install:deps:ubuntu": "sudo apt-get --yes install qemu-system-x86 qemu-system-arm qemu-efi-aarch64 qemu-utils socat && curl -fsSL https://bun.sh/install | bash"
35
+ },
36
+ "devDependencies": {
37
+ "@biomejs/biome": "^2.4.10",
38
+ "@types/bun": "^1.3.11",
39
+ "markdownlint-cli2": "^0.21.0"
40
+ },
41
+ "dependencies": {
42
+ "@clack/prompts": "^0.11.0",
43
+ "fflate": "^0.8.2",
44
+ "typescript": "^5"
45
+ },
46
+ "private": false,
47
+ "type": "module",
48
+ "license": "MIT",
49
+ "repository": {
50
+ "type": "git",
51
+ "url": "https://github.com/tikoci/quickchr.git"
52
+ },
53
+ "keywords": [
54
+ "mikrotik",
55
+ "routeros",
56
+ "chr",
57
+ "qemu",
58
+ "virtualization",
59
+ "network-testing"
60
+ ]
61
+ }