Skip to content

OSConfig Helpers

github-actions[bot] edited this page Nov 12, 2026 · 1 revision

nebulaos-osconfig helper reference

Every OS setting a printer has is read and written here. The Moonraker API reference stops at the endpoint; this page picks up where it stops.

Source: NebulaOS-firmware at b95b156, scripts/build/overlay/usr/lib/nebulaos/python/nebulaos_osconfig/. 18 modules.

The contract

nebulaos-osconfig is a command-line program: argv in, one JSON object out. It is run by the Moonraker component as a subprocess, which is the whole reason the privilege boundary works - the component never writes a system file itself.

nebulaos-osconfig DOMAIN VERB [--option value]...

It prints {"ok": true, ...} merged with the helper's own result, or {"ok": false, "error": "<CODE>", "message": "..."}, and never a traceback - the dispatcher catches every exception and turns it into that contract. Read-only verbs are listed in cli.py's READ_ONLY set and are the only ones that skip the audit log; every change writes one line to the persistent system log.

How to read the tables

Reached by is the DOMAIN VERB that routes to the function, taken from the dispatcher rather than from the function's name. A function with nothing there is not reachable from the command line: it is used by another module, by a boot service, or by a test.

Result keys are the keys that function's own return statements carry, extracted with ast. A blank means the returns could not be read - not that the function returns nothing.

No module here is hardware-verified. Everything is read from source.

bootdisplay.py

nebulaos_osconfig.bootdisplay - userspace boot display: mode and image (GUI/service readiness mission, 2026-10-09). boot/display.json source of truth (schema 1): mode, image boot/display.env MODE= for S01splash (shell) printer_data/config/uploads/boot-logo/ images to pick from (G63 2026-10-16, inside the config root; was printer_data/uploads, before that boot/uploads) /usr/share/nebulaos/boot-logos/ the logos NebulaOS ships (G116, Run 21): listed with the uploads, read-only, never deleted boot/custom.png|bmp|jpg validated copy of the selected image boot/cache/splash.raw framebuffer-native render (derived) boot/cache/splash.meta geometry + source identity of the render Pillow runs only here (a short-lived helper process), never at boot: the boot script blits splash.raw when splash.meta matches the live framebuffer.

Function Reached by Result keys
builtin_image(width, height) - -
builtin_path() - -
custom_path() - -
default_config() - image, mode, schema
delete_image(filename) boot-display delete-image name, result
fb_geometry() - bpp, layout, stride, xres, yres
firmware_short_sha() - -
fresh_install_default() boot-display fresh-default config, reason, result
guppy_rotation() - -
import_usb(file_id) boot-display import-usb existing, name, result
list_images() boot-display list-images custom_active, images, selected, usb, usb_present
load_config() - -
preview(filename) boot-display preview height, path, result, width
render(if_stale, geo) boot-display render bytes, kind, result
reset_image() boot-display reset-image -
resolve_image(filename) - -
resolve_upload(filename) - -
restore_original_logo() boot-display restore-original-logo -
set_image(filename) boot-display set-image -
set_mode(mode) boot-display set-mode applies, config, result
status() boot-display list-images, boot-display preview, boot-display status cache, config, config_error, custom_image_present, formats, max_dimension, max_file_bytes, modes, power_on
to_framebuffer(img, geo) - -
validate_image_file(path) - -
validate_mode(value) - -

camera.py

