@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 +193 -0
- package/LICENSE +21 -0
- package/README.md +334 -0
- package/package.json +61 -0
- package/src/cli/format.ts +154 -0
- package/src/cli/index.ts +2625 -0
- package/src/cli/wizard.ts +727 -0
- package/src/index.ts +124 -0
- package/src/lib/auth.ts +68 -0
- package/src/lib/channels.ts +222 -0
- package/src/lib/completions.ts +513 -0
- package/src/lib/console.ts +473 -0
- package/src/lib/credentials.ts +99 -0
- package/src/lib/device-mode.ts +283 -0
- package/src/lib/disk.ts +400 -0
- package/src/lib/exec.ts +101 -0
- package/src/lib/images.ts +167 -0
- package/src/lib/license.ts +292 -0
- package/src/lib/log.ts +41 -0
- package/src/lib/network.ts +635 -0
- package/src/lib/packages.ts +225 -0
- package/src/lib/password.ts +21 -0
- package/src/lib/platform.ts +458 -0
- package/src/lib/provision.ts +422 -0
- package/src/lib/qemu.ts +526 -0
- package/src/lib/qga.ts +667 -0
- package/src/lib/quickchr.ts +1871 -0
- package/src/lib/rest.ts +244 -0
- package/src/lib/secrets.ts +133 -0
- package/src/lib/socket-registry.ts +140 -0
- package/src/lib/state.ts +173 -0
- package/src/lib/storage.ts +163 -0
- package/src/lib/types.ts +690 -0
- package/src/lib/versions.ts +181 -0
- package/src/lib/zip.ts +44 -0
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
|
+

|
|
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
|
+
}
|