Shared Glove80 and Go60 firmware plus native control tooling built on RMK.
The repository and shared firmware layer are named moergo-rmk; board-specific
crates and artifacts retain their Glove80 and Go60 names.
crates/
├── moergo-rmk/ # shared embedded services and parity contract
├── glove80-rmk/ # Glove80 hardware entry points
├── go60-rmk/ # Go60 hardware entry points
├── moergo-control/ # multi-board native Rynk CLI
└── xtask/ # repository checks and release packaging
dependencies/
└── rmk/ # pinned upstream RMK/Rynk submodule
Each board firmware is a standalone Cargo workspace because it cross-compiles
for the nRF52840. Both depend on moergo-rmk; neither board may include source
from the other. Native packages share the root workspace. Generated release
artifacts go in dist/.
git submodule update --init --recursive
nix developThe development shell provides the pinned Rust toolchain, just, Nordic
bindgen support, and native BLE build dependencies.
Run just inside the development shell to list the supported tasks:
just fmt # format every owned Cargo workspace
just check # validate repository paths and run native checks/tests
just host-test # test the CLI and repository task runner
just board-check # compile both halves of both boards
just parity-check # host checks plus both-board compilation
just firmware # build and package both keyboard halves
just dist # alias of firmware
just go60-firmware # build and package the Go60 images
just firmware-all # build both release bundlesRun the CLI directly with cargo run -p moergo-control -- --help. It discovers
and manages either board through Rynk. See
crates/moergo-control/README.md for its
commands and crates/glove80-rmk/README.md for
firmware details.
Boards may register typed, namespaced device data through Rynk. The Go60 uses
this for its automatic split policy, active wired/BLE transport, and cable
detect state; query it with moergo-control device-data for JSON output.
Shared behavior belongs in crates/moergo-rmk. A board crate should contain
only hardware entry points, pins/drivers, board-specific device data, and
features that physically do not exist on its sibling. Any intentional
capability difference must be documented; an implementation difference is not
an acceptable reason to duplicate a service.
Every change to shared behavior is checked against both boards. Release qualification likewise builds both UF2 bundles, even when the initiating bug was observed on only one model.
just dist requires a clean repository and the exact clean RMK submodule
revision. It writes both ELF and UF2 images, SHA256SUMS, and a provenance
manifest under dist/. Packaging validates each half's UF2 family ID and the
application flash range 0x00026000..0x000dc000.
just go60-firmware applies the same validation to the Go60 build and writes
its independent bundle under dist/go60/. It stages tracked inputs at fixed
build paths and seeds RMK's storage build hash from the source commit, RMK
commit, and platform profile, so identical inputs reproduce identical UF2s.
Release manifests use schema 2 and include canonical configuration and
platform-profile SHA-256 hashes. The platform profile excludes compiled
keymap/behavior defaults and resolves lighting wake layers by name, so a
downstream personal-default build can prove that its hardware, capacities,
event queues, split transport, storage, and lighting topology still match the
stock build. Compare two files with cargo run -p xtask -- verify-config-profile STOCK CONFIGURED. Set MOERGO_DIST_DIR to package a
comparison build without replacing the normal dist/ bundle.
Downstream configuration repositories may set MOERGO_CONFIG_GIT_COMMIT and
MOERGO_CONFIG_GIT_DIRTY to include their source identity in firmware build
labels and release manifests.
A successful build is not hardware qualification. Before release, test both halves together: typing, layer lighting, state mutation/readback, split reconnect, USB/BLE transports, sleep/resume, persistence, and bootloader recovery.