{{define "configure-interfaces.html"}} {{template "base" .}} {{end}} {{define "content"}}
{{if .Loading}}
Downloading YANG schema from device… this takes a moment on first login.
{{template "configure-toolbar" .}}
{{else}} {{$d := .Desc}} {{if .Error}}
{{.Error}}
{{end}}
Interfaces
{{range $i, $r := .Interfaces}} {{/* ── Fold-out edit form ───────────────────────────────────────── */}} {{end}}{{/* range Interfaces */}}
Name Type Status Member of Addresses
{{.TypeDisplay}} {{if eq .OperStatus "up"}} up {{else if eq .OperStatus "down"}} down {{else}} {{.OperStatus}} {{end}} {{if .MemberOf}}{{.MemberOf}}{{else}}{{end}} {{if .AddrSummary}}{{.AddrSummary}}{{else}}{{end}} {{if ne .TypeSlug "loopback"}} {{end}}
{{/* ── General ──────────────────────────────────────────── */}} {{$ip := printf "/ietf-interfaces:interfaces/interface=%s" .Name}}

General

Description{{template "field-info" (index $d "description")}}
Enabled{{template "field-info" (index $d "enabled")}}
{{/* ── IP Addresses ──────────────────────────────────────── */}} {{if .HasIP}}

IP Addresses

{{/* IPv4 */}}
IPv4{{template "field-info" (index $d "ipv4-address")}}
{{if and .IPv4 .IPv4.DHCP}}
DHCP Settings & Options
Client ID
ARP check
Route preference
DHCP Options
{{if .IPv4.DHCP.Options}} {{range .IPv4.DHCP.Options}} {{end}}
OptionValue
{{.ID}} {{if .Value}}{{.Value}}{{else if .Hex}}hex:{{.Hex}}{{else}}{{end}}
{{end}} {{if $.DHCPv4Options}}
{{end}}
{{end}} {{if and .IPv4 .IPv4.Address}} {{range .IPv4.Address}} {{end}}
{{.IP}}/{{.PrefixLength}} {{if .Origin}}{{.Origin}}{{end}} {{if or (eq .Origin "") (eq .Origin "static")}} {{end}}
{{else}}

No IPv4 addresses configured.

{{end}}
/
{{/* IPv6 */}}
IPv6{{template "field-info" (index $d "ipv6-address")}}
{{if and .IPv6 .IPv6.DHCPv6}}
DHCPv6 Settings & Options
DUID
Information only
Route preference
DHCPv6 Options
{{if .IPv6.DHCPv6.Options}} {{range .IPv6.DHCPv6.Options}} {{end}}
OptionValue
{{.ID}} {{if .Value}}{{.Value}}{{else if .Hex}}hex:{{.Hex}}{{else}}{{end}}
{{end}} {{if $.DHCPv6Options}}
{{end}}
{{end}} {{if and .IPv6 .IPv6.Address}} {{range .IPv6.Address}} {{end}}
{{.IP}}/{{.PrefixLength}} {{if .Origin}}{{.Origin}}{{end}} {{if or (eq .Origin "") (eq .Origin "static")}} {{end}}
{{else}}

No IPv6 addresses configured.

{{end}}
/
{{end}}{{/* HasIP */}} {{/* ── Bridge port editor ──────────────────────────────── */}} {{if .IsBridgePort}}

Bridge Port Settings

{{$bpPath := printf "/ietf-interfaces:interfaces/interface=%s/infix-interfaces:bridge-port" .Name}} {{if .ParentBridgeIs8021Q}} {{end}}
Bridge
PVID
Flood unknown
Multicast router {{/* YANG default is auto. */}} {{$mr := "auto"}}{{if and .BridgePort .BridgePort.Multicast .BridgePort.Multicast.Router}}{{$mr = .BridgePort.Multicast.Router}}{{end}}
Multicast fast-leave {{/* Hidden companion lets the handler distinguish "form omitted the field" from "user unchecked". */}}
{{end}}{{/* IsBridgePort */}} {{/* ── LAG port editor ─────────────────────────────────── */}} {{if .IsLagPort}}

