Skip to content

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:

  1. explicit build CLI/environment override,
  2. ignored private configuration such as flashberry.local.yml,
  3. tracked project configuration such as flashberry.yml,
  4. 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:

identity:
  ssh:
    authorized_keys:
      - "ssh-ed25519 AAAA... workstation"

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:

python3 scripts/config.py validate flashberry.yml

Inspect the fully normalized configuration without revealing password/PSK values:

python3 scripts/config.py show-effective flashberry.yml

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.