Quickstart
Three ways in. The app is zero-setup and needs no token, it runs on our cloud.
Or self-host: the action installs cilicon from its own checkout, and uvx runs
it locally, both need a cilicon.yml and your own Modal token (modal token new).
Install the app (recommended, no token)
Install the cilicon GitHub App on a repo, drop a cilicon.yml at its root, and push.
Every PR gets a boot-tested check, built and booted on our cloud, so there's no Modal token and
no workflow to write.
Self-host: as a GitHub Action
Prefer to run it on your own Modal? Add it as a step; it builds + boots the matrix and posts the check on the PR.
# add cilicon as a step in your existing CI
- uses: neural-alloy/cilicon@v1
env:
MODAL_TOKEN_ID: ${{ secrets.MODAL_TOKEN_ID }}
MODAL_TOKEN_SECRET: ${{ secrets.MODAL_TOKEN_SECRET }}
Or run it locally
# nothing to install, uvx fetches and runs it in one shot
$ modal token new # once, writes a local token
$ uvx --from git+https://github.com/neural-alloy/cilicon@v1 cilicon run
# build + boot every target, in parallel
# check your cilicon.yml without running anything
$ uvx --from git+https://github.com/neural-alloy/cilicon@v1 cilicon doctor
fail-on-regression or a flash_max/ram_max budget (see Proof & gates).
cilicon.yml
A target is a toolchain plus a proof it actually ran. Adding a chip is a yaml entry, never a code change, 100+ boards and GPUs ship as presets.
targets:
- id: firmware/stm32
build: make
validate: qemu_system # boot it in QEMU
machine: lm3s6965evb
expect: "BOOT OK" # the proof it booted (from the console)
flash_max: 256K # and fits the silicon
ram_max: 64K
Common fields: build, validate, expect/expect_regex, flash_max/ram_max, board (a one-word preset), and matrix (sweep one spec into many cells).
Validation tiers
The validate field picks how a target is booted:
native/qemu_user: load the binary directly, resolve libs, reachmain(an ELF-load check, not a boot).qemu_system: full-system bare-metal boot in QEMU (MCUs); drive the virtual UART and assert a boot proof.qemu_system_linux_aarch64: full-system Linux boot: a real ARM64 kernel boots your static ELF as init, asserted from the console (Jetson / Pi / i.MX8-class boards).renode: boot on a Renode machine with modeled peripherals/sensors.real_gpu: run on an actual GPU on Modal (CUDA/kernels).custom: any tier cilicon hasn't heard of, viarun:in pure yaml.
ELF_LOAD, FULL_SYSTEM_BOOT, or REAL_HARDWARE) derived from the tier, so "it booted" and "it loaded" are never confused.cilicon doctor validates cilicon.yml and flags weak checks in 50 ms, before a cloud run.Proof & gates
A green cilicon check isn't "it compiled", it's "it booted on the target, and here's the receipt":
- Boot proof: a pass requires the
expectstring in the real console output, so the check means the artifact ran on the (emulated) chip, not that it built. - Size / regression gate:
flash_max/ram_maxfail a build that won't fit the silicon;--fail-on-regressionagainst a baseline fails a flash/RAM/boot-time regression. - Signed boot attestation:
--attestation(with an Ed25519 key) emits a DSSE-signed in-toto statement binding the artifact's sha256 to its boot result and fidelity. Sign where the boot happened; verify anywhere withcilicon verify-attestation. - Register to a fleet: a green run is a boot-proven artifact. On its own it prints
registered to 0 fleets; setneural_alloy.fleetincilicon.ymland the same signed proof is registered to your Neural Alloy fleet, the one the on-unit agent verifies before it deploys.
- uses: neural-alloy/cilicon@v1
with:
baseline: cilicon-baseline.json # fail on a size/boot regression
attestation: boot.dsse.json # signed boot proof, travels with the artifact