nebulaos_osconfig.camera - Creality Nebula Camera controls (G121, Run 22, The owner's list 2026-10-28; investigation _scratch/run22/G121-nebulacam-investigation.md). camera status stored settings, defaults, the camera camera set --json '{...}' change some settings (saved, then applied) camera defaults every setting back to the camera's defaults camera reset reboot the camera (XU selector 17) camera version the camera's firmware string (XU selector 15) camera apply [--device DEV] re-apply everything (S50webcam, when the camera appears - nothing persists in it) The camera (USB a108:2231, uvcvideo) keeps nothing over a reboot, power cycle or USB re-enumeration, so the settings live here (camera/camera.json, atomic) and are applied again each time it appears. Apply order (one control per ioctl): auto switches, manual values, the rest, then the IR mode. Exposure is one-way: the camera reads auto_exposure 0 (its own auto mode) but only accepts 1 (manual); the way back is a camera reset, which clears the stored manual flag BEFORE the reboot so the re-apply cannot switch it on. The vendor Extension Unit (GUID 41769ea2-04de-e347-8b2b-f4341aff003b, found in the USB descriptors, not assumed to be unit 6) is used ONLY through a frozen allow-list of (selector, query) pairs. Selector 13 (firmware update mode: the camera turns into a mass-storage device) is not in it and cannot be sent: no command-line or HTTP parameter carries a selector number. Flip stays in Mainsail. Gamma, hue, backlight compensation and focus (fixed lens) are never written.

Function Reached by Result keys
apply(device, ioctl) camera apply device, result
default_config() - brightness, contrast, exposure_manual, exposure_time, ir_mode, power_line, saturation, schema, sharpness, wb_auto, wb_temperature, zoom
defaults(ioctl, sleep, monotonic) camera defaults config, reenumerated, result
find_camera() - -
ioc_readwrite(type_char, nr, size, mips) - -
load_config() - -
reset(ioctl, sleep, monotonic) camera reset config, reenumerated, result
set_settings(changes, ioctl) camera set config, result
status() camera status config, config_error, defaults, device, firmware, ir_modes, power_line_modes, present, ranges
version(ioctl) camera version firmware, result
xu_unit(usb_dir) - -

cleanup.py

nebulaos_osconfig.cleanup - storage overview and "Clean up Creality files" (G66, 2026-10-16; architecture review 2026-10-16 followed). The data drive (/usr/data) is shared: NebulaOS keeps everything in /usr/data/nebulaos plus the print files in /usr/data/printer_data/gcodes; the Creality system (and the community helper script's add-ons for it) keep their data next to it. "Clean up Creality files" removes that data from a FIXED list - never "everything else" - with these guards: - /usr/data/printer_data/gcodes (NebulaOS's print files, the USB stick mount below it) is never touched; of printer_data only the other entries go - nothing that is, or contains, a mount point; symlinks are removed as links, never followed; nothing outside /usr/data - the swap file only when it is not in use - Creality's timelapse videos only when asked for - refused while printing (the Moonraker component checks first) - the print statistics a later import reads (Run 23, 2026-11-02): Creality's lifetime print counter and print history, and the old Moonraker database (printer_data/database) - see STATISTICS_KEEP Everything not on the list stays: macaddr.txt, wpa_supplicant.conf, factory_test_result, .nebulaos-clean-reset*, .keep, nebulaos*, ... Whatever the other slot runs is allowed; when it is Creality the screen says plainly that it will stop working.

Function Reached by Result keys
clean_creality(include_videos, log) system cleanup-creality freed_bytes, kept, removed, result
clean_nebulaos(log) system cleanup-nebulaos freed_bytes, result
nebulaos_plan() system cleanup-nebulaos-plan bytes, items
other_slot_system() - -
plan(include_videos) system cleanup-plan bytes, include_videos, items, other_slot, videos_bytes
storage() system storage at, free_bytes, other_slot, parts, total_bytes

cli.py

nebulaos_osconfig.cli - argv grammar and the JSON/exit-code contract (GUI/service readiness mission, 2026-10-09). nebulaos-osconfig system info nebulaos-osconfig system clean- reset|cancel-clean-reset|ack-clean-reset nebulaos-osconfig system swap-slot|keep-slot nebulaos- osconfig system ack-notice --id ID nebulaos-osconfig system storage | cleanup-plan [--videos] (read-only, walk the drive) nebulaos-osconfig system busy --profile request|point_of_no_return|git|restart|recovery [--holder TOKEN] (G91 busy check, read-only) nebulaos-osconfig system cleanup-creality [--videos] nebulaos-osconfig system cleanup-nebulaos- plan (read-only) | cleanup-nebulaos nebulaos-osconfig stats baseline (print statistics from before NebulaOS, imported once; read-only otherwise) nebulaos-osconfig image list|status|ack nebulaos-osconfig image check --file ID nebulaos-osconfig image install --file ID --sha256 HEX (starts "image worker") nebulaos-osconfig wifi status|scan nebulaos-osconfig wifi connect --stdin {"ssid": ..., "password": ...} nebulaos-osconfig wifi forget --ssid SSID nebulaos- osconfig network status|reset nebulaos-osconfig network set-hostname --hostname NAME nebulaos- osconfig network set-ipv4 --json '{"mode":"static",...}' nebulaos-osconfig boot-display status|reset-image nebulaos-osconfig boot-display set-mode --mode logo nebulaos-osconfig logs status | set-level --level info|warning|debug | write-state | save nebulaos-osconfig logs expire-debug (housekeeping: Debug ends after 24 h) nebulaos-osconfig settings status nebulaos-osconfig settings snapshot --reason new-system|changed|manual [--db api|file|none] [--if-changed] nebulaos-osconfig boot-display restore-original-logo nebulaos-osconfig boot- display list-images | preview [--file NAME] nebulaos-osconfig boot-display import-usb --file usb// | delete-image --file NAME nebulaos-osconfig boot-display set-image --file NAME_IN_UPLOAD_ROOT nebulaos-osconfig boot-display render [--if-stale] nebulaos-osconfig boot- display fresh-default (S04nebulaos-factory-seed, first seed only) nebulaos-osconfig display status|sleep|wake nebulaos-osconfig display set-brightness --level 25|50|75|100 nebulaos- osconfig spoolman status|remove | set --host HOST --port PORT (G125) nebulaos-osconfig camera status|defaults|reset|version | set --json '{...}' | apply [--device /dev/videoN] (G121) nebulaos-osconfig ota status | check [--force] | download | discard (G79: release check over HTTPS; the download becomes the image source ota/, installed with image install) nebulaos-osconfig mcu status|list nebulaos-osconfig mcu flash --file ID --sha256 HEX (ID and HEX from list) nebulaos-osconfig updates readiness (the last check, read- only) nebulaos-osconfig updates check-readiness [--if-changed] stdout: exactly one JSON object, {"ok": true, ...} or {"ok": false, "error": CODE, "message": ...}. exit 0 ok, 1 invalid input (nothing written), 2 operation failed, 3 refused because the printer is busy (G91: CODE is the busy reason, "refused": true; nothing changed), 64 usage.

Function Reached by Result keys
main(argv) - -

common.py

nebulaos_osconfig.common - shared plumbing for the NebulaOS OS-settings helper (GUI/service readiness mission, 2026-10-09). The helper is the narrow layer behind the [nebulaos] Moonraker component: argv in, exactly one JSON object out, validated input, atomic persistent writes, one lock. It never runs a shell and never interpolates input into a command line.

Function Reached by Result keys
atomic_write(path, data, mode) - -
atomic_write_json(path, obj) - -
busy_result(profile, reason, message, klippy) - busy, klippy, message, profile, reason
busy_state(profile, holder, facts) system busy busy, klippy, message, profile, reason
clear_reboot_pending() - -
fsync_dir(path) - -
invalid(code, message) - -
klippy_facts(timeout) - klippy, moonraker, status, update_manager_busy
klippy_pid_alive() - -
klippy_settled_for() - -
klippy_socket_request(method, params, timeout) - -
lock_holder(path) - -
maintenance(name) - -
parse_kv(text) - -
read_json(path) - -
reboot_grace(sleep) - -
reboot_pending() - -
refuse_if_busy(profile, holder) - busy, klippy, message, profile, reason
render_env(pairs) - -
run(argv, timeout, env) - -
schema_readable(raw, schema) - -
set_reboot_pending(what) - -
status_reasons(status) - -
trial_pending() - -

display.py

nebulaos_osconfig.display - display backlight brightness and sleep/wake (GUI/service readiness mission, 2026-10-09). display/display.json source of truth (schema 1): brightness, touch_wake display/display.env scalars for the shell side (/etc/nebulaos-backlight.sh boot restore, and the S98 touch-wake gate) Hardware is driven only through /usr/libexec/nebulaos/nebulaos- backlight over the kernel controller's standard backlight class device (/sys/class/backlight/nebulaos_backlight) - see that library's header.

Function Reached by Result keys
default_config() - brightness, schema, touch_wake
hardware_status() - asleep, available, error, interface, level, selected_level, touch_wake_running
load_config() - -
set_brightness(value) display set-brightness backlight, config, result
sleep() display sleep backlight, result
status() display status backlight, config, config_error, interface, levels
validate_level(value) - -
wake() display wake backlight, result

image.py

nebulaos_osconfig.image - install a system image (NebulaOS or Creality .img) into the slot that is NOT running, from the touchscreen (on-printer image updates, human decisions 2026-10-14): - NebulaOS may occupy both A/B slots; an install always writes the inactive slot (kernel + rootfs) and then switches the OTA marker to it. The running slot is never written. The RTOS partitions are never written. - Sources: USB sticks, config/firmware/os (Mainsail upload) and the image ota download fetched (G79, Run 23; sha256 checked against the release). - Integrity checks only (no signing): the Creality OTA envelope (7z, the chained per-chunk MD5 scheme, declared sizes and MD5s), a MIPS uImage kernel and a squashfs rootfs that fit their partitions, and a read-back of the written partitions. - Older versions may be installed. Creality images are accepted. - One automatic fallback after an install: the new slot boots as a trial; S99nebulaos-image-trial confirms it, or switches back once. - G91 (2026-10-22): refused at the request while the printer is busy (common.busy_state, request profile - a pending trial of the running slot counts), under the one maintenance lock. Two stop points instead of G88's "wait for the print": before the marker write (stop, blank the written slot) and after it, before the reboot (marker back, read back, TRIAL deleted, slot blanked; if the marker cannot be put back, nothing more is touched and the new system starts at the next power-on). The trial verdict waits while a print runs. A slot confirms itself by its first passed trial: system/slot- confirmed-, keyed on the slot's uImage header (name + CRCs) and squashfs superblock, deleted when an install starts writing that slot. Every switch into an unconfirmed slot writes a TRIAL record (system.swap_slot). image list .img files from the sources image check --file ID open the package, report what it holds image install --file ID --sha256 HEX start the install (background job) image status the running/last job, slots, last result image ack the screen showed the last result The format is the one scripts/package/build-img.py writes and Creality's own updater reads: an encrypted 7z holding /ota_v/ with ota_update.in, and per payload .NNNN.<md5 of the previous chunk, or of the whole payload for chunk 0000> plus ota_md5_. listing the chunk MD5s.

Function Reached by Result keys
ack() image ack result
check(file_id) image check build, date, file, has_rtos, kernel, kernel_size, result, rootfs_size, sha256, size, slots, system, target, version
chunk_names(members, payload_dir, name) - -
confirm_slot(slot) - -
count_start() image trial-count arm, attempts, result
describe(path) - build, images, kernel_arch, kernel_name, members, payload_dir, system, version
file_identity(file_id, size, mtime) - -
install(file_id, sha256) image install applies, result
layout_of(members) - -
list_files() image list files, slots, upload_dir, usb
list_members(path) - -
other_slot(slot) - -
parse_update_in(text) - images, version
remove_trial() - -
resolve(file_id) - -
root_identity(root) - build, date, nebulaos_version, version
run_job(file_id, sha256, holder) - -
sha256_file(path) - -
slot_bootable(slot) - -
slot_confirmed(slot) - -
slot_identity(slot, running, may_mount) - -
slot_key(slot) - -
slots_info(may_mount) - -
status() image status job, last, slots, trial
system_kind(kernel_name) - -
target_partitions(running) - -
trial() image trial removed_upload, result
uimage_name(head) - -
unconfirm_slot(slot) - -
write_trial(record) - -

logs.py

nebulaos_osconfig.logs - the general log level, the status file and "Save logs to USB" (G60, 2026-10-16). log level info | warning | debug, one setting for the system log (syslogd -l), the screen and Moonraker (-v at debug). Klipper has no log levels. state file printer_data/logs/nebulaos-state.txt: versions, slots, MCU, boot, storage, memory - written at boot, every 30 minutes and before a log zip. In the logs folder so Mainsail's own "download as zip" carries it too. Never passwords or keys. save logs one zip on the USB stick with every log, the state file and the printer's config files - streamed file by file (little memory), lines that look like secrets blanked out.

Function Reached by Result keys
expire_debug(now) logs expire-debug level, moonraker, result, system_log
get_level() - -
last_reset() - -
redact(line) - -
redact_text(text) - -
save_logs() logs save bytes, file, files, result, safe_to_remove, usb
set_level(level) logs set-level level, moonraker, result, system_log
state_text() - -
status() logs status applies_to, debug_until, level, levels, not_applicable
write_state() logs write-state path, result

mcu.py

nebulaos_osconfig.mcu - MCU firmware from the touchscreen (development run 4, 2026-10-11): "plug in USB, pick a compiled .bin, flash - no Creality bootloader knowledge needed". status what the boot-time MCU guard found, who owns the MCU, last flash list candidate .bin files: Mainsail uploads (config/firmware/mcu), a mounted USB stick (gcodes/USB/, /etc/usb-gcode- media.sh) and the built-in NebulaOS firmware; each one checked offline flash one file, by id and the sha256 the user confirmed Flashing is NOT reimplemented here. The file is checked and prepared (mcu_image.prepare(): a raw NebulaOS-klipper-mcu build is packed into the Creality format), snapshotted to tmpfs so a pulled USB stick or a new upload cannot change it mid- flight, and handed to the existing CLI, nebulaos-mcu flash <file>, which refuses while printing, stops Klipper, runs mcu_flash_file.py (bootloader entry, hardware-id allow-list, transfer, app start) and restarts Klipper whatever happened.

Function Reached by Result keys
flash(file_id, expected_sha) mcu flash file, kind, log, managed, native, result
list_files() mcu list builtin, files, upload_dir, usb
resolve(file_id) - -
status() mcu status guard, last_flash, managed, upload_dir

network.py

nebulaos_osconfig.network - persistent hostname and DHCP/static IPv4 for wlan0 (GUI/service readiness mission, 2026-10-09). Files (all under the NebulaOS namespace on /usr/data): network/osconfig.json source of truth (schema 1) network/osconfig.lkg.json last configuration proven to reach its gateway network/boot.env scalars for /etc/nebulaos- network.sh (boot) A live change is: validate -> keep the current files as *.prev -> write the new files -> apply through nebulaos-netapply (the same shell library the boot uses) -> verify (static: the gateway answers ARP; DHCP: a lease) -> commit, or restore *.prev and re-apply. The caller gets the real outcome.

Function Reached by Result keys
boot_env_text(cfg) - -
default_config() - hostname, ipv4, schema
default_hostname() - -
live_state() - address, dns, gateway, hostname, interface, prefix, ssid, wpa_state
load_config() - -
reset() network reset config, result
set_hostname(value) network set-hostname config, mdns_restart_required, result
set_ipv4(spec) network set-ipv4 config, result
status() network status boot_state, config, config_error, last_known_good, live, mdns_name, mdns_note
validate_config(cfg) - hostname, ipv4, schema
validate_hostname(value) - -
validate_ipv4(spec) - address, dns, gateway, mode, prefix

ota.py

nebulaos_osconfig.ota - NebulaOS system updates over the internet (G79). Design: _scratch/run15/G79-ota-design.md; 2026-10-29 (Run 23): NO signing key - the printer downloads only over HTTPS from github.com and checks the size and sha256 listed in the release record. ota status the last check and the download (read-only) ota check [--force] ask the release channel whether a newer NebulaOS is published (at most once per 24 h unless forced; never while the printer is busy) ota download fetch the offered .img (background job) into the "ota" image source; the install is then the ordinary image install --file ota/<name> (busy refusal, stop points, trial, rollback) ota discard delete the downloaded image Trust: the stable channel is the latest GitHub release of NebulaOS- KE/NebulaOS. Every request is HTTPS with certificate checks (the image's CA bundle), to github.com / api.github.com and, through redirects, only to GitHub's release asset hosts. The release record nebulaos-release.json (tools/release/ make-release-record.py) names the image, its size and sha256; the image is kept only when both match. TLS needs a set clock: before CLOCK_FLOOR nothing is fetched.

Function Reached by Result keys
allowed_url(url) - -
check(force, fetch, now) ota check -
discard() ota discard result
download(now) ota download downloaded, result
downloaded() - -
offer(rec, running) - -
parse_record(data) - -
parse_version(text) - -
run_download(opener) - -
running_version() - -
status() ota status channel, download, downloaded, last, running_version

poweronlogo.py

nebulaos_osconfig.poweronlogo - the power-on logo: the picture the RTOS (zero.bin, in the running slot's rtos/rtos2 partition) draws before Linux starts. G58/G40 (human design, 2026-10-15): - always a logo, NebulaOS's by default; the only choice is an image the user uploaded to the boot logo folder (the same one becomes the Linux boot screen, bootdisplay.py);

  • "Set NebulaOS logo" when the original logo is still in the RTOS; - "Restore Creality logo" - the original, saved on the printer before the first change (or copied from the other slot's RTOS, which still has it); - the running slot's RTOS, A or B: slot A too once it holds NebulaOS (G40). Only the logo slot inside zero.bin is written (rtos_logo.py: 480x272 baseline JPEG, 4:2:0, at most 10899 bytes at 0x5e50c). Before a write the whole image outside that slot must hash to FRAME_SHA256 - the stock image with the slot zeroed, the same for every logo - so an RTOS that is not the known one is never touched. Every write is read back. The Moonraker component refuses all of this while printing.
Function Reached by Result keys
check_writable() - -
classify(image) - -
convert(path) - -
nebulaos_slot_bytes() - -
restore_original() - applies, logo, result, slot
set_custom(path) - applies, logo, quality, result, slot
set_nebulaos() - applies, logo, result, slot
status() - logo, original_available, reason, slot, writable
write_slot(slot_bytes) - applies, logo, result, slot

snapshot.py

nebulaos_osconfig.snapshot - copies of the user's settings (G65, 2026-10-16). config/backups/settings--/ visible in Mainsail printer.cfg moonraker.conf songs.conf (calibration is in printer.cfg) macros/ nebulascreen/nebulascreen.json calibration/ SNAPSHOT.txt what, why, which system, and where the database copy is Moonraker's database (Mainsail's settings - and login data and API keys, so never under config, which Mainsail can download): --db file copied straight from printer_data/database (S04, before Moonraker starts - a consistent copy) into system/settings- db// --db api Moonraker's own POST /server/database/backup, which writes printer_data/backup/database/.db (Moonraker refuses while printing - recorded, not hidden) When: at the first boot of every new or rolled-back system (S04, before any migration touches a setting) and from the housekeeping loop whenever the settings changed since the newest snapshot - so the newest copy always predates the next update (Moonraker updates components without a hook). The newest KEEP are kept, with their database copies.

Function Reached by Result keys
fingerprint() - -
prune(keep) - -
snapshots() - -
status() settings status keep, snapshots
take(reason, db, if_changed, now) settings snapshot database, files, name, newest, pruned, result

spoolman.py

nebulaos_osconfig.spoolman - the Spoolman server Moonraker talks to (G125, Run 22, 2026-10-28: Settings > Connections > Spoolman on the screen, the same layout as the printer entry). spoolman status {configured, server, host, port} spoolman set --host HOST --port PORT [spoolman] server: http://HOST:PORT spoolman remove the [spoolman] section goes The setting lives where a user would put it by hand: the [spoolman] section of the user's moonraker.conf (config root, editable in Mainsail). Only that section's server line is touched - every other line, comment and option (sync_rate, ...) stays as it is; the file is written atomically. Not an [include]: Moonraker refuses to start on an include that matches no file. Moonraker reads it only at start: a change returns restart: "moonraker" and the caller restarts it (machine.services.restart moonraker, like the hostname). Refused while the printer is busy (G91 maintenance lock + request profile).

Function Reached by Result keys
remove() spoolman remove configured, host, port, restart, result, server
set_server(host, port) spoolman set configured, host, port, restart, result, server
status() spoolman status configured, host, port, server

stats.py

nebulaos_osconfig.stats - print statistics from before NebulaOS (Run 23, 2026-11-02: Settings > Printer > Statistics). stats baseline what the printer printed before NebulaOS, imported once NebulaOS counts its own prints with Moonraker's history (on by default; the NebulaOS Moonraker component adds them up). What came before lives on the data drive, next to NebulaOS, and is read here - never written: creality Creality's lifetime print time: /usr/data/creality/userdata/ config/user_data_not_deleted.json, kept by Creality's master- server (stock 1.1.0.12: "total_time" in seconds, the sum of Klipper's print_stats.print_duration per print). The file is base64 of AES-128-CBC, zero-padded; key and IV are two 16-character strings in master-server (stock 1.1.0.12, offsets 0xc85e8 and 0xc862c), found against ke-dev's own counter (Run 23): '{ "total_time": 468971, "creality_num": 1 }'. With it userdata/history/print_history_record.json (plain JSON, the prints Creality still lists): count and filament. moonraker an earlier Moonraker's history (OpenKE, helper script): /usr/data/printer_data/database/moonraker-sql.db, tables job_totals and job_history (Moonraker's own schema). A source is imported once and then kept in /usr/data/nebulaos/system/ stats-baseline.json, so its numbers survive "Clean up Creality files" or a reinstall of Creality's system. A source that is absent or unreadable is looked at again next time. "Clean up Creality files" keeps both sources (cleanup.STATISTICS_KEEP, printer_data/database).

Function Reached by Result keys
baseline() stats baseline sources
decrypt_creality(blob, key_hex, iv_hex) - -
read_creality_counter() - -
read_creality_history() - -
read_old_moonraker() - by_status, filament_mm, job_time, jobs, longest_print, print_time, reason, since, status

system.py

nebulaos_osconfig.system - read-only build/storage/service facts for the GUI (GUI/service readiness mission, 2026-10-09).

Function Reached by Result keys
ack_clean_reset() system ack-clean-reset result
ack_notice(nid) system ack-notice id, result
boot_slot() - -
build_info() - -
cancel_clean_reset() system cancel-clean-reset result
info() system busy, system cleanup-nebulaos-plan, system cleanup-plan, system info, system storage after_clean_reset, build, clean_reset_pending, hostname, kernel, notices, sftp, slot, storage
keep_slot() system keep-slot next_boot, result
notices() - -
request_clean_reset() system clean-reset applies, result
storage_info() - boot_check, error, mount
swap_slot() system swap-slot applies, from, next_boot, result, trial

updates.py

nebulaos_osconfig.updates - G73 (2026-10-17): is a pending app update installable on this printer? Moonraker's update_manager installs Klipper and Moonraker updates with pip install -U -r <requirements> against the NebulaOS package index (ready-made packages only - there is no compiler on the printer), and a Klipper update that changes klippy/chelper also needs a matching c_helper.so (G72). When the index has not caught up yet (it builds daily), the update fails and is rolled back. This check says so BEFORE the update button is pressed. Advisory only: it never blocks an update. readiness the last result (read-only, cached file) check-readiness run the check now and store it [--if-changed] Rules (architecture review 2026-10-17): - the target commit is Moonraker's own remote_hash; no ref guessing - no git fetch and no other write to the app repositories (Moonraker owns fetching) - only git show <remote_hash>:<file> / ls-tree - never started while update_manager is busy (pip in the same venv) or printing; an update started DURING a check overlaps it (pip --dry-run only reads the venv), which is accepted - read-only git: show, ls-tree, cat-file - never fetch - pip runs as Moonraker runs it: the app's own venv, --dry-run, the index from /etc/pip.conf, temporary files on /usr/data (not RAM)

Function Reached by Result keys
check(if_changed) updates check-readiness apps, checked_at, checked_epoch, message, result, targets
readiness() updates readiness -

wifi.py

nebulaos_osconfig.wifi - Wi-Fi networks for wlan0 through wpa_supplicant's control socket (Q3, 2026-10-11). Replaces GuppyScreen's direct socket use: the GUI and Mainsail both go through machine.nebulaos.wifi.*. status association state and the saved networks scan a fresh scan (waits for the results) connect {"ssid", "password"} on stdin - never on a command line. A new network is only saved once it has connected; a wrong password or a timeout removes it again and returns to the previous one forget remove every saved entry for an SSID wpa_supplicant owns /usr/data/nebulaos/network/wpa_supplicant.conf (update_config=1, SAVE_CONFIG). The passphrase is turned into the 64-hex WPA PSK here, so it is never stored in plain text and never quoted into a control command. SSIDs are sent hex-encoded for the same reason.

Function Reached by Result keys
connect(spec) wifi connect address, address_mode, address_note, address_ok, result, ssid
decode_network_ssid(value) - -
decode_ssid_text(text) - -
forget(ssid) wifi forget removed, result, ssid
parse_scan_results(text) - -
psk_for(raw_ssid, password) - -
read_stdin_spec(stream) - -
saved_networks(ctrl) - -
scan() wifi scan complete, networks, status
security(flags) - -
ssid_name(raw) - -
status() wifi status interface, saved, status
validate_ssid(value) - -
wpa_status(ctrl) - address, frequency, id, key_mgmt, ssid, state

Clone this wiki locally