mirror of
https://github.com/BetterCorp/BetterFrame.git
synced 2026-05-27 01:46:35 +00:00
56 lines
2.2 KiB
TypeScript
56 lines
2.2 KiB
TypeScript
|
|
/**
|
||
|
|
* Wire schema for the managed-image device config: pushed from server to
|
||
|
|
* kiosks running our pre-built Pi OS image. Kiosks pull on heartbeat
|
||
|
|
* (response includes `pending_config` when server-side version exceeds
|
||
|
|
* `applied_version`), apply via a privileged helper, echo `applied_version`
|
||
|
|
* back on the next heartbeat.
|
||
|
|
*
|
||
|
|
* Wifi PSKs are encrypted with the cluster_key delivered at pairing time,
|
||
|
|
* so the server can store ciphertext at rest and ship it to the kiosk
|
||
|
|
* without a per-kiosk re-encryption step.
|
||
|
|
*/
|
||
|
|
import * as av from "@anyvali/js";
|
||
|
|
|
||
|
|
export const NETWORK_MODES = ["dhcp", "static"] as const;
|
||
|
|
|
||
|
|
export const managedNetworkConfig = av.object(
|
||
|
|
{
|
||
|
|
mode: av.enum_(NETWORK_MODES),
|
||
|
|
// Interface name as exposed by NetworkManager (e.g. "eth0", "wlan0").
|
||
|
|
// Optional — helper defaults to the primary wired interface.
|
||
|
|
interface: av.optional(av.string().minLength(1).maxLength(32)),
|
||
|
|
// IPv4 CIDR for static mode. Ignored when mode=dhcp.
|
||
|
|
ip_cidr: av.optional(av.string().pattern("^[0-9.]+/[0-9]+$")),
|
||
|
|
gateway: av.optional(av.string().minLength(1).maxLength(64)),
|
||
|
|
dns: av.optional(av.array(av.string().minLength(1).maxLength(64))),
|
||
|
|
// 802.1Q VLAN id; helper creates a virtual interface when set.
|
||
|
|
vlan_id: av.optional(av.int().min(1).max(4094)),
|
||
|
|
},
|
||
|
|
{ unknownKeys: "reject" },
|
||
|
|
);
|
||
|
|
|
||
|
|
export const managedWifiConfig = av.object(
|
||
|
|
{
|
||
|
|
ssid: av.string().minLength(1).maxLength(64),
|
||
|
|
// PSK encrypted with cluster_key via shared/secrets.encryptString.
|
||
|
|
// Helper decrypts in-process before handing to NetworkManager.
|
||
|
|
psk_ciphertext: av.string().minLength(1).maxLength(512),
|
||
|
|
},
|
||
|
|
{ unknownKeys: "reject" },
|
||
|
|
);
|
||
|
|
|
||
|
|
export const managedConfig = av.object(
|
||
|
|
{
|
||
|
|
hostname: av.optional(av.string().minLength(1).maxLength(64)),
|
||
|
|
// IANA tz name, e.g. "Etc/UTC", "America/New_York".
|
||
|
|
timezone: av.optional(av.string().minLength(1).maxLength(64)),
|
||
|
|
network: av.optional(managedNetworkConfig),
|
||
|
|
wifi: av.optional(managedWifiConfig),
|
||
|
|
},
|
||
|
|
{ unknownKeys: "reject" },
|
||
|
|
);
|
||
|
|
|
||
|
|
export type ManagedNetworkConfig = av.Infer<typeof managedNetworkConfig>;
|
||
|
|
export type ManagedWifiConfig = av.Infer<typeof managedWifiConfig>;
|
||
|
|
export type ManagedConfig = av.Infer<typeof managedConfig>;
|