Configuration roadmap
This is a from-scratch, tier-by-tier plan for everything HiveKeeper could let an operator configure on a
HiveOS AP that it does not expose today. It expands the phase summary in the project README with concrete,
grammar-confirmed work. All CLI grammar below was confirmed live on an AP230 (HiveOS 10.6r1a) via ?
context help (scripts/hk-cli-explore.py), not guessed.
Principles (apply to every phase)
- TDD + atomic design for
hive-web— pure logic (CLI builders, parsers, advisories) lives insrc/libwith unit tests first; UI is atoms → molecules → organisms. hive-corestays vendor-agnostic — for now the web UI generates HiveOS CLI and pushes it throughapply-config; this UI-side grammar can later move into ahive-coredriver generator for multi-vendor.- North-star invariants — secrets never persist in the cloud gateway (only a
credRefdoes); the core engine is tenant-unaware and transport-agnostic. - Danger discipline — any command that can drop AP connectivity warns + confirms (the pattern in
NetworkForm/ClientModeForm). - Commits and docs in English.
Baseline — what exists today
Configurable now: hostname, Radio (channel / power / mode only), Mesh/Hive + tuning, Captive portal,
CAPWAP standalone, Network (mgmt IP / VLAN / gateway / DHCP-client / DNS / NTP), Client-mode, LED, SNMP,
Syslog, Wi-Fi (WPA2-PSK SSID create/edit/remove + VLAN), Advanced raw-CLI, Backup/Restore, Firmware upgrade
(lab/untested). Read-only: inventory, clients, live radio status (show acsp), CAPWAP state, on-AP log, map,
audit log. Adoption: SSH-banner discovery → adopt (probes show version, requires a serial).
Doc accuracy fix:
capabilities.mdclaims Radio configures “band, channel, width, Tx power”, butRadioFormonly sets channel/power/mode. Channel width and band are not configurable yet — they live on the radio profile (see Phase 1). Correctcapabilities.mdwhen Phase 1 lands.
Phase 0 — Foundation: credentials & trustworthy adoption
The two gaps that block everything else: there is no place to manage AP credentials, and discovery cannot tell an AP from any other SSH host.
0a. Credential management (per device / per site) — SHIPPED
Set or rotate a device’s SSH credential from the UI; the secret is sealed to the on-prem agent’s public key at the gateway (end-to-end, so the cloud never holds a usable plaintext) and written to the agent’s local vault, encrypted at rest. As built:
CredentialProvidergained a writable variant (WritableCredentialProvider) + aSecretUnsealerSPI; the agent’sWritableVaultCredentialProviderencrypts passwords at rest (HIVEKEEPER_VAULT_KEY) and still reads legacy plaintext.- New
Command.SetCredential/Result.CredentialSetflow through the existing engine/agent pipeline.EnvelopeCipher(RSA-OAEP + AES-GCM,env1:token;plain1:dev fallback) lives inhive-core.cryptoalongside the promotedSecretCipher. The gateway caches each agent’s public key from its mTLS cert and seals onPOST /api/agents/{id}/set-credential(admin) — synchronous, never a durable job, never persisted, never logged. ACredentialFormsection drives it. - Also changing the admin password ON the AP is implemented in
HiveOsDriver.adminPasswordCommands(admin root-admin <user> password …) and enabled — the grammar AND the password policy (8–32 chars, ≥1 digit, ≥1 uppercase, ≠ username/‘password’) were confirmed live on an AP230 with an end-to-end change test. The UI toggle is confirm-gated (a wrong value can lock out the AP, recoverable by reset). - Acceptance met: unit-tested offline (vault written, no plaintext on the wire, gateway leaks nothing) and the on-AP admin-password change validated end-to-end against the live AP230.
0b. Adoption with identification — SHIPPED
- Identity probe in discovery — an Identify action per discovered host runs an inventory through the agent; a success means a reachable HiveOS AP (and its model), a failure means it did not identify as one.
- Support classification —
supportLevel(a pure lib) maps a model to a badge: tested (AP230 / AP250 / AP630) / HiveOS · untested / unsupported, shown on the discovered host (after Identify) and on the device detail header. A soft signal, not a hard block. - Credential prompt at adopt — the discovered-hosts panel takes an optional username/password; on adopt the gateway registers the device and then sets its credential through the 0a sealed path, in one flow.
- Acceptance met offline (Identify + adopt-with-credential are unit-tested; the badge reflects the model). Live identify/adopt against the AP230 remains to validate when it is reachable.
Phase 1 — Radio completeness (closes the latency/density story)
Makes the new radioAdvisories warnings actionable. SHIPPED — channel width, client target power,
band-steering, client load-balancing and the per-profile max-client cap are live in the Radio section (a
radioProfileCommands builder + the RadioProfileForm organism, both unit-tested; tx-power-control added to
the existing RadioForm), and slow-rate pruning ships as a minimum-data-rate picker in the Wi-Fi
section (minRateCommands + a block in WifiSection). The channel-width advisory renders next to the profile
control. The rate-set grammar AND a generated line were confirmed live on the AP230 (ssid <name> 11g-rate-set 12-basic 18 24 36 48 54 applied to running-config and reverted). Note the HiveOS split confirmed
on the AP230: the
interface wifiN radio menu has channel, power/txpower, tx-power-control, range, rx-sop,
ed-threshold, antenna, dfs-backup-channel — but channel width and most density knobs live on the
named radio profile (radio_ng0 2.4 GHz, radio_ac0 5 GHz) that interfaces reference.
- ✅ Channel width —
radio profile <name> channel-width 20|40|80. The form surfaces the blast radius (a profile may be shared across interfaces/APs — wider than per-interface channel) and shows the width advisory. - ✅ Client target power —
interface wifiN radio tx-power-control <1-20|auto>(addresses AP↔client asymmetry / sticky clients). - ✅ Band-steering —
radio profile <name> band-steering(push dual-band clients to 5 GHz). - ✅ Client load-balancing —
radio profile <name> client-load-balance(spread clients across hive members). - ✅ Drop slow basic rates —
ssid <name> 11g-rate-set/11a-rate-set(kill 1/2/5.5/11 Mbps 11b rates that hog airtime — a large high-density win). A minimum-data-rate picker in the Wi-Fi section; the lowest kept rate becomes the only-basic(mandatory) rate, dropping everything slower from the air. Confirmed live on the AP230. - ✅ Max clients (per profile) —
radio profile <name> max-client. (ssid <name> max-clientbelongs with the per-SSID hardening in Phase 2.)
Phase 2 — Wi-Fi & security (largest functional gap) — SHIPPED
Was WPA2-PSK only. The AP230 supports a full suite (security-object <so> security protocol-suite ?), now exposed.
- ✅ Security suite picker —
open,wpa2-aes-psk,wpa3-saeship now: asecurityfield onSsidSpecdrives the typed configure-ssid path (so the passphrase stays sealed/redacted), the Add SSID form has a suite selector (passphrase hidden foropen), and an edit preserves the SSID’s existing suite. Theprotocol-suitegrammar was confirmed live on the AP230 (wpa3-sae/opentakeascii-key/no-key respectively). The 802.1X suites (wpa2-aes-8021x,wpa3-aes-8021x-std) need a RADIUS server and land with the 802.1X/RADIUS work below. - ✅ 802.1X / RADIUS — the enterprise suites (
wpa2-aes-8021x,wpa3-aes-8021x-std) ship via the typedSsidSpecpath: a nestedRadiusSpec(server + shared-secret + optional auth-port) emitssecurity-object <so> security aaa radius-server primary <ip> shared-secret <secret>. The shared secret is masked bySecrets(now coversshared-secret) and encrypted at rest in durable jobs. The Add SSID form swaps the passphrase field for RADIUS server + secret when an enterprise suite is picked; 802.1X SSIDs are edited by remove + re-add. Grammar confirmed live on the AP230. (Backup1-3 / accounting / keepalive are a later tuning pass.) - ✅ PPSK (Private PSK) — the self-registration model ships (Caminho A): a HiveAP serves PPSK and hosts the
enrolment portal, so users register and the AP issues + stores the per-user key locally (in
users.txt, which the backup captures). The PPSK block in the Wi-Fi section emits, all confirmed live:[no] security-object <so> security private-psk(+external-server,default-psk-disabled,ppsk-server <mgt0-ip>),[no] security-object <so> ppsk-web-server(+https,web-directory <d>,auth-userto authenticate registrants against RADIUS), andssid <so> user-group <group>— via apply-config. HiveKeeper does NOT mint individual keys: HiveOS has no running-config grammar to create a key (… private-psk user …is rejected — confirmed live); keys come from user self-registration or an external server. Admin-driven key minting is Caminho B below. - ✅ Per-SSID hardening/tuning —
hide-ssid,max-client,schedule,dtim-period,inter-station-traffic(surfaced as client isolation, its inverse),rrm(802.11k),wnm(802.11v) ship as a Hardening block in the Wi-Fi section (ssidHardeningCommands, apply-config). Grammar confirmed live.
Phase 3 — Network policy & L2/L3 — SHIPPED
Was: VLAN read from config but not edited beyond the SSID’s optional VLAN. Now a full policy/L2-L3 surface, all
grammar confirmed live on the AP230 (with two write quirks the live probes caught: HiveOS rejects a
combined user-profile <n> attribute 99 vlan-id 99 — each setting is its own line, the running-config later
coalescing them; and firewall/QoS objects must be created before they take a rule).
- ✅ User-profiles & VLANs — the Policy section lists the user profiles read from the running-config and
lets an operator create/overwrite one (default VLAN id or VLAN group, optional QoS policy +
schedule, keyed by a numeric attribute 0–4095), bind it to an SSID’s security object
(
security-object <so> default-user-profile-attr <attr>), and remove it (userProfileCommands/bindUserProfileCommands/removeUserProfileCommands+parseUserProfiles). - ✅ Per-profile policy — a bandwidth SLA (
performance-sentinel enable | guaranteed-bandwidth | action), L2/L3 firewall bindings + default actions (security ip-policy from-access|to-access,ip-policy-default-action, the MAC equivalents) and aqos-marker-map 8021p|diffserv(userProfilePolicyCommands). - ✅ Firewall policy objects —
ip-policy <name>(create + apermit|deny|nat|redirectrule on a service between from/to) andmac-policy <name>(create; MAC rules via Advanced raw-CLI — the combined line is rejected) (ipPolicyCommands/macPolicyCommands+parseFirewallPolicies). - ✅ QoS — a
qos policy <name>rate-limit object (qos policy <n> user-profile <kbps> <weight>,qos enable), referenced by a profile’s QoS field (qosPolicyCommands+parseQosPolicies); and per-SSIDqos-classifier/qos-markerprofile bindings +wmmtoggle for voice/video priority (ssidQosCommands, in the Wi-Fi section). - ✅ LLDP — device-wide
lldpenable +timer/holdtime/receive-only/max-entries(lldpCommands, in the Network section). HiveOS has no per-interface LLDP (interface mgt0 lldpis rejected). - ✅ Static routes —
ip route net <ip> <mask> gateway <gw> [metric]/ip route host <ip> gateway <gw>, listed + added + removed (confirm-gated; the gateway must be on a connected subnet) (staticRouteCommands+parseStaticRoutes, in the Network section). - ⛔ Multicast / IGMP snooping — not exposed: the AP230 has no
igmpcommand, nomgt0sub-node, and noshow igmp(confirmed live). The AP bridges multicast; IGMP snooping is the upstream switch’s job. Tunnel policy is likewise referenced-only (no top-level object grammar) — seecapabilities.md→ Not yet.
Phase 4 — RF tuning & high-density (advanced) — SHIPPED
Radio knobs for dense RF environments, behind an Advanced RF tuning disclosure in each radio form. All
grammar was confirmed live on the AP230 via ? context help — and it caught the HiveOS split between
where knobs live: interface-level (interface wifiN radio …) vs profile-level (radio profile <name> …).
- ✅ Interface-level (added to
radioCommands, surfaced inRadioForm):rx-sop <number|high|low|medium>(receiver start-of-packet detection — dBm or a density preset),ed-threshold <-70..-50>(energy-detect threshold),dfs-backup-channel <freq|channel>(the 5 GHz fallback when radar forces a channel switch). - ✅ Profile-level (added to
radioProfileCommands, surfaced inRadioProfileForm):dfs,short-guard-interval,ampdu,amsdu,frameburst(bare-line toggles,no …to disable);high-density enableandweak-snr-suppress enable(toggles whose positive form carries theenablesub-word, so the negation isno … enable);tx-beamforming auto|explicit-only(no …to disable);phymode 11a|11ac|11b/g|11na|11ng; andreceive-chain/transmit-chain <1-3>. - Acceptance met: the two builders and both organisms are unit-tested, and the knobs were applied and
reverted live on the AP230 (no
save, non-persistent) — interface knobs onwifi1, profile knobs on throwaway custom profilesHKTEST*(created, confirmed inshow running-config, removed withno radio profile …).capabilities.mdupdated. - Live write quirks the apply test caught (and why probing beats
?-only confirmation):- Default radio profiles are read-only.
radio profile radio_ac0 …(andradio_ng0) is rejected with “can’t configure default radio profile radio_ac0!” — this hits Phase 1’s channel-width too. The knobs only apply to a custom profile, which the first knob line auto-creates.RadioProfileFormwarns when the chosen name looks like a factory default, and a Bind to radio selector closes the loop: it appendsinterface wifiN radio profile <name>as the last line so the configured custom profile actually takes effect (confirmed live: create+configureHKBIND,interface wifi1 radio profile HKBIND, revert by re-bindingradio_ac0+no radio profile HKBIND). Binding is warned (it swaps the radio’s whole profile and briefly drops its wireless clients; mgt0 is unaffected) and flags a band mismatch (an 11ac profile bound to the 2.4 GHz radio). phymodemust precedechannel-widthandtx-beamforming. A fresh profile is11b/g; both reject as “inconsistent with current phymode” / “incompatible PHY mode … configure PHY mode first” until phymode matches.radioProfileCommandstherefore emitsphymodefirst.
- Default radio profiles are read-only.
Phase 5 — Operations & lifecycle
- Firmware upgrade GA — validate the
save imagepath on real hardware, move it out of lab/untested. - Scheduling — global
schedule <name>objects reused by SSIDs and user-profiles. SHIPPED (objects): a Schedules section (scheduleCommands/removeScheduleCommandsbuilders +parseSchedules+ theScheduleSectionorganism, all unit-tested) creates/lists/removes recurrent (weekday-rangeand/ortime-range, optionaldate-range) and one-time (once <date> <time> to <date> <time>) schedules; both forms were applied to the running-config and reverted live on the AP230. SSID hardening (Phase 2) and user-profiles (Phase 3) already reference these by name. - Scheduled reboot — SHIPPED. A confirm-gated Scheduled reboot control in the Power section
(
rebootScheduleCommands/cancelRebootScheduleCommandsbuilders +parseRebootSchedule+ theScheduledRebootFormorganism, all unit-tested) sets a recurringreboot schedule daily every <n> day(s) time <hh:mm:ss>/weekly every <n> week(s) <Weekday> time <hh:mm:ss>, reads the next reboot fromshow reboot schedule, and cancels withno reboot schedule. A decisive live quirk justified probing over?-help: the recurringreboot scheduleforms are non-interactive (apply-config can drive them — validated by scheduling a future reboot, confirming viashow, then cancelling, with no actual reboot), butreboot dateandreboot offsetprompt “Do you really want to reboot? (Y/N)” which would hang the exec channel — so only the recurring forms are exposed. The schedule is not part of the running-config (save: false). - Config templates — SHIPPED. Apply a set of HiveOS CLI lines (a template) to every device in a scope
(org / site / group) in one bulk write. New gateway endpoint
POST /api/fleet/bulk/apply-config(BulkApplyConfigRequest→doBulkApplyConfig) mirrors the read-only bulk path but requires OPERATOR, validates/normalizes the command list server-side, and re-authorizes each device at operator level against its own lineage before touching it (a cross-site group can carry a foreign-site device) — reusing the same per-device outcome model (ok / failed / agent_offline / skipped / forbidden / timeout) and wall-clock budget. UI: a Config templates section on the Bulk ops page (ConfigTemplatePanel) with a scope picker, a CLI editor, asave configtoggle, a confirm gate (it writes to many APs), and the per-device outcomes table; named templates are saved locally (configTemplate.js—parseTemplateCommands+ localStorage CRUD). All unit-tested (gateway security slice + web lib/organism/client). - Alerting / thresholds — SHIPPED (in-console). An Alerts page runs an on-demand fleet scan (each
device’s agent online-state + a live inventory read) and evaluates it against threshold rules, listing only
the APs that breach one: agent offline, still cloud-managed (CAPWAP up — HiveKeeper’s signature), high
client load (a configurable per-AP cap), and radios outside best practice (reusing
radioAdvisories). A purealerts.jsrules engine (evaluateAlerts+worstSeverity+ locally-persisted thresholds) feeds both the fleet scan (FleetAlertsPanel, which re-evaluates the held snapshots live as you edit a threshold) and can feed the device Monitoring tab. All unit-tested. Background poller + delivery — SHIPPED (postgres profile). The rules were ported to Java (hive-coreAlertRules+RadioAdvisories, faithful to the web engine and unit-tested) and aFleetPoller(@Scheduled,@EnableScheduling) scans every tenant on an interval (HIVEKEEPER_ALERT_POLL_INTERVAL_MS, default 5 min), reaching each device through its agent exactly like bulk inventory. It diffs results against afleet_alertstate table (FlywayV10, RLS) so each alert is delivered on onset and again on resolution, never every poll, and adds aninventory-failedsignal when a connected agent can’t read its AP. Delivery goes throughAlertNotifierto per-tenant webhook (RestClient) and email (JavaMailSender/SMTP) channels, each with a minimum-severity floor; one failing channel never blocks the others. Channels + thresholds persist per tenant (alert_channel/alert_settings,PostgresAlertService+InMemoryAlertService) behind REST (/api/alerts/...: viewer to read, admin to mutate) and an Alert delivery section on the Alerts page. The PSK/secret-free alert payload carries only fleet metadata. All unit-tested (rules, notifier gating, poller dedup/resolution, REST authz, web organism). - PPSK admin-driven key management (Caminho B) — let an operator mint per-user private PSKs from HiveKeeper
itself, rather than relying on end-user self-registration (Phase 2’s Caminho A). HiveOS exposes no
running-config grammar to create an individual key over SSH (confirmed live on the AP230:
security-object <so> security private-psk user …is rejected — that path is HiveManager’s proprietary channel). The clean, north-star-aligned way to add it is to make the on-prem agent run/own a RADIUS server (e.g. FreeRADIUS) that stores the user↔PSK mappings, and point the AP at it viaaaa ppsk-server radius-server primary <ip>/security-object <so> ppsk-web-server auth-user/security-object <so> security private-psk radius-auth(all grammar already confirmed on the AP230). HiveKeeper would then own a key CRUD (generate/rotate/revoke a user’s PSK) in its DB and provision RADIUS — a new subsystem (a RADIUS-managing component), not a guided form, so it lands as its own phase. The agent-as-RADIUS placement keeps secrets on-prem, consistent with the sealed-credential model. (Writing keys directly into the AP’s localusers.txtover SSH is not a supported/safe path — those live in a separate TPM store, not the replayable running-config.) The full architecture is now specified in PPSK via RADIUS — design: the AP→RADIUS wiring is confirmed live (aaa ppsk-server radius-server primary …,security-object <so> security private-psk radius-auth); the remaining work is the RADIUS runtime on the agent, a sealed key store, and theManagePpskUserCRUD pipeline. It stays its own phase because the RADIUS runtime is infrastructure that must be stood up and validated end-to-end (a guided form alone cannot deliver it). Milestone 1 (AP→RADIUS wiring) — SHIPPED. AppskRadiusCommandsbuilder + a PPSK via RADIUS block in the Wi-Fi section point the AP’s local PPSK server at an external RADIUS backend (aaa ppsk-server radius-server primary <ip>with optionalshared-secret/auth-port, plusaaa ppsk-server auto-save-interval <60-3600>) and forward a security object’s private-PSK auth to it (security-object <so> security private-psk radius-auth pap|chap|ms-chap-v2; a bareradius-authdefaults to PAP). The shared secret is masked bySecretsserver-side. All grammar — including theradius-authmethods left as<method>in the design — was confirmed live on the AP230: applied to the running-config against a throwaway security object, confirmed viashow running-config, then reverted clean (non-persistent, nosave).auth-port 1812/auto-save-interval 600are RADIUS defaults the AP omits from the running-config. Unit-tested (builder + organism). Milestones 2-3 — SHIPPED (unit-tested, lab-untested). HiveKeeper now mints, rotates, and revokes per-user Private PSKs it owns on-prem:- M2 — sealed
ManagePpskUserpipeline + key CRUD. AManagePpskUsercommand /PpskUserManagedresult flow through the same agent-control path asSetCredential(handled before SSH dispatch — the AP is never touched): the gateway generates the key (PskGenerator, pure/RNG-injectable), seals it to the agent (EnvelopeCipher,env1:), and the engine unseals it locally and writes the on-premPpskUserStore. The gateway persists metadata + apsk_refonly (FlywayV9, RLS-isolatedppsk_user;PostgresPpskUserServiceInMemoryPpskUserService), exposesGET/POST /api/agents/{id}/ppsk-users,POST …/{id}/rotate,DELETE …/{id}(VIEWER to list, OPERATOR to mutate, scoped to the agent’s site), and returns the generated PSK once. A PPSK users device tab (PpskUsersSection) drives it; the agent’sFilePpskUserStoreencrypts keys at rest (HIVEKEEPER_VAULT_KEY) like the credential vault.
- M3 — agent RADIUS runtime.
FreeRadiusFilesProvisionerrenders the on-prem user set into a FreeRADIUSfiles-module authorize file (standard PAPCleartext-Password+ RFC 2868 VLAN tunnel attributes) on every mutation;scripts/dev-radius.ps1stands up FreeRADIUS in Podman co-located with the agent. - M4 — live lab validation — REMAINS (needs real hardware + a Wi-Fi client). The end-to-end auth path and the exact Aerohive user-profile VSA (currently a documented comment, not emitted) must be captured from a real exchange. Until then PPSK Caminho B ships untested, like firmware upgrade. Full steps: PPSK via RADIUS — runbook.
- M2 — sealed
Phase 6 — Wi-Fi 6, redundancy & lifecycle — SHIPPED
Driven by a live session against an AP230, an AP630 and an AP410C-1 (HiveOS 10.6r6).
- Wi-Fi 6 (802.11ax) — SHIPPED. Radio-profile
phymode 11ax-2g|11ax-5g,channel-width 160(+40-above/40-below), 1–4 spatial streams, the11axsub-node (bss-color,ofdma-dl/ul,twt) andmu-mimo; and SSID suites OWE (Enhanced Open), WPA3-Enterprise 192-bit and 802.11r fast roaming. Confirmed live and applied to the AP630/AP410C-1. Ceilings the docs had blamed on HiveOS were AP230 hardware limits; the full grammar is offered and the device refuses what it cannot do. - Per-device radio discovery — SHIPPED. The driver asks the AP how many radios it has and binds an SSID to all of them, so the AP410C-1’s third radio is no longer silently left off the air. A four-radio model needs no code change (asserted by test).
- Channel scan — SHIPPED. A per-radio view of
show acspchannel costs + neighbours, with the cheapest channel named. The costs are the AP’s own (ACSP); HiveKeeper explains them.hivekeeper scan+ a console panel, read-only. - Backup destination — SHIPPED. One git repository per organization, set in the console; the token is sealed to each agent and pushed on config and on connect. A failed push is not a failed backup.
- Adoption baseline + manage-from-current — SHIPPED. Adoption captures the AP’s running-config as the as-adopted git baseline; the radio-profile form preloads an existing profile’s values and the Radio form shows current channel/width/power/mode. (Full preload for the remaining value forms — captive portal, client mode, mgt0 network — is a follow-up.)
- Multiple agents per device — SHIPPED. Agents are first-class and device reachability is an explicit many-to-many set (editable in the console), so two or more agents can drive the same AP — active/standby, a load split, distinct network paths, or a migration — no longer tied to the device’s site. The gateway elects one serving agent (deterministic by id over the connected reachable set); unattended work runs on it and durable jobs fail over to a reachable peer. Adopting an AP from either agent dedups to one row, reachable by both.
- Agent auto-update + drain — SHIPPED. Opt-in, tag-following, label-scoped; the agent drains its running job before any restart.
Suggested sequencing
Phase 0 first (it unblocks reliable management and is the operator’s most-felt gap), then Phase 1 (cheap, and
it completes the latency story already surfaced by the radio advisories), then Phases 2–5 by demand. Each
phase ships per-feature: a tested lib builder + an organism + a capabilities.md update.