https://github.com/tikoci/quickchr
`quickchr` - Fastest path to MikroTik RouterOS CHR instance for testing and scripts
https://github.com/tikoci/quickchr
ai automation bun chr claude-ai cli copilot-enabled mikrotik routeros testing-library testing-tools tikoci wizard
Last synced: 6 days ago
JSON representation
`quickchr` - Fastest path to MikroTik RouterOS CHR instance for testing and scripts
- Host: GitHub
- URL: https://github.com/tikoci/quickchr
- Owner: tikoci
- License: mit
- Created: 2026-04-20T20:39:49.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2026-04-20T22:41:15.000Z (about 2 months ago)
- Last Synced: 2026-04-20T23:26:24.175Z (about 2 months ago)
- Topics: ai, automation, bun, chr, claude-ai, cli, copilot-enabled, mikrotik, routeros, testing-library, testing-tools, tikoci, wizard
- Language: TypeScript
- Homepage: https://tikoci.github.io/p/quickchr
- Size: 1.41 MB
- Stars: 1
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Security: SECURITY.md
Awesome Lists containing this project
README
# quickchr
> **Experimental** — quickchr is under active development. Most testing has been on
> macOS (Apple Silicon + Intel), with CI on Linux (x86_64 + aarch64). Windows support
> is early-stage: unit tests pass on `windows-latest` CI, but integration tests
> (running a real CHR via QEMU for Windows) are not yet automated. Expect rough edges
> on Windows — bug reports welcome.
CLI and library to download, launch, and manage MikroTik CHR virtual machines via QEMU.
## Quick Start
### Prerequisites
- [Bun](https://bun.sh) runtime
- [QEMU](https://www.qemu.org) with `qemu-system-x86_64` and/or `qemu-system-aarch64`
- `qemu-img` for disk resize and extra-disk features (`--boot-size`, `--add-disk`, `quickchr disk`)
- UEFI firmware (edk2) for arm64 CHR
Install QEMU:
```bash
# macOS
brew install qemu
# Ubuntu/Debian
sudo apt install qemu-system-x86 qemu-system-arm qemu-efi-aarch64 qemu-utils
# Fedora/RHEL
sudo dnf install qemu-kvm qemu-system-aarch64 edk2-aarch64 qemu-img
# Arch
sudo pacman -S qemu-full
```
```powershell
# Windows — install QEMU (qemu-img is included)
winget install SoftwareFreedomConservancy.QEMU
# Install Bun (PowerShell — matches bun.sh official install docs)
powershell -c "irm bun.sh/install.ps1 | iex"
# Alternative: winget install Oven-sh.Bun
```
### Install
CLI:
```bash
bun install -g @tikoci/quickchr
quickchr doctor
```
Library:
```bash
bun add @tikoci/quickchr
```
### CLI Usage
The easiest way to start is the interactive wizard — it walks through
every option and starts the CHR for you:
```bash
quickchr setup
```

Or run `quickchr` with no arguments on a TTY to get the same wizard
automatically. See [MANUAL.md §3 setup](./MANUAL.md#setup) for a
step-by-step breakdown of every wizard prompt.
All wizard options are also available as flags for scripting:
```bash
# Create a machine without starting it
quickchr add --name my-chr --channel stable --arch arm64
# Create with a resized boot disk and extra blank disks
quickchr add --name lab --boot-size 1G --add-disk 512M --add-disk 2G
# Start an existing machine
quickchr start my-chr
# Create and start in one step
quickchr start --name throwaway --version 7.22.1 --boot-size 2G
# List instances (table view)
quickchr list
# Detailed status for one instance
quickchr status my-chr
# Stable connection descriptor/env for a running instance
quickchr inspect my-chr
quickchr env my-chr
quickchr env my-chr --json
# Stop an instance
quickchr stop my-chr
# Stop all instances
quickchr stop --all
# Run a RouterOS CLI command on a running instance
quickchr exec my-chr /system/resource/print
quickchr exec my-chr ":put [/system/routerboard/get serial-number]"
# Attach to serial console of a running instance (exit: Ctrl-A X)
quickchr console my-chr
# Tail QEMU log
quickchr logs my-chr
quickchr logs my-chr --follow
# Query live machine config (license, device-mode, admin users)
quickchr get my-chr
quickchr get my-chr license
quickchr get my-chr device-mode --json
# Manage snapshots (requires qcow2 boot disk)
quickchr snapshot my-chr list
quickchr snapshot my-chr save before-upgrade
quickchr snapshot my-chr load before-upgrade
quickchr snapshot my-chr delete before-upgrade
# Apply or renew a trial license
quickchr license my-chr
# Network discovery and virtual socket management
quickchr networks
quickchr networks sockets
# Set up shell completions (bash/zsh/fish)
quickchr completions
# Reset disk to fresh image
quickchr clean my-chr
# Inspect disk layout
quickchr disk lab
# Remove instance entirely
quickchr remove my-chr
# Check prerequisites
quickchr doctor
```
### Create / Start Options
| Flag | Description | Default |
|------|-------------|---------|
| `--version ` | RouterOS version (e.g., 7.22.1) | Latest stable |
| `--channel ` | stable, long-term, testing, development | stable |
| `--arch ` | arm64, x86, or auto | Host native |
| `--name ` | Instance name | Auto-generated |
| `--cpu ` | vCPU count | 1 |
| `--mem ` | Memory in MB | 512 |
| `--boot-disk-format ` | Boot disk format: qcow2\|raw | qcow2 |
| `--boot-size ` | Resize boot disk (e.g., 512M, 2G). Requires `qemu-img`. | |
| `--add-disk ` | Attach an extra blank qcow2 disk. Repeatable. Requires `qemu-img`. | |
| `--forward ` | Add or pin a QEMU SLiRP hostfwd mapping. Repeatable. | |
| `--bg` / `--background` | Run in background (default) | true |
| `--fg` / `--foreground` | Run in foreground — serial console on stdio | |
| `--add-package ` | Extra package to install (repeatable) | |
| `--install-all-packages` | Install all packages from `all_packages.zip` | |
| `--add-user ` | Create a user after boot | |
| `--disable-admin` | Disable the default admin account | |
| `--no-secure-login` | Keep admin with no password (skip managed account creation) | |
| `--add-network ` | Add a network NIC (repeatable). Specs: `user`, `shared`, `bridged:`, `socket::`, `tap:`. Default: single user NIC. | |
| `--no-network` | Start with no NICs (headless) | |
| `--no-winbox` | Exclude WinBox port mapping | |
| `--no-api-ssl` | Exclude API-SSL port mapping | |
| `--license-level ` | Apply trial license: p1, p10, unlimited | |
| `--license-account ` | MikroTik account email | env `MIKROTIK_WEB_ACCOUNT` |
| `--license-password ` | MikroTik account password | env `MIKROTIK_WEB_PASSWORD` |
| `--device-mode ` | Configure device-mode: rose\|advanced\|basic\|home\|auto\|skip | |
| `--device-mode-enable ` | Set one or more device-mode flags to yes | |
| `--device-mode-disable ` | Set one or more device-mode flags to no | |
| `--port-base ` | Starting port number | Auto (9100+) |
| `--timeout-extra ` | Add extra seconds to the auto-computed boot timeout | |
| `--dry-run` | Print what would run without executing | |
### RouterOS Provisioning Support Policy
quickchr separates **boot-only** machine management from **post-boot provisioning**.
- **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.
- **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.
- 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.
- If you plan to provision, prefer **`--channel long-term`** or an explicit version **`>= 7.20.8`**.
| Feature | Minimum RouterOS | Notes |
|---------|------------------|-------|
| Boot / start / stop | Any RouterOS 7.x | Boot-only path; no post-boot RouterOS mutations |
| Disk resize / extra disks / disk inspection | Any RouterOS 7.x | Requires host `qemu-img`; QEMU-local only |
| Network attachment / port mappings | Any RouterOS 7.x | QEMU networking setup; not provisioning |
| Managed login / custom user / disable-admin | 7.20.8+ | Post-boot provisioning |
| Package install / install-all-packages | 7.20.8+ | Post-boot provisioning |
| License apply / renew | 7.20.8+ | Post-boot provisioning |
| Device-mode changes | 7.20.8+ | Post-boot provisioning; intentionally unsupported below the baseline |
### Background vs Foreground Mode
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.
```bash
# Background (default) — returns after CHR finishes booting
quickchr start --channel stable
# Foreground — serial console attached to your terminal
quickchr start --channel stable --fg
```
In **foreground** mode, your terminal becomes the CHR serial console. Use these key sequences:
| Key | Action |
|-----|--------|
| `Ctrl-A X` | Exit QEMU and return to shell |
| `Ctrl-A C` | Toggle QEMU monitor (`quit` to force-stop) |
| `Ctrl-A H` | List all key shortcuts |
> **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 ` to shut it down cleanly.
### Disk Support
- The CHR boot image starts as raw. Using `--boot-size` converts it to qcow2 and resizes it before first boot.
- Extra disks from `--add-disk` are always created as blank qcow2 images.
- `quickchr clean ` removes any resized boot disk and extra disks, then recreates them from the saved machine config.
- `quickchr disk ` shows the stored disk layout. If `qemu-img` is installed, it also shows virtual and actual sizes.
- `quickchr snapshot` manages qcow2 internal snapshots (requires a qcow2 boot disk).
### Multi-NIC Networking
By default each CHR gets a single user-mode NIC (SLIRP). Use `--add-network` to add more NICs or replace the default:
```bash
# Two CHRs on a shared L2 segment via socket_vmnet (macOS) or bridge (Linux)
quickchr start --name router1 --add-network user --add-network shared
quickchr start --name router2 --add-network user --add-network shared
# Named virtual socket (L2 tunnel between CHRs)
quickchr networks sockets create lab-switch
quickchr start --name r1 --add-network socket::lab-switch
quickchr start --name r2 --add-network socket::lab-switch
# Bridge to a physical interface
quickchr start --name gw --add-network user --add-network bridged:en0
```
### Port Layout
Each instance gets a block of 10 ports. With the default base of 9100, the first instance maps:
| Offset | Service | Default port |
|--------|---------|-------------|
| +0 | HTTP / REST / WebFig | 9100 |
| +1 | HTTPS | 9101 |
| +2 | SSH | 9102 |
| +3 | RouterOS API | 9103 |
| +4 | RouterOS API-SSL | 9104 |
| +5 | WinBox | 9105 |
| +6–+9 | Reserved (spare) | 9106–9109 |
The second instance gets 9110–9119, and so on.
### Custom Port Forwards and Fixed WinBox
Use `--forward ` on `add` or create-and-start `start` commands to add
extra QEMU SLiRP `hostfwd` mappings. The shorthand uses the built-in service
registry when it knows the guest port:
```bash
quickchr add lab --forward smb # auto host port → guest 445/tcp
quickchr add dude-lab --forward winbox:8291 # host 8291 → guest WinBox 8291/tcp
quickchr add app-lab --forward myapp:9200:7777/udp
```
Reusing a built-in service name such as `winbox` pins/replaces that service's
host port for the machine. Existing machines keep the mapping stored in
`machine.json`; recreate the machine to change fixed service ports cleanly.
### Machine Descriptors and Subprocess Environments
`quickchr inspect [--json]` emits a stable JSON descriptor for a
**running** machine: status, ports, URLs, auth, env vars, and the machine
directory. `--json` is accepted for parity; inspect output is always JSON.
`quickchr env [--json]` prints the same subprocess environment as
`ChrInstance.subprocessEnv()`: shell `KEY=value` lines by default, or a JSON
map with `--json`.
> **Credential caveat:** descriptor/env output includes connection secrets
> (`auth.password`, `auth.basic`, `auth.header`, `QUICKCHR_AUTH`, `BASICAUTH`)
> so child processes can connect without reading quickchr's secret store. Treat
> it like a password: do not commit it, paste it into public issues, or leave it
> in CI logs. Stopped machines fail with `MACHINE_STOPPED`; start the machine
> before requesting a descriptor or env map.
### Library Usage
```typescript
import { QuickCHR } from "@tikoci/quickchr";
// Start a CHR instance
const chr = await QuickCHR.start({
name: "disk-lab",
channel: "stable",
arch: "arm64",
mem: 512,
bootSize: "1G",
extraDisks: ["512M", "2G"],
});
// Use REST API
const info = await chr.rest("/system/resource");
console.log(info);
// Run a RouterOS CLI command
const result = await chr.exec("/system/resource/print");
console.log(result);
// Stop (or remove)
await chr.stop();
await chr.remove();
console.log(chr.state.bootDiskFormat); // "qcow2"
console.log(chr.state.extraDisks); // ["512M", "2G"]
```
### Use in Tests
```typescript
import { describe, test, afterAll, expect } from "bun:test";
import { QuickCHR } from "@tikoci/quickchr";
let chr: Awaited>;
test("boot CHR and check version", async () => {
chr = await QuickCHR.start({ channel: "stable" });
const resource = await chr.rest("/system/resource");
expect(resource["board-name"]).toBe("CHR");
}, 120_000);
afterAll(async () => {
if (chr) await chr.remove();
});
```
## Environment Variables
| Variable | Description |
|----------|-------------|
| `MIKROTIK_WEB_ACCOUNT` | MikroTik.com account email (for license renewal) |
| `MIKROTIK_WEB_PASSWORD` | MikroTik.com password (for license renewal) |
| `QUICKCHR_INTEGRATION` | Set to `1` to run integration tests |
## Documentation
- **[MANUAL.md](./MANUAL.md)** — complete reference: every CLI command,
every library API, provisioning, channels, networking, storage,
errors. Source-checked.
- **[DESIGN.md](./DESIGN.md)** — architecture, layers, design principles.
- **[CONTRIBUTING.md](./CONTRIBUTING.md)** — dev setup and `bun run check`.
- **[CHANGELOG.md](./CHANGELOG.md)** — release history.
## License
MIT