LAG Port Settings

LACP state is operational-only; configure the LAG itself on its own row.

LAG
{{end}}{{/* IsLagPort */}} {{/* ── WiFi interface editor ───────────────────────────── */}} {{/* Radio + PSK pickers mirror the Add-Interface wizard pattern: each picker has Edit / + New buttons that toggle an inline form (radio: name/country/band/ channel; PSK: name/passphrase). The forms POST to the wizard endpoints and HTMX swaps the picker {{if and .WiFi .WiFi.AccessPoint}} {{$ap := .WiFi.AccessPoint}} {{else if and .WiFi .WiFi.Station}} {{$sta := .WiFi.Station}} {{end}}
Radio
{{template "wizard-radio-picker.html" (dict "Radios" $.WizardWifiRadios "Selected" $curRadio "ID" $radioPickerID)}}
SSID
Hidden SSID
Security mode {{$sm := "wpa2-wpa3-personal"}}{{if and $ap.Security $ap.Security.Mode}}{{$sm = $ap.Security.Mode}}{{end}}
SSID
Security mode {{$sm := "auto"}}{{if and $sta.Security $sta.Security.Mode}}{{$sm = $sta.Security.Mode}}{{end}}
PSK
{{template "wizard-psk-picker.html" (dict "Keys" $.WizardSymKeys "Selected" $curSecret "ID" $pskPickerID)}}
{{end}}{{/* IsWifi */}} {{/* ── Bridge editor ────────────────────────────────────── */}} {{if .IsBridge}}
{{/* Bridge settings */}}

Bridge Settings

Type{{template "field-info" (index $d "bridge-type")}}
{{$brPath := printf "/ietf-interfaces:interfaces/interface=%s/infix-interfaces:bridge" .Name}} {{/* STP — own form, save inside the fold-out so the section is self-contained. YANG defaults are rendered inline (value="..." not a placeholder) so the user sees the actual default; the ↺ button drops the candidate value and lets YANG re-apply. */}}
Spanning Tree Protocol
Protocol{{template "field-info" (index $d "stp-force")}} {{$stpForce := "rstp"}}{{if and .Bridge .Bridge.STP.ForceProtocol}}{{$stpForce = .Bridge.STP.ForceProtocol}}{{end}}
Hello time (s){{template "field-info" (index $d "stp-hello")}}
Forward delay (s){{template "field-info" (index $d "stp-fwd-delay")}}
Max age (s){{template "field-info" (index $d "stp-max-age")}}
Transmit hold count{{template "field-info" (index $d "stp-hold-count")}}
Max hops{{template "field-info" (index $d "stp-max-hops")}}
{{/* Multicast Snooping — parallel STP layout. YANG defaults inline: querier=auto, query-interval=125. */}}
Multicast Snooping
Snooping
Querier {{$q := "auto"}}{{if and .Bridge .Bridge.Multicast .Bridge.Multicast.Querier}}{{$q = .Bridge.Multicast.Querier}}{{end}}
Query interval (s)
{{/* Bridge Ports */}}

Bridge Ports

{{if $r.PortCandidates}}
{{range $r.PortCandidates}} {{end}}
{{else}}

No candidate port interfaces available.

{{end}} {{/* 802.1Q VLANs — matrix layout: rows = VLAN, columns = bridge ports + self, cells = U/T/-. Click VID to expand the existing per-port checkbox editor. */}} {{if .BridgeIs8021Q}}

VLANs

