osx — the OSCortex app toolchain
Build and preview Flutter apps for OSCortex,
a Rust microkernel with a Flutter shell. OSCortex apps ship as .osx
bundles. Flutter's own toolchain doesn't target OSCortex, so this package adds
the missing init / build / run steps and hides the internals.
A single osx build produces a universal bundle that is native for both
aarch64 and x86_64 — app developers never pick an architecture, and never
touch the engine, gen_snapshot, or the bundle format by hand.
osx init set up the OSCortex build target for a Flutter app
osx build compile the app into a universal .osx bundle (both arches)
osx run boot OSCortex in an emulator to preview (host arch fast, other emulated)
Install
dart pub global activate oscortex_cli
This puts an osx executable on your path (ensure ~/.pub-cache/bin is in
PATH). To run from a checkout instead:
cd tools/osx-cli
dart pub get
dart run bin/osx.dart --help
Prerequisites
| Tool | Needed for | Notes |
|---|---|---|
| Flutter (3.41.x) | init, build |
Produces the asset bundle + AOT kernel. Must be on PATH. |
| Docker | build |
Runs the per-arch gen_snapshot (Linux ELF) in a container. |
| QEMU | run |
qemu-system-aarch64 and/or qemu-system-x86_64. |
| OSCortex source tree | all | The CLI orchestrates scripts in the repo. See Locating the OSCortex tree. |
osx init checks all of these and tells you what's missing.
Commands
osx init
Run from the root of your Flutter app. It:
- Confirms you're in a Flutter project.
- Locates the OSCortex source tree (records it for later commands).
- Verifies the toolchain (Flutter, Docker, QEMU, host arch + accelerator).
- Fetches the pinned prebuilt engine and
gen_snapshotfor both architectures (via the repo'sengine-port/fetch-engine.sh; the pin lives inengine-port/artifact.config). - Writes
.osx/config.json(display name, engine pin, OSCortex tree, version).
cd my_flutter_app
osx init
osx init --name "My App" # override the launcher display name
osx init --skip-fetch # record config + verify only (no download)
osx build
Compiles the current app into .osx bundles. The pipeline:
host flutter pub get
host flutter build bundle -> build/flutter_assets/
host frontend_server --aot --tfa -> build/app_aot.dill (arch-neutral)
docker gen_snapshot[arm64] (linux/arm64) -> libapp-arm64.so
docker gen_snapshot[x64] (linux/amd64) -> libapp-x64.so
pack per-arch My App-arm64.osx / My App-x64.osx
pack universal My App.osx (both snapshots in one bundle)
osx build # both arches + the universal bundle
osx build --arch arm64 # one arch only (no universal bundle)
osx build --name "My App" # override display name
osx build --output-dir dist # write bundles somewhere else
osx build --no-assets # skip staging build/flutter_assets
Outputs land in build/osx/ by default:
build/osx/
My App.osx ← universal (fat) bundle — ship this
My App-arm64.osx ← aarch64-only bundle
My App-x64.osx ← x86_64-only bundle
flutter_assets/ ← fonts, manifests, shaders (arch-neutral)
The AOT kernel (
app_aot.dill) is architecture-neutral and built once on the host. Onlygen_snapshotis per-arch, and it's a Linux binary, sobuildruns it in a throwawayubuntu:22.04container with the matching--platform. On Apple Silicon thex86_64snapshot is produced under emulation (correct, just slower).
osx run (alias osx preview)
Boots OSCortex in QEMU so you can preview across architectures. The host-native arch runs with hardware acceleration (HVF on macOS, KVM on Linux); the other arch is emulated with TCG.
osx run # boot the host arch, accelerated
osx run --arch x64 # boot x86_64 (emulated on an arm64 host)
osx run --arch arm64 # boot aarch64 (emulated on an x86_64 host)
osx run --image path/to.iso # boot a specific image
osx run --headless # no window; serial on stdio
osx run --serial-log /tmp/s.log # capture guest serial to a file
osx run --release v0.1.2 # pick the release the ISO is fetched from
Image selection, in order:
--image <path>if given.- A local
aarch64.kernel(checked under~/OSCortex-run/, orOSCORTEX_KERNEL) — the fastest, most reliable interactive path on Apple Silicon. - Otherwise the release
.isofor the target arch, downloaded and cached under<oscortex>/.image-cache/.
In the window: click to capture the mouse, Ctrl+Alt+G to release, close the
window (or Ctrl-A X when headless) to quit.
The universal .osx bundle
OSCortex .osx bundles start with a fixed 112-byte header (OSCP magic, name,
version, and a single AOT snapshot length) — see the kernel's app registry. That
v1 layout carries one snapshot.
osx build emits a universal bundle that is a strict, backward-compatible
superset:
offset 0 ┌───────────────────────────────┐
│ v1 header (OSCP, version 1) │ ← unchanged; aot_len covers the
│ aot_len = primary snapshot │ host-arch (primary) snapshot
offset 112 ├───────────────────────────────┤
│ primary AOT snapshot │ ← an unmodified OS boots THIS arch
├───────────────────────────────┤
│ OSXM index │ ← (arch token, offset, length)×N
│ other AOT snapshot(s) │
└───────────────────────────────┘
- An OS that only understands v1 reads the header and the first snapshot — it always boots the primary (host) arch, so a fat bundle never bricks an older build.
- An arch-aware loader reads the
OSXMindex and picks the matching arch at install/run time.
For maximum compatibility today, build also writes plain per-arch v1 bundles
(-arm64.osx / -x64.osx). Their bytes are identical to what the project's
tools/oscortex-pack.py produces. See
Status for the kernel-side selection that's still
pending.
Locating the OSCortex tree
osx orchestrates scripts inside the OSCortex repo. It finds the tree via, in
order:
--oscortex-root <path>OSCORTEX_ROOTenvironment variableoscortexRootrecorded in your app's.osx/config.json(written byinit)- The repo this CLI itself lives in (when run from a checkout)
export OSCORTEX_ROOT=~/src/oscortex
# or
osx init --oscortex-root ~/src/oscortex
Status / known limitations
| Area | State |
|---|---|
osx init (fetch both engines, config, prereqs) |
Works. |
osx build (both arches + universal bundle) |
Works. Per-arch v1 bundles are byte-identical to oscortex-pack.py; the universal bundle is verified v1-readable. |
osx run (HVF/KVM host arch + TCG other arch) |
Works for both arches; auto-fetches the release ISO; prefers a local .kernel on aarch64. |
| Kernel-side arch selection from the fat bundle | Pending. Today's kernel reads only the v1 header (one arch). The universal bundle's OSXM index is the forward path; until the kernel learns to select, install the per-arch bundle that matches the target. |
osx run side-loading a freshly built app on boot |
Not yet. run boots the OS image (shell + core apps). Installing your own .osx into a running guest uses the package pipeline. |
build without Docker |
Not supported — gen_snapshot is a Linux binary. A native macOS/Windows gen_snapshot would remove this dependency. |
License
Dual-licensed: AGPL-3.0-or-later or a commercial license. See the LICENSE
and LICENSE-COMMERCIAL files in the OSCortex repository.