Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion build/dependencies.lock
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ manifest = "build/legacy-manifest.json"
schema = 1
component_count = 332
file_count = 2131
byte_count = 314676226
byte_count = 314678142
source_build_components = 228
unresolved_components = 104
inherited_baseline = "07505ea58c7bba698d6b9220ff43946a43cac76b"
Expand Down
4 changes: 2 additions & 2 deletions build/legacy-manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -1021,8 +1021,8 @@
"kind": "runtime",
"path": "static/build/.tmp_update/bin/bloomctl",
"file_count": 1,
"byte_count": 35372,
"tree_sha256": "653222365d8f06ca4bae9a750525e04649991943afab1701eb6a0f6919a6578d",
"byte_count": 37288,
"tree_sha256": "3a652088eb1b1384f5f22a60b1ecb44c1fca8dafe1c1fa6803c94a131a4e5748",
"source": "https://github.com/boburning/BloomOS",
"source_revision": "release-commit",
"license": "GPL-3.0-only",
Expand Down
2 changes: 1 addition & 1 deletion docs/1.0_TEST_MATRIX.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ shoulder button.
| 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; 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; 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 flags cannot activate listeners; process-aware listener audit; Samba hidden until a materially different source-build candidate passes feasibility |
| Wi-Fi profile store | JFFS2 private root; stdin-only bounded replacement and single-profile upsert; requested/document ID binding; inactive-ID deletion with active-profile refusal; schema/unknown-field/count/size enforcement; WPA2-PSK, explicit open, and manual hidden WPA2; WEP and hidden open denial; unique IDs/SSIDs; redacted status; mode-0600 atomic single-profile supplicant projection; symlink, occupied-slot, live-lock, invalid-profile, and prior-byte preservation |
| 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; 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 |
| GameSwitcher | MENU open/toggle; Left/Right select; L1/R1 page; A resume; X actions; START Quick Settings; B Home; no inherited brightness/view/delete behavior |
| Apps/Tweaks | Every app disposition recorded; normal configuration requires no Tweaks; supervised launch remains bounded |
Expand Down
9 changes: 8 additions & 1 deletion docs/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,8 @@ the requested handle and republishes the complete validated store without
returning profile data. Delete refuses the currently projected ID, identified
by a non-secret managed-config marker, so forgetting a profile cannot silently
strand or mutate the active station. Active-profile replacement and removal
remain the responsibility of the later rollback-bounded association slice.
use the rollback-bounded association slice described below: independently
confirm a different profile first, then delete the now-inactive old ID.
The service never scans, associates, changes the Wi-Fi preference, or restarts
the radio; those behaviors remain with later NET-04 API work and the existing
`bloom-network`/`bloom-wifi` owners.
Expand Down Expand Up @@ -322,6 +323,12 @@ recovery verify the private snapshot, atomically restore it, restart the station
lifecycle, and recheck services and listeners. Confirmation still requires a
fresh host-key-verified SSH connection; neither status nor evidence exposes a
profile, SSID, credential, address, nonce, or snapshot content.
`bloomctl network profile upsert ID` preserves the stdin-only secret boundary,
`profile status` remains redacted, and `network forget ID` delegates only the
explicit non-secret ID to the same private store. Forget therefore fails closed
for the projected profile. The supported active-forget sequence is a confirmed
`network connect` transaction to a fallback ID followed by forgetting the old
ID; there is no delete-first or implicit-fallback operation.

Listener reconciliation is event-driven at existing fixed boundaries: boot,
Wi-Fi reconciliation, Wi-Fi preference changes, remote-key publication, and
Expand Down
23 changes: 20 additions & 3 deletions docs/DEVELOPMENT.md
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,8 @@ associates to a network. Run the focused gate with the pinned shell-test image:
Do not copy a live `/appconfigs/wpa_supplicant.conf` into the repository,
fixture output, shell history, logs, screenshots, or evidence. Device preflight
may inspect only filesystem type, mode, byte count, structural counts, and
redacted health. The remaining NET-04 scan/connect orchestration and active
profile removal must retain stdin or an equivalent fixed private input
boundary and the existing rollback rules.
redacted health. NET-04 orchestration retains stdin or an equivalent fixed
private input boundary and the existing rollback rules.

