Configuration
Flashberry reads YAML through scripts/config.py. The selected path is passed
with CFG=...; the default is flashberry.yml. During the overhaul migration,
configuration is normalized into a canonical provider/board/identity/network
schema before validation or rendering.
The precedence contract is:
- explicit build CLI/environment override,
- ignored private configuration such as
flashberry.local.yml, - tracked project configuration such as
flashberry.yml, - schema defaults.
Phase 1 does not merge multiple YAML files itself: CFG still selects one
configuration file. Later provider/build phases may add explicit composition.
Canonical schema and defaults
provider: raspberry-pi-os
board: raspberry-pi-3b
arch: arm64
identity:
user:
name: user
password: "user"
shell: /bin/bash
groups: [sudo]
sudo:
enabled: true
passwordless: false
root:
name: root
password: ""
password_semantics: empty_local_password
ssh:
permit_password_login: false
permit_empty_passwords: false
ssh:
enabled: true
password_authentication: true
authorized_keys: []
network:
backend: NetworkManager
hostname: flashpi
address_observer:
enabled: true
startup_timeout_seconds: 60
report_interfaces: true
report_ipv4: true
report_ipv6: true
state_file: /run/flashberry/network-addresses.json
helper_command: flashberry-ip
ethernet:
enabled: true
interface_match: ["eth*", "en*"]
carrier_wait_seconds: 30
ipv4:
method: auto
dhcp: true
ipv6:
method: auto
wifi:
enabled: true
country: DE
station:
enabled: true
networks: []
select_any_configured_ssid: true
selection_order: [explicit_priority, remembered_success, signal_strength]
connect_timeout_seconds: 35
retry: true
hidden_ssids_supported: true
fallback_hotspot:
enabled: true
trigger: no_configured_station_reachable_within_timeout
remain_available_until_station_connects: true
reenter_after_station_loss_seconds: 60
ssid: flashberry
password: null
password_semantics: null_or_empty_means_open_hotspot
security_when_password_present: wpa2-or-better
ipv4_address: 192.168.42.1/24
dhcp_range: 192.168.42.20-192.168.42.200
dns_forwarding: true
The fallback credentials are intentional overhaul defaults: the effective user
is user, its default password is user, and it belongs to sudo. Root has an
empty local password by default, but identity.root.ssh.permit_password_login
and identity.root.ssh.permit_empty_passwords both default to false, so that
empty root password is not accepted remotely.
The default network backend is NetworkManager. Ethernet is configured for
automatic addressing and the schema reserves the flashberry-ip address
observer. Wi-Fi accepts any number of station profiles. If no configured station
can be reached, the fallback hotspot is enabled; a null or empty hotspot
password means an open hotspot, while a configured passphrase must be
WPA-compatible.
Phase 4 implements that contract during make configure. Flashberry installs
NetworkManager as the configured backend, renders root-owned connection
keyfiles, and disables a legacy dhcpcd service when present so two DHCP clients
do not race. The Ethernet profile autoconnects with DHCP/automatic IPv6, retries
autoactivation indefinitely for late carrier, uses NetworkManager wildcard
matching for the configured eth*/en* interface patterns, and maps
carrier_wait_seconds to NetworkManager's per-device carrier wait timeout.
Wi-Fi station profiles deliberately have NetworkManager autoconnect disabled:
flashberry-network-firstboot owns selection so the configured ordering cannot
be bypassed. It ranks reachable profiles by explicit priority, then remembered
successful-connection timestamp, then signal strength. Hidden profiles remain
eligible without broadcast scan visibility. The configured connection timeout
is shared across candidates so one failing profile cannot consume the complete
selection window.
If no configured station can be reached during startup, the controller activates
flashberry-hotspot. A null/empty hotspot password renders no Wi-Fi security
section (open AP); a configured password renders WPA-PSK constrained to RSN
(WPA2 or better). NetworkManager ipv4.method=shared provides the configured
hotspot address, DHCP range, and DNS forwarding. After station loss, hotspot
re-entry honors reenter_after_station_loss_seconds. With the default
remain_available_until_station_connects, the controller does not explicitly
tear down the fallback before attempting the station; on single-radio hardware
NetworkManager/driver constraints can still require an AP-to-station transition.
flashberry-ip reports current interface addresses plus station/hotspot status
without reading connection secrets. The startup service waits up to
network.address_observer.startup_timeout_seconds for a non-loopback address,
writes network.address_observer.state_file, and a NetworkManager dispatcher
refreshes that state on later address/connectivity changes.
Wi-Fi station profiles
Keep real station credentials in ignored local configuration. Multiple profiles are represented as a list:
network:
wifi:
country: DE
station:
networks:
- ssid: home
psk: "private-passphrase-one"
priority: 50
- ssid: workshop
psk: "private-passphrase-two"
hidden: true
priority: 20
fallback_hotspot:
ssid: flashberry-setup
password: null
A station psk may be null/empty for an open network, 8-63 characters for a
passphrase, or 64 hexadecimal digits. Duplicate station SSIDs are rejected so
selection is deterministic. Hotspot passwords use the same WPA length rules,
but null/empty explicitly means an open hotspot.
Legacy compatibility window
The historical keys remain accepted for one compatibility window and are translated before defaults are applied:
user:
name: pi
password: disabled
hostname: flashpi
ssh_authorized_keys: []
wifi:
country: DE
ssid: LegacyNetwork
psk: "private-passphrase"
hidden: false
Translation is deterministic:
| Legacy field | Canonical field |
|---|---|
user.* |
identity.user.* |
hostname |
network.hostname |
ssh_authorized_keys |
identity.ssh.authorized_keys |
wifi.country |
network.wifi.country |
single wifi profile |
network.wifi.station.networks[0] |
If both forms specify the same translated field, the explicit canonical value wins and legacy input only fills canonical fields that are absent. Validation prints deprecation warnings naming the old field, never its value.
scripts/config.py keeps user.*, hostname, ssh_authorized_keys, and
single-profile wifi.* compatibility aliases for the migration window, but the
Phase-4 configure path consumes the canonical identity.* and network.*
fields. The historical standalone wpa_supplicant.conf provisioning path is
removed; NetworkManager owns both station and hotspot profiles.
Provider and board
Phase 1 recognizes these providers:
raspberry-pi-os— the default; Phase 2 makes its pinned stable Lite 64-bit vendor image the default acquisition path.debian— retained as an expert alternative.archlinuxarm— reserved for the explicit Arch Linux ARM provider path.
The initial board profile is raspberry-pi-3b; Phase 3 adds the boot-stack and
DTB/overlay enforcement for that board. arm64 is the supported architecture
for the default Raspberry Pi OS provider.
The compatibility-only Debian bootstrap fields (suite, mirror, and
components) remain accepted in Phase 1 because the current rootfs builder
still consumes them. Provider-specific acquisition supersedes them in Phase 2.
Identity and SSH keys
Complete OpenSSH public keys belong under identity.ssh.authorized_keys:
Private keys must never be placed in Flashberry configuration. Password and PSK fields are accepted as provisioning inputs but are treated as secrets for inspection/redaction purposes.
Safe validation and redacted inspection
Validate without building:
Inspect the fully normalized configuration without revealing password/PSK values:
show-effective always replaces password/PSK-like values with <redacted>.
Validation errors report field names and constraints but do not echo rejected
secret values. YAML parse errors are intentionally summarized without embedding
source tokens, because parser diagnostics can otherwise disclose credentials.
Low-level get, list, and render-wifi remain internal compatibility
interfaces. render-wifi is no longer used by the configure pipeline, but all
three can access provisioning values and therefore must not be redirected into
logs or provenance. Phase-4 NetworkManager rendering writes secret-bearing
keyfiles directly at mode 0600 and emits only a secret-free runtime policy.
Other retained fields
locale, timezone, packages, firstboot_expand_root, and image remain
supported during the overhaul. image.name may contain letters, digits, .,
_, and -; image.size_gb must be an integer from 1 through 1024.