{{range $r.BridgeMembers}}{{end}} {{range $vi, $vlan := .VLANRows}} {{range $r.BridgeMembers}} {{if index $vlan.UntaggedSet .}} {{else if index $vlan.TaggedSet .}} {{else}}{{end}} {{end}} {{if index $vlan.UntaggedSet $r.Name}} {{else if index $vlan.TaggedSet $r.Name}} {{else}}{{end}} {{end}} {{if not .VLANRows}} {{end}} {{/* Add VLAN inline row */}}
VID{{.}}{{$r.Name}}
(self)
UTUT
Untagged
{{range $r.BridgeMembers}} {{end}}
Tagged
{{range $r.BridgeMembers}} {{end}}
No VLANs configured
{{end}}{{/* BridgeIs8021Q */}}
{{end}}{{/* IsBridge */}} {{/* ── LAG editor ───────────────────────────────────────── */}} {{if .IsLag}}

Members

{{if $r.PortCandidates}}
{{range $r.PortCandidates}} {{end}}
{{else}}

No candidate port interfaces available.

{{end}}

LAG Settings

{{$lagMode := ""}}{{if and .Lag .Lag.Mode}}{{$lagMode = .Lag.Mode}}{{end}} {{if $.LagHashOptions}} {{end}} {{/* LACP options — hidden unless mode=lacp via JS or always shown */}}
Mode{{template "field-info" (index $d "lag-mode")}}
{{range $.LagModeOptions}} {{end}}
Hash policy{{template "field-info" (index $d "lag-hash")}} {{$lagHash := ""}}{{if and .Lag .Lag.Hash}}{{$lagHash = .Lag.Hash}}{{end}}
LACP mode{{template "field-info" (index $d "lacp-mode")}} {{$lacpMode := ""}}{{if and .Lag .Lag.LACP.Mode}}{{$lacpMode = .Lag.LACP.Mode}}{{end}}
LACP rate{{template "field-info" (index $d "lacp-rate")}} {{$lacpRate := ""}}{{if and .Lag .Lag.LACP.Rate}}{{$lacpRate = .Lag.LACP.Rate}}{{end}}
System priority{{template "field-info" (index $d "lacp-sysprio")}}
{{end}}{{/* IsLag */}} {{/* key-detail-body */}}
{{/* ── Add Interface modal dialog ─────────────────────────────────────── One
, multiple per-type
. The type pulldown enables exactly one fieldset so only its inputs submit. Bridge type adds a vlan-filtering radio (default plain). Ethernet uses a datalist-backed input that doubles as a "Restore deleted interface" picker for free-form names and known unconfigured interfaces alike. Unsupported types share one panel pointing to Advanced YANG tree. */}}

Add Interface

Interface Type{{template "field-info" (index $d "type")}}
{{/* Bridge */}} {{/* LAG */}} {{/* VLAN — name auto-built from . via JS as the user changes either input; user can still override by typing. */}} {{/* Ethernet — re-bind a previously deleted physical interface. Ethernet names are tied to detected hardware, so this is a plain {{if .UnconfiguredPhysical}} {{range .UnconfiguredPhysical}}{{end}} {{else}} {{end}}
{{/* VETH */}} {{/* Dummy */}} {{/* Loopback */}} {{/* GRE — point-to-point tunnel. tunnel-common requires local and remote IPs of the same family. */}} {{/* GRETAP — like GRE but carries Ethernet frames. Same backend container (infix-interfaces:gre); YANG when-clause discriminates by if:type. */}} {{/* VXLAN — VNI mandatory (0..16777215); UDP port defaults to 4789. */}} {{/* WireGuard — private-key references a keystore asymmetric-key by name. The YANG `must` clause additionally enforces X25519 format at commit; we expose all asym keys here and let the backend validate. Inline keystore key creation is a Phase 4 follow-up (same keystore-integration work as the WiFi PSK picker). */}} {{/* WiFi — radio reference is mandatory; mode (Station / AP) drives which security mode options apply and whether the PSK picker shows. AP-only fields (hidden checkbox + AP security modes) are swapped in by JS based on the mode radio. */}} {{/* Unsupported types fall here: "other" only now. */}}
{{template "configure-toolbar" .}} {{end}}{{/* else schema ready */}} {{end}} {{define "field-info"}}{{if .}} {{end}}{{end}}