The NET-04 scan slice is asynchronous and keeps result data only under `/tmp`:

Expand Down Expand Up @@ -333,6 +332,24 @@ invoke `network-rollback` from an independently safe path. Never exercise this
transition over the only unattended device route until its exact candidate has
passed the host fault matrix and a local synthetic device fixture.

The bounded device-facing aliases are:

```sh
bloomctl network profile status
bloomctl network profile upsert PROFILE_ID < private-profile.json
bloomctl network connect begin 120
bloomctl network connect apply NONCE PROFILE_ID
bloomctl network connect confirm NONCE
bloomctl network connect cancel
bloomctl network forget INACTIVE_PROFILE_ID
```

To forget the active profile, first apply and independently confirm a different
stored fallback profile. Only then call `network forget` for the old ID. The
private authority rejects deleting the currently projected ID, so an
interrupted or failed cutover retains both the exact rollback configuration and
the old stored profile. Do not add a delete-first shortcut.

An activated update is not considered successful merely because the installer
or MainUI started. The runtime records each unconfirmed boot in durable
`.bloom/update` state. After device checks pass, developers can promote the
Expand Down
9 changes: 6 additions & 3 deletions docs/program/BloomOS_Work_Program_v3.json
Original file line number Diff line number Diff line change
Expand Up @@ -834,20 +834,23 @@
"github_prs": [
487,
488,
489
489,
490
],
"risk_tier": 2,
"deployment_tier": "warm",
"device_gate_required": true,
"last_audited_main": "5f155d6d16fd927d775f18219353c76ae641d750",
"last_audited_main": "8ffc258c61748deea3c773a69b4ddc9fe0402d52",
"evidence": [
"issue #486 tracks NET-04 after NET-03 merged as fca10bb0168d8bc38737881745b68cd3bab9da59; no prior NET-04 issue or implementation PR exists and current main has no bounded scan/connect/forget orchestration",
"draft PR #487 is the first host-safe NET-04 slice and extends the private NET-03 authority with stdin-only atomic single-profile upsert, requested/document ID binding, inactive-profile deletion, a non-secret projected-profile marker, and fail-closed refusal to replace or forget the active profile; 16/16 focused tests passed in 4.8 seconds with exact 314651736-byte dependency preflight, and the slice does not scan, associate, restart Wi-Fi, change preferences, or complete NET-04",
"exact head b6e96eebd08df0eb8b75867b8e341f3e7a76a830 passed risk-scoped shell workflow 33171784731 with 1m29s test execution; its exact 14104-byte helper with SHA-256 5fd88c120a0a4a979a890865a5ac06e7dbeb7e50ffcd99ce08cfa389e637ecc7 was transferred through strict key-only SSH and exercised only under /tmp/bloom-net04a-fixture-b6e96eeb with synthetic profiles, where active deletion failed closed, inactive deletion succeeded, root/store/config modes were 0700/0600/0600, live supplicant SHA-256 d7fbadd510e76c423638513337620e17944d51b4505ff314354971d97114bdef remained unchanged, temporary state was removed, and aggregate device health remained green; PR #487 merged as 105f744d2fc88d0cacf023ecb62e800572dd0933",
"draft PR #488 adds a fixed-interface asynchronous scan worker with immediate high-entropy operation IDs, redacted status, explicit local-only results, cancellation, terminal discard, one active worker, bounded rate/runtime/raw output/results/history, open/WPA2-PSK/unsupported classification, and bloomctl delegation; 9/9 affected tests passed in 13.8 seconds and 13/13 focused manifest/provenance tests passed in 271 seconds with exact 314669717-byte dependency preflight, and the slice does not connect, forget, change a preference, restart Wi-Fi, modify DHCP, or complete NET-04",
"exact head b5de4b1ae7f7d558886a8c55047b3a7ada141621 passed risk-scoped shell workflow 33173965383 with 1m39s test execution; its exact 17244-byte helper with SHA-256 0d3b3e1ac893fb787fcd7c3d28b2c526175475d360bab83836d9072efab695b3 and a 156-byte synthetic platform fixture were transferred through strict key-only SSH and exercised only under /tmp with /bin/echo replacing iw, transitioned RUNNING to SUCCEEDED with zero results, enforced 0700/0700/0600/0600 root-operation-state-result modes, discarded the terminal operation, removed every task file, retained live supplicant SHA-256 d7fbadd510e76c423638513337620e17944d51b4505ff314354971d97114bdef, and left aggregate device health green without a real scan or network mutation",
"PR #489 adds a rollback-bounded wifi-profile-connect transaction that preserves the exact prior supplicant bytes in a mode-0600 snapshot below a mode-0700 JFFS2-private root, places only its SHA-256 in public transaction metadata, routes station replacement through the canonical network and Wi-Fi owners, and restores the exact prior configuration after apply failure, explicit rollback, timeout, or interrupted boot; 88/88 direct affected tests and 140/140 exhaustive focused tests passed with exact 314676226-byte inventory preflight",
"exact candidate ff5b0775 was 33007 bytes with SHA-256 d3bed4f13e6e3b84344ba9c370f6a258b09d5e23a96378068a64eff30b2ab68e and passed an isolated Mini Plus /tmp fixture using synthetic station content and mocked dependencies: apply changed only the fixture, rollback restored the exact prior hash, private root/snapshot modes were 0700/0600, no public secret copy existed, all task files were removed, and the live device remained associated and healthy without a real scan, profile transition, Wi-Fi toggle, DHCP mutation, listener mutation, update, or reboot"
"exact candidate ff5b0775 was 33007 bytes with SHA-256 d3bed4f13e6e3b84344ba9c370f6a258b09d5e23a96378068a64eff30b2ab68e and passed an isolated Mini Plus /tmp fixture using synthetic station content and mocked dependencies: apply changed only the fixture, rollback restored the exact prior hash, private root/snapshot modes were 0700/0600, no public secret copy existed, all task files were removed, and the live device remained associated and healthy without a real scan, profile transition, Wi-Fi toggle, DHCP mutation, listener mutation, update, or reboot",
"PR #490 exposes the existing private profile and guarded transaction authorities through bounded bloomctl network profile status, profile upsert ID, connect begin/apply/confirm/cancel, and forget ID commands; profile input remains stdin-only, traversal IDs fail locally, forget still refuses the projected profile, and active forget is only the safe sequence of independently confirming a distinct fallback before deleting the now-inactive old ID; 76/76 direct tests and 99/99 exhaustive focused tests passed with exact 314678142-byte inventory preflight",
"exact candidate b06f4a82 bloomctl was 37288 bytes with SHA-256 f9e765dd668aeff9137772ecd06bf3bd02b7fe30597e46243dcb2b1caa29046e and passed an isolated Mini Plus /tmp fixture against mocked network, transaction, and profile helpers with synthetic stdin content; every task file was removed and live networking remained associated without a real scan, profile transition, active deletion, Wi-Fi toggle, DHCP or listener mutation, update, or reboot"
]
},
{
Expand Down
33 changes: 32 additions & 1 deletion static/build/.tmp_update/bin/bloomctl
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ BLOOM_NETWORK="${BLOOM_NETWORK_BIN:-$SCRIPT_DIR/bloom-network}"
BLOOM_NETWORK_SERVICES="${BLOOM_NETWORK_SERVICES_BIN:-$SCRIPT_DIR/bloom-network-services}"
BLOOM_NETWORK_TRANSACTION="${BLOOM_NETWORK_TRANSACTION_BIN:-$SCRIPT_DIR/bloom-network-transaction}"
BLOOM_WIFI_SCAN="${BLOOM_WIFI_SCAN_BIN:-$SCRIPT_DIR/bloom-wifi-scan}"
BLOOM_WIFI_PROFILES="${BLOOM_WIFI_PROFILES_BIN:-$SCRIPT_DIR/bloom-wifi-profiles}"
BLOOM_JQ="${BLOOM_JQ_BIN:-$SCRIPT_DIR/jq}"
BLOOM_CONTROLS="${BLOOM_CONTROLS_BIN:-$SCRIPT_DIR/bloom-controls}"
BLOOM_TIME="${BLOOM_TIME_BIN:-$SCRIPT_DIR/bloom-time}"
Expand Down Expand Up @@ -379,7 +380,7 @@ usage() {
printf ' bloomctl platform capabilities\n' >&2
printf ' bloomctl settings {status|values|import-onion|sync-onion|reconcile-onion|materialize-onion|activate-bloom|rollback-authority|reset-defaults|first-run-status|complete-first-run|set FIELD VALUE}\n' >&2
printf ' bloomctl power {status|reboot|poweroff}\n' >&2
printf ' bloomctl network {status|reconcile|enable|disable|scan start|status ID|results ID|cancel ID|discard ID|transaction status|begin SECONDS|apply NONCE OPERATION [ID]|confirm NONCE|rollback}\n' >&2
printf ' bloomctl network {status|reconcile|enable|disable|profile status|upsert ID|forget ID|scan start|status ID|results ID|cancel ID|discard ID|connect begin SECONDS|apply NONCE PROFILE_ID|confirm NONCE|cancel|transaction status|begin SECONDS|apply NONCE OPERATION [ID]|confirm NONCE|rollback}\n' >&2
printf ' bloomctl controls {status|brightness VALUE|volume VALUE|mute true}\n' >&2
printf ' bloomctl time status\n' >&2
printf ' bloomctl lid status\n' >&2
Expand Down Expand Up @@ -626,6 +627,22 @@ network_command() {
1:status:) "$BLOOM_NETWORK" status ;;
1:reconcile:) "$BLOOM_NETWORK" request reconcile ;;
1:enable:|1:disable:) "$BLOOM_NETWORK" request "$1" ;;
2:profile:status)
[ -x "$BLOOM_WIFI_PROFILES" ] || { printf 'bloomctl: Wi-Fi profile service is unavailable\n' >&2; return 1; }
"$BLOOM_WIFI_PROFILES" status --json
;;
3:profile:upsert)
case "$3" in ''|.*|-*|*[!a-z0-9._-]*) usage; return 2 ;; esac
[ "${#3}" -le 32 ] || { usage; return 2; }
[ -x "$BLOOM_WIFI_PROFILES" ] || { printf 'bloomctl: Wi-Fi profile service is unavailable\n' >&2; return 1; }
"$BLOOM_WIFI_PROFILES" upsert "$3"
;;
2:forget:*)
case "$2" in ''|.*|-*|*[!a-z0-9._-]*) usage; return 2 ;; esac
[ "${#2}" -le 32 ] || { usage; return 2; }
[ -x "$BLOOM_WIFI_PROFILES" ] || { printf 'bloomctl: Wi-Fi profile service is unavailable\n' >&2; return 1; }
"$BLOOM_WIFI_PROFILES" delete "$2"
;;
2:scan:start)
[ -x "$BLOOM_WIFI_SCAN" ] || { printf 'bloomctl: Wi-Fi scan service is unavailable\n' >&2; return 1; }
"$BLOOM_WIFI_SCAN" start
Expand Down Expand Up @@ -654,6 +671,20 @@ network_command() {
[ -x "$BLOOM_NETWORK_TRANSACTION" ] || { printf 'bloomctl: network transaction service is unavailable\n' >&2; return 1; }
"$BLOOM_NETWORK_TRANSACTION" apply "$3" "$4" "$5"
;;
3:connect:begin|3:connect:confirm)
[ -x "$BLOOM_NETWORK_TRANSACTION" ] || { printf 'bloomctl: network transaction service is unavailable\n' >&2; return 1; }
"$BLOOM_NETWORK_TRANSACTION" "$2" "$3"
;;
2:connect:cancel)
[ -x "$BLOOM_NETWORK_TRANSACTION" ] || { printf 'bloomctl: network transaction service is unavailable\n' >&2; return 1; }
"$BLOOM_NETWORK_TRANSACTION" rollback
;;
4:connect:apply)
case "$4" in ''|.*|-*|*[!a-z0-9._-]*) usage; return 2 ;; esac
[ "${#4}" -le 32 ] || { usage; return 2; }
[ -x "$BLOOM_NETWORK_TRANSACTION" ] || { printf 'bloomctl: network transaction service is unavailable\n' >&2; return 1; }
"$BLOOM_NETWORK_TRANSACTION" apply "$3" wifi-profile-connect "$4"
;;
*) usage; return 2 ;;
esac
}
Expand Down
Loading