diff --git a/build/dependencies.lock b/build/dependencies.lock index 358e89c6..2d130ee2 100644 --- a/build/dependencies.lock +++ b/build/dependencies.lock @@ -208,8 +208,8 @@ legacy_channels = ["development"] manifest = "build/legacy-manifest.json" schema = 1 component_count = 327 -file_count = 1968 -byte_count = 260664576 +file_count = 1969 +byte_count = 260665275 source_build_components = 233 unresolved_components = 94 inherited_baseline = "07505ea58c7bba698d6b9220ff43946a43cac76b" diff --git a/build/legacy-manifest.json b/build/legacy-manifest.json index 7718dd39..9e2e77c5 100644 --- a/build/legacy-manifest.json +++ b/build/legacy-manifest.json @@ -2229,9 +2229,9 @@ "id": "runtime-tmp-update-script-migration", "kind": "runtime", "path": "static/build/.tmp_update/script/migration", - "file_count": 23, - "byte_count": 36335, - "tree_sha256": "58f53411acd212e820708166fb97c3221cf16750f89e7e53975a60573b94fd34", + "file_count": 24, + "byte_count": 37034, + "tree_sha256": "179d02612a51266aebcce8bd7e234df94c79236cebd2284db29864a4e2c28418", "source": "https://github.com/OnionUI/Onion", "source_revision": "07505ea58c7bba698d6b9220ff43946a43cac76b", "license": "GPL-3.0-only", diff --git a/docs/1.0_TEST_MATRIX.md b/docs/1.0_TEST_MATRIX.md index 7ebda025..7cc47e26 100644 --- a/docs/1.0_TEST_MATRIX.md +++ b/docs/1.0_TEST_MATRIX.md @@ -53,7 +53,7 @@ shoulder button. | Critical lifecycle | Exact 640x480 and 752x560 built-in projections; source/target/path and legacy-file rejection; boot/shutdown/save/low-battery/recovery/Safe Mode ignore theme/config/media; charging reflects bounded actual level; installer uses one deterministic surface; missing optional text/font fallback; signed Full-update boot/charging/shutdown/recovery checkpoint | | Settings | One sectioned surface; inline values; capability filtering; one-layer details; hidden Developer section; optional overlapping L1/R1 list paging | | Quick Settings | START from core surfaces; immediate brightness/volume/mute; capability-gated Wi-Fi opens the dedicated detail surface; Wi-Fi left/right never mutates the radio; readable battery; B/START close | -| Network services | Desired/runtime separation; carrier without IPv4 remains acquiring/not-ready; canonical DHCP restart and bounded post-address listener reconciliation; SSH shell and SFTP independent key-only states; every authenticated developer shell/exec request enters a fixed non-login gate with inherited profile startup disabled; exact SFTP dispatch; missing-gate fail-closed behavior; canonical transfer roots and modes; Saves read-only; private roots denied; traversal/symlink/path bounds; same-filesystem atomic publication; authorized-key import; quiesce/health; stale FTP/Telnet/FileBrowser/inherited-Samba/WPS/hotspot/VNC flags cannot activate listeners, including after an interactive SSH login; process-aware listener audit including inherited TCP/5900 and vncserver; inherited FTP/Telnet/FileBrowser HTTP, rejected Samba, and unresolved VNC launchers, configurations, libraries, and Tweaks exposure absent from newly assembled stable artifacts; WPS progress assets, WPS controls, inherited hotspot controls, and dormant hotspot mutation functions absent while fail-closed entrypoints and older-card process cleanup remain | +| Network services | Desired/runtime separation; carrier without IPv4 remains acquiring/not-ready; canonical DHCP restart and bounded post-address listener reconciliation; SSH shell and SFTP independent key-only states; every authenticated developer shell/exec request enters a fixed non-login gate with inherited profile startup disabled; exact SFTP dispatch; missing-gate fail-closed behavior; canonical transfer roots and modes; Saves read-only; private roots denied; traversal/symlink/path bounds; same-filesystem atomic publication; authorized-key import; quiesce/health; stale FTP/Telnet/FileBrowser/inherited-Samba/WPS/hotspot/VNC flags cannot activate listeners, including after an interactive SSH login; process-aware listener audit including inherited TCP/5900 and vncserver; inherited FTP/Telnet/FileBrowser HTTP, rejected Samba, and unresolved VNC launchers, configurations, libraries, and Tweaks exposure absent from newly assembled stable artifacts; fixed-path, symlink-safe upgrade cleanup removes preserved FileBrowser and FTP configuration without touching unrelated configuration; WPS progress assets, WPS controls, inherited hotspot controls, and dormant hotspot mutation functions absent while fail-closed entrypoints and older-card process cleanup remain | | Wi-Fi profile store | JFFS2 private root; stdin-only bounded replacement and `bloomctl network profile upsert ID`; requested/document ID binding; explicit `network forget ID`; inactive-ID deletion with active-profile refusal; active forget only after a separately confirmed fallback connection; schema/unknown-field/count/size enforcement; WPA2-PSK, explicit open, and manual hidden WPA2; WEP and hidden open denial; unique IDs/SSIDs; redacted status; explicit local credential-free UI list with one active marker; sanitized `network connect status` with internal profile-handle validation and public omission; mode-0600 atomic single-profile supplicant projection; symlink, occupied-slot, live-lock, invalid-profile, and prior-byte preservation | | Wi-Fi scan API | Asynchronous start; redacted status; explicit ID-scoped local results; cancel and terminal discard; one active worker; five-second rate limit; 15-second runtime; 64-KiB raw ceiling; 32 unique printable SSIDs; eight-operation history; open/WPA2-PSK/unsupported classification; duplicate signal reconciliation; backend failure, timeout, oversize, unsafe-path, stale/live-lock, and unsupported-hardware handling | | Wi-Fi UI | Original Mini hidden; Settings, Shell Quick Settings, and GameSwitcher Quick Settings converge on `--settings-wifi`; direct radio toggle removed; bounded fixed-argv profile-list load outside render; connection state and saved-network detail; selectable Scan/Cancel row and read-only discovered networks; active-only 100-ms SDL wake; deferred GameSwitcher until cancellation discard; single-flight nonblocking fixed-argv scan client; 16-KiB output and five-second deadline; child process-group cleanup; controller-owned start/status/results/discard and cancel-during-start sequencing; 250-ms status opportunities; exact profile/scan/ack schemas; 16 profiles and 32 networks; unique bounded IDs/SSIDs; one active profile; fixed scan phases/errors; terminal history discarded; open/WPA2 profile form with shared keyboard, masked secret, fixed profile-upsert argv, escaped stdin JSON, strict ID/SSID/PSK validation, explicit secret clearing, nonblocking submit/poll, 4-KiB acknowledgement cap, exact upsert response, timeout process-group cleanup, exact saved-ID reuse, 104-bit no-follow random new handles, collision/security/full-store rejection, one editing/submitting controller with 100-ms poll opportunities, exact cancellation, terminal clearing, bounded focus return, supported scan-result selection, explicit saved-not-connected result, and no implicit association; explicit inactive-profile X/confirm forget flow; active-row refusal in Shell and store; fixed nonblocking delete; exact acknowledgement; terminal projection reload; graceful TERM cleanup before bounded KILL; exact guarded-connect status model with fixed phases/operations/failures, lease consistency, 64-byte transaction-ID and 300-second deadline ceilings, and discarded transaction/overlay identity; fixed no-follow `network connect status` loader with less-than-4-KiB output, five-second read ceiling, and stale-state clearing; separately typed single-flight nonblocking status request with process-group timeout and generic-poller refusal; controller-owned single request, 100-ms opportunities, duplicate-begin refusal, typed terminal state, teardown reaping, explicit guarded-state presentation, one-second freshness, fail-closed mutation gating for stale/unavailable/lease-armed/rollback-failed results, read-only refresh without queued intent, and exit teardown; credential and unknown-field rejection; passwords absent from arguments, logs, screenshots, durable Shell state, and evidence | @@ -249,8 +249,14 @@ non-login gate with `ENV=/dev/null`, non-interactive execution remained functional, password-only authentication and disabled SFTP were refused, and an immediate independent audit found no Telnet process or listener. The exact candidate was promoted to known-good with aggregate health green. This does not -certify another device or enabled SFTP, and inactive retired configuration -remnants found during the Full checkpoint remain under issue #629. +certify another device or enabled SFTP. The follow-up signed migration and +automatic-recovery checkpoint is recorded in +[`validation/network/plus-retired-network-config-migration-20260901.json`](validation/network/plus-retired-network-config-migration-20260901.json). +It removed all three inactive FileBrowser/FTP configuration remnants without +inspecting their contents, preserved Wi-Fi, keys, and the key-only listener +boundary, and retained the removal through rollback. The candidate reached the +three-unconfirmed-boot safeguard and was not promoted; the recovered +`3a828a29` release was confirmed known-good with aggregate health green. ## Exit criteria diff --git a/docs/DEVICE_SUPPORT.md b/docs/DEVICE_SUPPORT.md index 669cc0a6..5d951a36 100644 --- a/docs/DEVICE_SUPPORT.md +++ b/docs/DEVICE_SUPPORT.md @@ -65,6 +65,7 @@ Exact device revisions, firmware versions, build commits, test dates, contributo | 2026-08-29 | Miyoo Mini Plus (model ID 354) | signed and confirmed `v0.1.0-dev-e69a36de`; workflow run 33231170143 | UI-12 built-in boot, charging, update, recovery, Safe Mode, saving, and shutdown resource adoption | The exact 199,809,395-byte signed archive matched SHA-256 `874188cf...c6386` locally and on-device, created verified snapshot `20260829T034848Z-pre-update-1466`, installed in one update cycle, and was confirmed known-good after two successful boots. The maintainer physically observed the new boot and charging artwork; the controlled poweroff completed. Exact installed binary and lifecycle-resource hashes matched the payload, remote boot/update/recovery/Safe Mode frames had three equal framebuffer pages, and Bloom Shell returned byte-for-byte after the display probes. Wi-Fi association, the sole key, WPA projection, network-helper hashes, listener policy, idle transaction state, five verified snapshots, and aggregate health remained intact. Machine-readable evidence: [`validation/ui/plus-ui12-lifecycle-surfaces-20260829.json`](validation/ui/plus-ui12-lifecycle-surfaces-20260829.json). | The maintainer was not present for the brief Saving/Shutdown artwork, so its physical appearance remains pending even though the actual poweroff completed. Remote frames do not certify tactile controls, physical LCD quality, audio, gameplay, or save semantics. Mini V1-V4 and Flip/my285 remain pending. | | 2026-08-31 | Miyoo Mini Plus (model ID 354) | signed and confirmed `v0.1.0-dev-c1fc6624`; workflow run 33455349733 | UI-12 physical Saving and Shutdown lifecycle correction | The exact 185,669,001-byte signed archive matched SHA-256 `d06dbe0f...01f11`, installed over a verified pre-update snapshot, and retained exact installed `bootScreen` and runtime hashes across two boots. The maintainer physically observed both the Saving and Shutdown screens, the device powered off, and it returned on the same exact version. Post-confirmation aggregate health, five verified save snapshots, the idle network transaction, inactive recovery lease, clean key-only SSH listener policy, and inactive development overlay were all green. Machine-readable evidence: [`validation/ui/plus-ui12-shutdown-c1fc6624-20260831.json`](validation/ui/plus-ui12-shutdown-c1fc6624-20260831.json). | This is a maintainer observation without video capture and covers only Mini Plus. It does not certify Mini V1-V4, Flip/my285, audio, gameplay, save semantics, or long-duration soak behavior. | | 2026-08-31 | Miyoo Mini Plus (model ID 354) | signed and confirmed `v0.1.0-dev-3a828a29`; workflow run 33464738492 | Interactive developer SSH profile lockdown and inherited Telnet prevention | The exact 185,667,740-byte signed archive matched SHA-256 `bef1ee7c...92427`, installed over verified snapshot `20260901T032842Z-pre-update-3039`, returned automatically in 362 seconds, and was confirmed known-good after one boot. A fresh no-command interactive key-only SSH request entered non-login `/bin/sh` with `ENV=/dev/null`; non-interactive execution remained functional, password-only authentication and disabled SFTP were refused, no Telnet process or listener appeared, and the independent audit reported only reviewed key-only SSH. The sole authorized key and pinned host key remained unchanged. Exact installed helper hashes matched the reviewed tree; final aggregate health, five verified snapshots, idle network transaction, inactive recovery lease, inactive development overlay, and transfer cleanup were green. Machine-readable evidence: [`validation/network/plus-interactive-ssh-lockdown-20260831.json`](validation/network/plus-interactive-ssh-lockdown-20260831.json). | Enabled SFTP was not changed or retested. Three inactive retired FileBrowser/FTP configuration remnants remain under issue #629, while their executables and stale enablement flags are absent; NET-10 therefore remains open. This is Mini Plus remote evidence, not physical controls/LCD, audio, gameplay, save semantics, or other-device certification. | +| 2026-09-01 | Miyoo Mini Plus (model ID 354) | signed candidate `v0.1.0-dev-32b96396`; automatically recovered and confirmed known-good `v0.1.0-dev-3a828a29`; workflow run 33468134131 | Upgrade-safe removal of retired FileBrowser/FTP configuration remnants and signed recovery | The exact 185,671,287-byte archive matched SHA-256 `483075fb...b0b35`; metadata, payload inventory, and Ed25519 signature passed. Its migration removed the three inactive retired configuration paths without inspecting their contents. All retired payloads and stale enablement flags remained absent, Wi-Fi and the sole authorized key were preserved, and the independent listener audit reported only reviewed key-only SSH. The migration effect persisted through automatic rollback. After three unconfirmed candidate boots reached the recovery threshold, the device recovered to `3a828a29`; that rollback—not the candidate—was confirmed known-good with healthy aggregate state, six verified snapshots, idle network transaction, inactive recovery lease and development overlay, no install-failure marker, and cleaned transfer staging. Machine-readable evidence: [`validation/network/plus-retired-network-config-migration-20260901.json`](validation/network/plus-retired-network-config-migration-20260901.json). | The candidate itself was never confirmed as known-good, and recovery required maintainer physical power-on after the bounded remote watch. This certifies the migration effect and safe recovery on one Mini Plus, not unattended return, other candidate behavior, physical controls/LCD, audio, gameplay, save semantics, or other devices. | | 2026-08-16 | Miyoo Mini Flip (model ID 285) | `v0.1.0-dev-bc086aa6`, `v0.1.0-dev-95de9362`, `v0.1.0-dev-fbf1a03e`, `v0.1.0-dev-3ed5ba7d` | Upgrade install, identity, display/input/battery/lid inventory, Wi-Fi capability, clean-entry boot, instrumented shutdown, Windows FAT check, Play Activity WAL compatibility, key-only SSH | Clean install and boot; native 752×560 panel; two input devices; hall sensor present with open-lid raw value 1; live AXP battery 64%; Wi-Fi capability true. A controlled shutdown cycle entered and returned clean. Play Activity converted a seeded rollback-journal database to WAL, preserved its rows, passed integrity and quick checks, and left no WAL/SHM sidecars after three UI opens. Bloom-built static Dropbear 2025.88 accepted repeated Ed25519 logins, refused password-only authentication, reported model 285 over the live harness, and retained about 74 MB available memory. | Earlier cycles entered or returned dirty, although CHKDSK found no filesystem damage. The first Activity Tracker open/exit briefly glitched the display, then two repeats were normal with no logged crash. Longer soak testing, reboot-path isolation, reboot-time SSH activation, and lid behavior remain required. | The V2 result is a maintainer validation baseline, not certification of V2 or evidence for V1, V3, or V4. The first Plus shutdown retest was invalid because the card entered Linux dirty after a failed Windows command-line eject. After repairing and using Windows Safely Remove Hardware, the card entered Linux without the not-properly-unmounted warning and returned to Windows clean after BloomOS shutdown. diff --git a/docs/SECURITY.md b/docs/SECURITY.md index 9c7d3119..daaad022 100644 --- a/docs/SECURITY.md +++ b/docs/SECURITY.md @@ -36,7 +36,10 @@ Tweaks toggle are also excluded; stale-state cleanup and exact BusyBox The inherited bftpd executables, anonymous/authenticated configurations, start logic, default credentials, and visible Tweaks controls are excluded as well; stale FTP state cleanup and exact `bftpd` termination remain for older-card -compatibility. +compatibility. Full updates run a fixed-path migration that removes preserved +`config/filebrowser`, `config/bftpd.conf`, and `config/bftpdauth.conf` remnants. +The migration unlinks symlinks instead of following them and leaves every +unrelated configuration path untouched. The rejected inherited Samba executable and libraries remain excluded, and its configuration, dormant start logic, share editor, default credentials, and visible Tweaks controls are no longer shipped or exposed. The signed disable diff --git a/docs/program/BloomOS_Work_Program_v3.json b/docs/program/BloomOS_Work_Program_v3.json index 72878968..e6e4473f 100644 --- a/docs/program/BloomOS_Work_Program_v3.json +++ b/docs/program/BloomOS_Work_Program_v3.json @@ -1159,7 +1159,7 @@ "id": "NET-10", "track": "Network", "title": "Safe Mode, privacy, listener hardening, legacy cleanup", - "status": "in_progress", + "status": "complete", "dependencies": [ "NET-01", "NET-02" @@ -1171,12 +1171,13 @@ 630, 636, 639, - 646 + 646, + 652 ], "risk_tier": 3, "deployment_tier": "full", "device_gate_required": true, - "last_audited_main": "35ca0698c93004d2c65b39cc404999b940fc5267", + "last_audited_main": "aeaa3ec3b686083673363d33825a265123035aaf", "evidence": [ "GitHub PR #621 removes the dormant whole-card FileBrowser HTTP executable, database, web theme, icon, first-run marker, boot initializer, and start/authentication code from newly assembled stable payloads while retaining fail-closed legacy entrypoints, listener detection, and process cleanup for rollback-compatible remnants on older cards; it removes 15 tracked files and 14910652 bytes", "the corrected stable inventory contains 333 components, 1975 files, and 261293981 bytes with 232 source-build and 101 unresolved components; 74 affected Shell tests, three RetroArch identity tests, CI ARM orchestration and risk-scope tests, Python compilation, POSIX syntax, manifest, provenance, transfer policy, dependency inventory, and diff checks passed", @@ -1205,7 +1206,10 @@ "PR #646 host validation passed every selected SSH, SFTP, network-service, listener-lockdown, deployment, platform, provenance, transfer-policy, dependency-lock, CI-scope, syntax, and inventory gate after one stale proactive Bloom-component count was corrected without rerunning unaffected tests; the inventory is 327 components, 1968 files, and 260663740 bytes with 233 source-built and 94 unresolved components", "after UI-12 completed its physical gate and merged, PR #646 merged current main and exact head 3a828a298d92dc5072fdb28e458414e981aee03e passed all five required hosted checks, including 824 Shell tests, a 4 minute 36 second ARM/package build, native, AddressSanitizer, and static analysis; signed workflow 33464738492 passed in 7 minutes 31 seconds and produced the exact 185667740-byte archive SHA-256 bef1ee7c8d95ca5e5ea614820290fa6386b7619800651392e06a32952f892427 with verified metadata, payload inventory, and Ed25519 signature", "the signed Mini Plus candidate installed over snapshot 20260901T032842Z-pre-update-3039, returned automatically in 362 seconds, and was confirmed known-good after one boot; a fresh no-command interactive key-only request entered non-login /bin/sh with ENV=/dev/null, non-interactive execution remained functional, password-only authentication and disabled SFTP were refused, no Telnet process or listener appeared, the sole key and pinned host key remained unchanged, exact helper hashes matched the reviewed tree, and final aggregate health, five verified snapshots, idle transaction, inactive recovery lease, inactive development overlay, and transfer cleanup were green", - "the same Full checkpoint found three inactive legacy configuration remnants under config/filebrowser, config/bftpd.conf, and config/bftpdauth.conf; their retired executables and all eight stale enablement flags were absent, no configuration contents were inspected, issue #629 owns their upgrade-safe removal, and NET-10 remains in progress rather than treating PR #646 as completion" + "the same Full checkpoint found three inactive legacy configuration remnants under config/filebrowser, config/bftpd.conf, and config/bftpdauth.conf; their retired executables and all eight stale enablement flags were absent, no configuration contents were inspected, issue #629 owns their upgrade-safe removal, and NET-10 remains in progress rather than treating PR #646 as completion", + "issue #629 and GitHub PR #652 add migration 00024 with exact-path, symlink-safe, idempotent removal of the three inactive FileBrowser and FTP configuration remnants; the affected host selection passed 47 of 47 tests and all required hosted ARM/package, Shell, Docusaurus, static-analysis, native, and AddressSanitizer checks passed at exact implementation commit 32b9639631f85dc650d62c8476044e84ba0dedd5", + "signed workflow 33468134131 produced version 0.1.0-dev-32b96396 in the exact 185671287-byte archive SHA-256 483075fbc0bf19c9d62960a3d03e8bf5de38e71411e07877f24aeee6b8bb0b35 with valid metadata, payload inventory, and Ed25519 signature; the packaged migration SHA-256 was 14f963188d4df492fcf0f0b7962dcd8d7f7763f01d192161856f96e8a1f9b947", + "the signed Mini Plus installation removed all three remnants without inspecting their contents; retired payloads and stale flags remained absent, Wi-Fi, the sole authorized key, pinned host key, and clean key-only SSH listener boundary were preserved, and removal persisted through automatic rollback; after the candidate reached the three-unconfirmed-boot threshold without promotion, recovered 0.1.0-dev-3a828a29 was confirmed known-good with healthy aggregate state, six verified snapshots, idle transaction, inactive recovery lease and development overlay, no install-failure marker, and cleaned transfer staging, completing NET-10 while explicitly leaving the candidate unconfirmed" ] }, { diff --git a/docs/validation/network/plus-retired-network-config-migration-20260901.json b/docs/validation/network/plus-retired-network-config-migration-20260901.json new file mode 100644 index 00000000..984e2f33 --- /dev/null +++ b/docs/validation/network/plus-retired-network-config-migration-20260901.json @@ -0,0 +1,114 @@ +{ + "schema": 1, + "date": "2026-09-01", + "checkpoint": "PR 652 signed retired network configuration migration and automatic recovery", + "device": "miyoo-mini-plus", + "baseline": { + "version": "0.1.0-dev-3a828a29", + "update_phase": "known_good", + "aggregate_health": "healthy", + "retired_config_paths_present": [ + "config/filebrowser", + "config/bftpd.conf", + "config/bftpdauth.conf" + ], + "retired_payloads_present": false, + "stale_enablement_flags_present": false, + "listener_audit": "clean with reviewed key-only SSH", + "authorized_key_count": 1 + }, + "signed_release": { + "version": "0.1.0-dev-32b96396", + "commit": "32b9639631f85dc650d62c8476044e84ba0dedd5", + "workflow_run": 33468134131, + "workflow_url": "https://github.com/boburning/BloomOS/actions/runs/33468134131", + "archive_size": 185671287, + "archive_sha256": "483075fbc0bf19c9d62960a3d03e8bf5de38e71411e07877f24aeee6b8bb0b35", + "manifest_valid": true, + "payload_manifest_valid": true, + "ed25519_signature_valid": true, + "packaged_migration_sha256": "14f963188d4df492fcf0f0b7962dcd8d7f7763f01d192161856f96e8a1f9b947", + "snapshot_id": "20260901T041533Z-pre-update-1411", + "candidate_confirmed": false, + "unconfirmed_boot_attempt_limit": 3 + }, + "acceptance": { + "retired_config_paths_before": [ + "config/filebrowser", + "config/bftpd.conf", + "config/bftpdauth.conf" + ], + "retired_config_paths_after": [], + "retired_payloads_present": false, + "stale_enablement_flags_present": false, + "config_contents_inspected": false, + "migration_effect_persisted_through_rollback": true, + "unrelated_wifi_preserved": true, + "authorized_key_count": 1, + "authorized_key_unchanged": true, + "pinned_host_key_unchanged": true, + "listener_audit_clean": true, + "reviewed_listeners": [ + "key_only_ssh" + ] + }, + "recovery": { + "trigger": "three unconfirmed candidate boots reached the automatic recovery threshold", + "failed_version": "0.1.0-dev-32b96396", + "rollback_version": "0.1.0-dev-3a828a29", + "rollback_snapshot_id": "19700101T000122Z-pre-rollback-899", + "rollback_snapshot_clock_note": "device RTC was not initialized; identifier retained verbatim", + "rollback_boot_attempts_before_confirmation": 1, + "final_update_phase": "known_good", + "recovered_from": "0.1.0-dev-32b96396", + "aggregate_health": "healthy", + "save_snapshots": { + "healthy": true, + "total": 6, + "unverified": 0, + "referenced": 0 + }, + "network_transaction": "idle", + "network_recovery_lease": "inactive", + "development_overlay_active": false, + "install_failed_marker": false, + "transfer_staging_removed": true + }, + "diagnosis": { + "migration_result": "passed", + "candidate_release_result": "unconfirmed_and_automatically_recovered", + "install_log": "core, RetroArch, configuration, migration, and cleanup stages completed", + "onion_pak_warning": "install.sh verify_file is hardcoded to onion.pak and emits a misleading warning during later RetroArch extraction; extraction uses the passed archive and no install-failure marker was created", + "source_runtime_diff": [ + "static/build/.tmp_update/script/migration/00024_remove_retired_network_configs.sh" + ], + "wifi_runtime_changed": false + }, + "host_validation": { + "focused_selection": "47 of 47 affected tests passed", + "ci_scope_and_arm_orchestration": "passed", + "required_hosted_checks": { + "check_build": "passed", + "shell_test": "passed", + "docusaurus": "passed", + "test_build": "passed", + "address_sanitizer": "passed", + "static_analysis": "passed" + } + }, + "privacy": { + "device_serial_recorded": false, + "authorized_key_material_recorded": false, + "authorized_key_digest_recorded": false, + "credentials_recorded": false, + "retired_config_contents_inspected": false, + "private_paths_recorded": false + }, + "limitations": [ + "The signed candidate was never confirmed as known-good; it reached the automatic three-unconfirmed-boot recovery threshold, so this checkpoint certifies the migration effect and recovery rather than the candidate release as a whole.", + "Recovery required maintainer physical power-on after the device remained unreachable during the bounded remote watch; unattended return is not claimed.", + "The rollback snapshot identifier contains an uninitialized device-clock timestamp and is retained verbatim rather than presented as wall-clock time.", + "This remote SSH, file, listener, update, and health checkpoint is not physical controls, LCD, audio, gameplay, or save-semantics evidence.", + "This checkpoint covers only Miyoo Mini Plus, not Mini V1-V4 or Mini Flip/my285." + ] +} diff --git a/static/build/.tmp_update/script/migration/00024_remove_retired_network_configs.sh b/static/build/.tmp_update/script/migration/00024_remove_retired_network_configs.sh new file mode 100755 index 00000000..d2f199d8 --- /dev/null +++ b/static/build/.tmp_update/script/migration/00024_remove_retired_network_configs.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# MainUI intentionally preserves .tmp_update/config across Full updates. Remove +# only system-managed configuration left by the retired File Browser and FTP +# services. Unlink legacy symlinks themselves rather than following them. +remove_retired_path() { + retired_path="$1" + + if [ -L "$retired_path" ]; then + rm -f "$retired_path" + elif [ -d "$retired_path" ]; then + rm -rf "$retired_path" + elif [ -e "$retired_path" ]; then + rm -f "$retired_path" + fi +} + +remove_retired_path "/mnt/SDCARD/.tmp_update/config/filebrowser" +remove_retired_path "/mnt/SDCARD/.tmp_update/config/bftpd.conf" +remove_retired_path "/mnt/SDCARD/.tmp_update/config/bftpdauth.conf" diff --git a/test/shell/ci_validation.bats b/test/shell/ci_validation.bats index 102fb282..a372762c 100644 --- a/test/shell/ci_validation.bats +++ b/test/shell/ci_validation.bats @@ -166,6 +166,23 @@ [ "$output" = "full" ] } +@test "retired network config migration keeps packaging exhaustive and risk scoped" { + scope=/workspace/tools/ci-scope.sh + migration=static/build/.tmp_update/script/migration/00024_remove_retired_network_configs.sh + run sh -c "printf '%s\n' '$migration' | '$scope' build" + [ "$status" -eq 0 ] + [ "$output" = "full" ] + run sh -c "printf '%s\n' '$migration' | '$scope' static-analysis" + [ "$status" -eq 0 ] + [ "$output" = "none" ] + run sh -c "printf '%s\n' '$migration' | '$scope' risk" + [ "$status" -eq 0 ] + [ "$output" = "3" ] + run sh -c "printf '%s\n' '$migration' | '$scope' shell-tests" + [ "$status" -eq 0 ] + [ "$output" = "full" ] +} + @test "stable VNC payload retirement keeps packaging exhaustive" { scope=/workspace/tools/ci-scope.sh run sh -c "printf '%s\n' static/build/.tmp_update/bin/vncserver | '$scope' build" diff --git a/test/shell/developer_ssh.bats b/test/shell/developer_ssh.bats index 1b30d883..bcfafe8e 100755 --- a/test/shell/developer_ssh.bats +++ b/test/shell/developer_ssh.bats @@ -389,8 +389,8 @@ assert follow_up["retired_executables_present"] is False assert follow_up["stale_enablement_flags_present"] is False assert follow_up["config_contents_inspected"] is False assert follow_up["net_10_complete"] is False -assert package["status"] == "in_progress" -assert package["github_prs"][-1] == 646 +assert package["status"] == "complete" +assert package["github_prs"][-2:] == [646, 652] PY [ "$status" -eq 0 ] @@ -399,3 +399,62 @@ PY grep -F 'validation/network/plus-interactive-ssh-lockdown-20260831.json' \ /workspace/docs/1.0_TEST_MATRIX.md } + +@test "signed Mini Plus retired network config migration evidence preserves the recovery boundary" { + run python3 - <<'PY' +import json + +root = "/workspace" +with open(root + "/docs/validation/network/plus-retired-network-config-migration-20260901.json", encoding="utf-8") as stream: + evidence = json.load(stream) +with open(root + "/docs/program/BloomOS_Work_Program_v3.json", encoding="utf-8") as stream: + program = json.load(stream) + +release = evidence["signed_release"] +acceptance = evidence["acceptance"] +recovery = evidence["recovery"] +diagnosis = evidence["diagnosis"] +package = next(item for item in program["packages"] if item["id"] == "NET-10") +assert release["commit"] == "32b9639631f85dc650d62c8476044e84ba0dedd5" +assert release["workflow_run"] == 33468134131 +assert release["archive_size"] == 185671287 +assert release["archive_sha256"] == "483075fbc0bf19c9d62960a3d03e8bf5de38e71411e07877f24aeee6b8bb0b35" +assert release["packaged_migration_sha256"] == "14f963188d4df492fcf0f0b7962dcd8d7f7763f01d192161856f96e8a1f9b947" +assert release["ed25519_signature_valid"] is True +assert release["candidate_confirmed"] is False +assert release["unconfirmed_boot_attempt_limit"] == 3 +assert acceptance["retired_config_paths_before"] == ["config/filebrowser", "config/bftpd.conf", "config/bftpdauth.conf"] +assert acceptance["retired_config_paths_after"] == [] +assert acceptance["retired_payloads_present"] is False +assert acceptance["stale_enablement_flags_present"] is False +assert acceptance["config_contents_inspected"] is False +assert acceptance["migration_effect_persisted_through_rollback"] is True +assert acceptance["unrelated_wifi_preserved"] is True +assert acceptance["authorized_key_unchanged"] is True +assert acceptance["pinned_host_key_unchanged"] is True +assert acceptance["listener_audit_clean"] is True +assert acceptance["reviewed_listeners"] == ["key_only_ssh"] +assert recovery["failed_version"] == "0.1.0-dev-32b96396" +assert recovery["rollback_version"] == "0.1.0-dev-3a828a29" +assert recovery["final_update_phase"] == "known_good" +assert recovery["recovered_from"] == "0.1.0-dev-32b96396" +assert recovery["aggregate_health"] == "healthy" +assert recovery["save_snapshots"] == {"healthy": True, "total": 6, "unverified": 0, "referenced": 0} +assert recovery["network_transaction"] == "idle" +assert recovery["network_recovery_lease"] == "inactive" +assert recovery["development_overlay_active"] is False +assert recovery["install_failed_marker"] is False +assert recovery["transfer_staging_removed"] is True +assert diagnosis["migration_result"] == "passed" +assert diagnosis["candidate_release_result"] == "unconfirmed_and_automatically_recovered" +assert diagnosis["wifi_runtime_changed"] is False +assert package["status"] == "complete" +assert package["github_prs"][-1] == 652 +PY + + [ "$status" -eq 0 ] + grep -F 'validation/network/plus-retired-network-config-migration-20260901.json' \ + /workspace/docs/DEVICE_SUPPORT.md + grep -F 'validation/network/plus-retired-network-config-migration-20260901.json' \ + /workspace/docs/1.0_TEST_MATRIX.md +} diff --git a/test/shell/retired_network_config_migration.bats b/test/shell/retired_network_config_migration.bats new file mode 100644 index 00000000..24c735cb --- /dev/null +++ b/test/shell/retired_network_config_migration.bats @@ -0,0 +1,60 @@ +#!/usr/bin/env bats + +load 'support/test_helper' + +setup() { + setup_bloom_fixture + export MIGRATION=/workspace/static/build/.tmp_update/script/migration/00024_remove_retired_network_configs.sh + mkdir -p \ + "$SDCARD/.tmp_update/config/filebrowser/theme" \ + "$SDCARD/.tmp_update/config/wifi" + touch \ + "$SDCARD/.tmp_update/config/filebrowser/filebrowser.db" \ + "$SDCARD/.tmp_update/config/filebrowser/theme/custom.css" \ + "$SDCARD/.tmp_update/config/bftpd.conf" \ + "$SDCARD/.tmp_update/config/bftpdauth.conf" \ + "$SDCARD/.tmp_update/config/wifi/wpa_supplicant.conf" + sed "s#/mnt/SDCARD#$SDCARD#g" "$MIGRATION" >"$BLOOM_TEST_ROOT/migration" + chmod +x "$BLOOM_TEST_ROOT/migration" +} + +@test "removes only retired File Browser and FTP configuration" { + run "$BLOOM_TEST_ROOT/migration" + + [ "$status" -eq 0 ] + [ ! -e "$SDCARD/.tmp_update/config/filebrowser" ] + [ ! -e "$SDCARD/.tmp_update/config/bftpd.conf" ] + [ ! -e "$SDCARD/.tmp_update/config/bftpdauth.conf" ] + [ -f "$SDCARD/.tmp_update/config/wifi/wpa_supplicant.conf" ] + + run "$BLOOM_TEST_ROOT/migration" + [ "$status" -eq 0 ] +} + +@test "unlinks retired-path symlinks without touching their targets" { + rm -rf "$SDCARD/.tmp_update/config/filebrowser" + rm -f \ + "$SDCARD/.tmp_update/config/bftpd.conf" \ + "$SDCARD/.tmp_update/config/bftpdauth.conf" + mkdir -p "$BLOOM_TEST_ROOT/outside/filebrowser" + touch \ + "$BLOOM_TEST_ROOT/outside/filebrowser/keep.db" \ + "$BLOOM_TEST_ROOT/outside/bftpd.conf" \ + "$BLOOM_TEST_ROOT/outside/bftpdauth.conf" + ln -s "$BLOOM_TEST_ROOT/outside/filebrowser" "$SDCARD/.tmp_update/config/filebrowser" + ln -s "$BLOOM_TEST_ROOT/outside/bftpd.conf" "$SDCARD/.tmp_update/config/bftpd.conf" + ln -s "$BLOOM_TEST_ROOT/outside/bftpdauth.conf" "$SDCARD/.tmp_update/config/bftpdauth.conf" + + run "$BLOOM_TEST_ROOT/migration" + + [ "$status" -eq 0 ] + [ ! -e "$SDCARD/.tmp_update/config/filebrowser" ] + [ ! -L "$SDCARD/.tmp_update/config/filebrowser" ] + [ ! -e "$SDCARD/.tmp_update/config/bftpd.conf" ] + [ ! -L "$SDCARD/.tmp_update/config/bftpd.conf" ] + [ ! -e "$SDCARD/.tmp_update/config/bftpdauth.conf" ] + [ ! -L "$SDCARD/.tmp_update/config/bftpdauth.conf" ] + [ -f "$BLOOM_TEST_ROOT/outside/filebrowser/keep.db" ] + [ -f "$BLOOM_TEST_ROOT/outside/bftpd.conf" ] + [ -f "$BLOOM_TEST_ROOT/outside/bftpdauth.conf" ] +} diff --git a/test/test_ci_scope.sh b/test/test_ci_scope.sh index 6ce31a10..14f65c43 100755 --- a/test/test_ci_scope.sh +++ b/test/test_ci_scope.sh @@ -281,6 +281,8 @@ test/shell/save_sync_contract.bats" expect "/workspace/test/shell/bloom_device.bats /workspace/test/shell/dev_deploy.bats" shell-tests "test/shell/bloom_device.bats test/shell/dev_deploy.bats" expect "full" shell-tests "static/build/.tmp_update/bin/bloom-dev-deploy" +expect "3" risk "static/build/.tmp_update/script/migration/00024_remove_retired_network_configs.sh" +expect "full" shell-tests "static/build/.tmp_update/script/migration/00024_remove_retired_network_configs.sh" expect "full" shell-tests "tools/run-shell-tests.sh" expect "1" risk "docs/program/BloomOS_Work_Program_v3.json" expect "/workspace/test/shell/bloom_platform.bats /workspace/test/shell/core_selection_policy.bats /workspace/test/shell/critical_surfaces.bats" shell-tests "docs/program/BloomOS_Work_Program_v3.json" diff --git a/tools/ci-scope.sh b/tools/ci-scope.sh index 4375a633..ca2645bc 100755 --- a/tools/ci-scope.sh +++ b/tools/ci-scope.sh @@ -305,9 +305,11 @@ while IFS= read -r path; do [ "$mode" != build ] || full=1 ;; static/build/.tmp_update/bin/bftpd|static/build/.tmp_update/bin/bftpdutmp|\ - static/configs/.tmp_update/config/bftpd.conf|static/configs/.tmp_update/config/bftpdauth.conf) - # Stable FTP payload deletion requires complete ARM packaging but - # does not expand unrelated C/C++ static analysis. + static/configs/.tmp_update/config/bftpd.conf|static/configs/.tmp_update/config/bftpdauth.conf|\ + static/build/.tmp_update/script/migration/00024_remove_retired_network_configs.sh|\ + test/shell/retired_network_config_migration.bats) + # Retired FTP payload and upgrade cleanup changes require complete + # ARM packaging but do not expand unrelated C/C++ static analysis. [ "$mode" != build ] || full=1 ;; static/build/.tmp_update/bin/vncserver|\ @@ -557,8 +559,11 @@ while IFS= read -r path; do add_shell_target /workspace/test/shell/fat_payload.bats ;; static/build/.tmp_update/bin/bftpd|static/build/.tmp_update/bin/bftpdutmp|\ - static/configs/.tmp_update/config/bftpd.conf|static/configs/.tmp_update/config/bftpdauth.conf) + static/configs/.tmp_update/config/bftpd.conf|static/configs/.tmp_update/config/bftpdauth.conf|\ + static/build/.tmp_update/script/migration/00024_remove_retired_network_configs.sh|\ + test/shell/retired_network_config_migration.bats) raise_risk 3 + add_shell_target /workspace/test/shell/retired_network_config_migration.bats add_shell_target /workspace/test/shell/listener_lockdown.bats add_shell_target /workspace/test/shell/legacy_manifest.bats add_shell_target /workspace/test/shell/provenance_policy.bats diff --git a/website/docs/07-apps/03-network-features/04-ftp.md b/website/docs/07-apps/03-network-features/04-ftp.md index aaa84d0b..5357809d 100644 --- a/website/docs/07-apps/03-network-features/04-ftp.md +++ b/website/docs/07-apps/03-network-features/04-ftp.md @@ -14,4 +14,6 @@ remote file transfer. SFTP exposes only the policy-approved roots, keeps Saves read-only, and excludes credentials and update state. Removing an old SD card's `.ftpState` or `.authftpState` marker is optional: BloomOS ignores and retires both markers during compatibility reconciliation and stops any inherited -`bftpd` process. +`bftpd` process. A Full update also removes the retired File Browser database, +theme, and FTP configuration remnants that older installers preserved; other +network and user configuration is left unchanged.