Pulls in the coarse-pacing fix for parked wait-for-operator states (a
machine left in Door or Hold no longer pins ~28% of the core), plus
P10's grblHAL CI/tests and the mlockall-root-only change. Fetch-verified.
Adds pacing_test.py (dry hold/resume + CPU measurement) and records the
diagnosis and bench validation in BRINGUP.
Pulls in the inverted-doors-bit fix in the liveness probe enclosure
guard (the probe was skipping on every spawn with the lid closed and
would have moved the gantry with it open) plus the P10 -Werror warning
cleanup. Source pushed to origin/main; bitbake -c fetch verified.
forgectrl aa99d30, grblHAL-glowforge e74f53f, and the forgefirm-app
sources at python3-gfhardware c9d602a (PV 0.1.3) - the Phase 1-9
remediation work, pushed and pinned.
- Controllers stop at K80, before forgectrl at K90: runlevel 0/6 no
longer tears down the cooling engine, fire gates, and broker while a
controller may still be executing a job.
- The grblhal/gfcloud init scripts are real emergency levers: stop
routes through the supervisor (POST /controller/stop - a bare pkill
was safed and respawned seconds later), start resumes supervision,
status exists, and the pkill fallback matches full executable paths
instead of truncated names or bare substrings.
- slotmigrate: the partition grow gets the same 2048-sector tolerance
as the filesystem branch (an exact compare rewrote the MBR at S02 on
every boot on disks where the grow cannot land on the last sector),
verifies it made progress, and the resize2fs retry is bounded at
three attempts with the counter kept on p3 itself.
- Installer: archive product/platform are verified after the signature,
and a validly signed OLDER release now requires an explicit yes
instead of installing as a silent downgrade. All predictable /tmp
paths in the installer and ffboot are mktemp now.
- release.sh rejects multiple positional versions (the last one used to
win silently) and a release without factory-era verification dies
unless explicitly bypassed; mkfw.sh refuses to pack when the public
key for the post-sign self-check is missing.
- forgefirm-logrotate: size-capped rotation (boot + hourly) for the
/data logs - a full /data breaks settings, update staging, and the
controllers own writes.
- Bench build scripts derive every path from their own location or
FF_SRC_TOP/FF_BUILD_TOP and log to mktemp files.
- Move the passwordless-root debug-tweaks image feature out of the
shared kas config into forgefirm-image-dev.bb, so the release
forgefirm-image built from the same config is not passwordless-root.
release.sh gains a gate that reads the built rootfs /etc/shadow and
fails on an empty root password, plus a config-level guard that
debug-tweaks is not present in the resolved kas dump. (B-1)
- The installer copies ffboot out of the signature-verified new rootfs
it already mounts, instead of fetching and executing it from a mutable
GitHub raw ref. (B-2)
- Record audit remediation Phase 2 (GATE B) status in BRINGUP.md,
including the bench pass still required to close the gate.
The controller now maps the door pair and the interlock loop onto the core's
safety-door signal, with the e-stop bit behind a machine setting. Records what
is gated and what is deliberately not, the answer to the audit's latch-reset
question, and the bench items the change still needs.
README: replace the stalled REST/GUI roadmap with what the firmware does -
GRBL mode over TCP for LightBurn/UGS/cncjs, optional cloud mode, the web
control panel, cameras, A/B install beside the factory firmware - plus the
supported hardware incl. the 8 MP camera limitation and a real roadmap.
kas/README: xvclk is the board's 24 MHz oscillator; the pin/push rule covers
every source repo; camera and motion sections are hardware-validated, with the
OV8856 caveat spelled out; the device-tree item closes with the control_12v
residue named; forgectrl is the machine-services daemon with two supervised
controllers.
LIGHTBURN.md: drop the pre-first-light status line.
BUILD.md: state the NXP firmware licensing and where the EULA lives on the
machine. The image now installs firmware-imx-lic, so the EULA text ships beside
the VPU/EPDC blobs it covers; a bbappend declares that package at parse time,
which is what makes it installable from an image recipe.
Record the finished shared-services state: forgectrl is the one
machine-services daemon behind both controller modes, both controllers
are cooling-engine clients, and the drill and soak coverage that proves
it. Attribute the cooling policy records to the engine's present home.
Add the remaining polish as Next work item 8 - diagnostics as engine
modes, the rail-policy remainder, cloud per-job fan profile
confirmation, /cool/status cosmetics, and button edge detection.
The controller lifecycle belongs to the forgectrl supervisor (it
spawns the selected controller as a direct child); the init script no
longer starts grblHAL and remains only as a manual emergency stop.
BRINGUP item 2: grbl-mode laser software implemented + bench-verified
without fire (record in the gate list); first light pending.
LIGHTBURN.md: arming/button-press operation, S-max 1000, fire gates.
scripts/bench/laser_stream_test.py: host-side stream-dump contract
verification against the null-sink build.
The cloud-action-surface branches are merged: forgectrl builds from main
(same revision), and the forgefirm-app include pins the python3-gfhardware
master head (adds docs/CLOUD.md and the clean-shutdown gfhome).
gfhome.py, ffmachine.py, and gfcloud.py (with the gfcloud init script
and gfhome conf sample) live in the python3-gfhardware repository's
forgefirm-app/ directory. The three recipes fetch that repo through a
shared include (forgefirm-app.inc) carrying a single pinned SRCREV,
replacing the local file:// copies. Package names, dependencies, and
installed paths are unchanged.
ws_connect() returns the running WsClient, so gfhome no longer needs to
build the client by hand to get a clean disconnect; the exit path uses
WsClient.shutdown() to stop and join the socket thread.
Points the forgectrl recipe at the pushed cloud-action-surface commit
(controller_mode=cloud + the compatibility banner) so a test image
carries the cloud-mode UI. Repoint to main once the branch is merged.
Enable the read-only firmware-version probe and point it at
/data/forgefirm/gf-latest.json, where the web-service client records the
latest firmware Glowforge advertises and the version this release was
tested against. forgectrl reads that file for the cloud-mode
compatibility banner.
gfcloud runs the machine under the Glowforge web service (the factory
cloud experience: the app drives homing, framing, printing). Its init
service starts it only when controller_mode = cloud - mutually exclusive
with grblHAL, which already stands down for that mode - so it owns
/dev/glowforge exclusively. Persistent GFUIService loop with a clean
SIGTERM shutdown that safes the hardware; the init script mirrors
grblhal.init. The image installs gfcloud and python3-ffmachine.
Extract the shared-config identity overrides and the forgectrl-routed
ForgectrlMachine from gfhome into an installed module (python3-ffmachine);
gfhome imports it instead of carrying its own copy, so the coming gfcloud
daemon builds the same machine the same way.
Replace the local dispatch table with gfutilities' dispatch_action
(allow_print=False), so the homing runner and GFUIService share one
action surface and cannot drift.
b5c6d9c declares the shared config readers in glowforge_homing.c;
without the declaration the hard-float build read the homing-session
timeout from the wrong register and SIGTERMed every gfcloud homing
runner ~55 ms after spawn.
Bump to the forgectrl revision whose startup pass reloads
regulatory.db (required while the flashed kernel still has built-in
cfg80211 - its boot-time load fails pre-mount and stays failed
without a reload), hints a region only when one is set (unset =
automatic: the AP's 802.11d country, else world; a 00 hint over the
kernel's own world default reports the confusing intersection alias
"country 98"), and pins wlan0 power save off.
BRINGUP.md: bench record for the wireless-regulatory diagnosis and
the live-verified wifi_country / power-save flow; the kernel-batch
note now carries CFG80211/MAC80211=m, DEFAULT_PS off, and the lm75
vs-supply.
Bump to the forgectrl revision that adds the wifi_country setting
(System-tab region picker); the daemon applies it with iw reg set at
startup and on change, so iw joins RDEPENDS. regulatory.db itself
ships via the glowforge-image base (wireless-regdb-static).
Before writing the target slot, the installer now shows what it holds
(factory firmware v<ver>, ForgeFIRM, an unrecognized filesystem, or
unknown/unreadable content). Factory images are archived as before;
anything else requires the operator to type ERASE, since it is
overwritten without a backup. The archive manifest now records the
semantic FIRMWARE_VERSION (ver=), which the update manager displays in
the restore list. Bump forgectrl to the matching GUI change.
/etc/forgefirm/keys ships the ForgeFIRM release-signing public key
and the Glowforge factory keyring (public keys only) - the update
manager verifies release downloads/uploads against the former and
factory archives against the latter. forgectrl SRCREV bumped to the
update-manager commit; runtime deps on ffboot, fwup, the keyring, and
curl made explicit.
The runbook no longer records the bench machine's fuse identity (a
fuse identity cannot be rotated, so a public document must never
carry it); the forgectrl pin follows that repo's history rewrite.
The image's busybox has no blockdev applet, so the grow path silently
skipped. Sector counts now come from /sys/class/block (no external
tool); log lines also go to /dev/kmsg so migration results are visible
in dmesg after boot.
The gf_hostname override is gone (the forgectrl UI no longer offers
it): the hostname is a pure derivation of the serial - base 23 over
the factory consonant alphabet - so a gf_serial override re-derives
MACHINE.HOSTNAME and the fuse derivation stands otherwise. BRINGUP
records the panel rework (units, fuse identity, always-on position)
as offline-verified; board deploy and pin bumps held during the
firmware-upgrade bench testing.
install-forgefirm.sh is now single-stage and never repartitions: run
from factory firmware, it archives every factory slot version plus the
recovery boot partitions to /data/forgefirm/archive (manifest with
md5s), verifies the signed forgefirm.fw against the embedded ForgeFIRM
pubkey (raw 32-byte form for the factory's fwup 0.14.2; dev key until
the production key ceremony), applies it to the INACTIVE slot with the
factory's own fwup, post-verifies the written rootfs, installs
/data/ffboot, and flips the saved env with read-back verification. The
booted factory slot stays installed and bootable; /data is untouched
beyond the archive. Fixed release asset name forgefirm.fw (version in
the fwup metadata and release tag).
slotmigrate (new recipe, rcS before mountall) reclaims the legacy
layout on eMMC-slot boots: deletes p4, grows p3 to the end of the
disk (sfdisk + partx BLKPG - works with a sibling partition as root),
then e2fsck+resize2fs. Every step is keyed off the actual disk state,
so interrupted runs resume and factory-layout disks are a no-op; SD
boots never touch the eMMC.
-l inventories every bootable partition (SD, eMMC slots 1/2, legacy
p4) as machine-parsable key=value lines: firmware type
(forgefirm/factory), version, kernel presence, booted/next markers,
plus the saved-env selection - the shared probe for the installer and
the forgectrl update manager.
Boot switching now writes all four selection variables (mmcdev,
mmchwpart, mmcpart, mmcroot) in one fw_setenv -s transaction and
read-back verifies, falling back from the libubootenv script format
to the classic u-boot-tools format to per-variable writes - the same
script works on factory firmware and ForgeFIRM. mmchwpart was
previously never set and a mixed env could survive a mid-flip power
cut. Switch targets must pass a content probe (rootfs mounts, kernel
present) unless -f. -e picks the newest factory slot by probing,
excluding slots occupied by ForgeFIRM.
The new ffboot recipe installs it as /usr/sbin/ffboot with
/etc/fw_env.config (factory-identical redundant env layout at eMMC
0x80000/0x82000) - previously the image shipped fw_setenv with no
config. Added to forgefirm-image.
The release image now targets the 200 MiB factory eMMC slot: content
plus 40 MiB working space, hard build failure past the slot size. The
raw ext4 is deployed alongside the wic; scripts/mkfw.sh packs it into
a signed .fw with factory-pattern upgrade.a/upgrade.b tasks. fwup
1.16.0 recipe (applies ForgeFIRM and Glowforge-signed archives on
device) is installed in both images. Dev images stay SD-sized with a
256 MiB working margin and no ceiling.
Verified on the 20260808153331 build: release ext4 180.8 MiB; signed
.fw applies byte-exact with fwup 1.16.0 and with the factory's 0.14.2
(raw-format pubkey), and 0.14.2 -V verifies the signature.
Every image writes /etc/forgefirm-version and echoes "ForgeFIRM
<version>" on the serial-console login prompt (/etc/issue, beneath the
OpenGlow banner) and at SSH login (motd). Release images carry
v${FORGEFIRM_RELEASE}; the dev image stamps the build timestamp - the
same DATETIME as the artifact name - tagged (dev) so a bench machine
is never mistaken for a release.
The init script consults controller_mode in /data/forgefirm.conf:
'cloud' (once that mode exists, with its own service reading the same
key) keeps grblHAL down; grbl, unset, or a missing config starts it.
Board-verified both ways: cloud in the conf leaves the controller
stopped with a clear message, grbl starts and serves normally.
forgectrl ad0b441: OpenGlow branding, operational /status dashboard.
grblHAL-glowforge c472a13: position anchor at homing for the status
readers. Runbook updated.