@@ -199,109 +199,47 @@
} ) [ k ] || ( k || 'Unknown' ) ;
}
// v0.7.0 : the Boot rules card — ordered first-match-wins rules
// (MAC prefix / architecture → target) plus the optional
// boot-decision webhook. S aved as one config because rule order
// matters. With no rules and no webhook, behavior is identical to
// before the feature existed .
function bootRulesCard ( cfg , targetOptions ) {
const archChoices = [
[ '' , 'any arch' ] , [ 'bios' , 'BIOS' ] , [ 'uefi-x64' , 'UEFI x64' ] ,
[ 'uefi-ia32' , 'UEFI IA32' ] , [ 'uefi-arm64' , 'UEFI ARM64' ] ,
] ;
// v0.7.1: optional first-boot binary pin. "Auto" lets the
// escalation ladder learn per machine; pinning skips the learning
// walk entirely (e.g. a rack known to run Secure Boot → shim).
const modeChoices = [
[ '' , 'auto (learn)' ] , [ 'firmware' , 'Firmware NIC' ] ,
[ 'builtin' , 'iPXE drivers' ] , [ 'shim' , 'Secure Boot (shim)' ] ,
] ;
const rules = ( cfg . rules || [ ] ) . map ( r => Object . assign ( { } , r ) ) ;
const tbody = el ( 'tbody' , { } ) ;
const msg = el ( 'div' , { class : 'msg' } ) ;
const webhookInput = el ( 'input' , { type : 'text' , spellcheck : 'false' ,
placeholder : 'http://automation.example/boot-decision (optional)' ,
value : cfg . webhook _url || '' } ) ;
const targetSelect = ( val ) => el ( 'select' , { } ,
[ el ( 'option' , { value : '' } , '— target —' ) ]
. concat ( targetOptions . map ( t =>
el ( 'option' , Object . assign ( { value : t . id } , t . id === val ? { selected : '' } : { } ) , t . title ) ) ) ) ;
const redraw = ( ) => {
tbody . innerHTML = '' ;
if ( ! rules . length ) {
tbody . appendChild ( el ( 'tr' , { } , el ( 'td' , { colspan : '7' , class : 'empty' , style : 'padding:14px' } ,
'No rules. Add one to route whole groups of machines (an OUI, an architecture) to a target — or to pin a boot binary (e.g. Secure Boot racks → shim, zero failed cycles).' ) ) ) ;
}
rules . forEach ( ( r , i ) => {
const macIn = el ( 'input' , { type : 'text' , spellcheck : 'false' , placeholder : 'aa:bb:cc (prefix)' ,
value : r . mac _prefix || '' , oninput : e => { r . mac _prefix = e . target . value ; } } ) ;
const archSel = el ( 'select' , { onchange : e => { r . arch = e . target . value ; } } ,
archChoices . map ( ( [ v , label ] ) =>
el ( 'option' , Object . assign ( { value : v } , v === ( r . arch || '' ) ? { selected : '' } : { } ) , label ) ) ) ;
const tgtSel = targetSelect ( r . target || '' ) ;
tgtSel . onchange = e => { r . target = e . target . value ; } ;
const modeSel = el ( 'select' , { onchange : e => { r . driver _mode = e . target . value ; } } ,
modeChoices . map ( ( [ v , label ] ) =>
el ( 'option' , Object . assign ( { value : v } , v === ( r . driver _mode || '' ) ? { selected : '' } : { } ) , label ) ) ) ;
const noteIn = el ( 'input' , { type : 'text' , placeholder : 'note' ,
value : r . note || '' , oninput : e => { r . note = e . target . value ; } } ) ;
const enabled = el ( 'input' , { type : 'checkbox' , onchange : e => { r . enabled = e . target . checked ; } } ) ;
enabled . checked = r . enabled !== false ;
tbody . appendChild ( el ( 'tr' , { } , [
el ( 'td' , { } , macIn ) ,
el ( 'td' , { } , archSel ) ,
el ( 'td' , { } , tgtSel ) ,
el ( 'td' , { } , modeSel ) ,
el ( 'td' , { } , noteIn ) ,
el ( 'td' , { style : 'text-align:center' } , enabled ) ,
el ( 'td' , { style : 'text-align:right' } ,
el ( 'button' , { class : 'danger' , onclick : ( ) => { rules . splice ( i , 1 ) ; redraw ( ) ; } } , '✕' ) ) ,
] ) ) ;
} ) ;
// v0.7.2 : compact read-out of saved group rules — created from the
// unified "Pin MAC" form on the Hosts tab (a prefix or an architecture
// there s aves a rule instead of a pin). First match wins, top to
// bottom. The boot-decision webhook remains available via the API
// (/api/boot-rules `webhook_url`) but no longer has a UI knob .
function groupRulesCard ( cfg , targetOptions ) {
const rules = ( cfg && cfg . rules ) || [ ] ;
if ( ! rules . length ) return null ;
const titleFor = id => {
const t = targetOptions . find ( x => x . id === id ) ;
return t ? t . title : id ;
} ;
redraw ( ) ;
const addBtn = el ( 'button ' , { class : 'ghost' , onclick : ( ) => {
rules . push ( { mac _prefix : '' , arch : ' ', target : '' , enabled : true , note : ' '} ) ;
redraw ( ) ;
} } , '+ Add rule' ) ;
const saveBtn = el ( 'butto n' , { onclick : async ( ) => {
// A rule needs at least one effect: a target or a boot-binary pin.
const bad = rules . find ( r => r . enabled !== false && ! r . target && ! r . driver _mode ) ;
if ( bad ) { msg . textContent = 'Every enabled rule needs a target or a boot-binary pin.' ; msg . className = 'msg err' ; return ; }
const r = await putJSON ( '/api/boot-rules' , { rules , webhook _url : webhookInput . value . trim ( ) } ) ;
if ( r . ok ) { msg . textContent = 'Saved.' ; msg . className = 'msg ok' ; }
else { msg . textContent = 'Save failed: ' + await r . text ( ) ; msg . className = 'msg err' ; }
} } , 'Save rules' ) ;
const modeLabel = { firmware : 'Firmware NIC' , builtin : 'iPXE drivers' , shim : 'Secure Boot (shim)' } ;
const rows = rules . map ( ( r , i ) => el ( 'tr' , r . enabled === false ? { style : 'opacity:.5' } : { } , [
el ( 'td ' , { class : 'mono' } , r . mac _prefix || el ( 'span' , { class : 'tag' } , 'any MAC' ) ) ,
el ( 'td' , { } , r . arch || el ( 'span ', { class : 'tag' } , 'any arch ') ) ,
el ( 'td' , { } , r . target ? titleFor ( r . target ) : el ( 'span' , { class : 'tag' } , '—' ) ) ,
el ( 'td' , { } , r . driver _mode
? el ( 'spa n' , { class : 'tag accent' } , modeLabel [ r . driver _mode ] || r . driver _mode )
: el ( 'span' , { class : 'tag' } , 'auto' ) ) ,
el ( 'td' , { } , r . note || '' ) ,
el ( 'td' , { style : 'text-align:right' } ,
el ( 'button' , { class : 'danger' , onclick : async ( ) => {
if ( ! confirm ( 'Remove this group rule?' ) ) return ;
const fresh = await getJSON ( '/api/boot-rules' ) . catch ( ( ) => ( { rules : [ ] , webhook _url : '' } ) ) ;
( fresh . rules = fresh . rules || [ ] ) . splice ( i , 1 ) ;
await putJSON ( '/api/boot-rules' , fresh ) ;
render ( 'hosts' ) ;
} } , 'Remove' ) ) ,
] ) ) ;
return el ( 'div' , { class : 'card' } , [
el ( 'header' , { } , [
el ( 'h2' , { } , 'Boot rules' ) ,
el ( 'h2' , { } , 'Group rules' ) ,
el ( 'span' , { class : 'sub' } , 'first match wins · checked top to bottom' ) ,
] ) ,
el ( 'div' , { class : 'body' } , [
el ( 'table' , { } , [
el ( 'thead' , { } , el ( 'tr' , { } , [
el ( 'th' , { } , 'MAC prefix' ) , el ( 'th' , { } , 'Arch' ) , el ( 'th' , { } , 'Target' ) ,
el ( 'th' , { } , 'Boot binary' ) , el ( 'th' , { } , 'Note' ) , el ( 'th' , { } , 'On' ) , el ( 'th' , { } , '' ) ,
el ( 'th' , { } , 'Boot binary' ) , el ( 'th' , { } , 'Note' ) , el ( 'th' , { } , '' ) ,
] ) ) ,
tbody ,
] ) ,
el ( 'div' , { style : 'margin-top:12px' } , [ addBtn , saveBtn ] ) ,
el ( 'label' , { class : 'field' , style : 'margin-top:16px;display:block' } , [
el ( 'span' , { class : 'name' } , 'Boot-decision webhook (optional)' ) ,
webhookInput ,
el ( 'span' , { class : 'hint' } ,
'When no pin or rule matches, OpenPXE GETs this URL with ?mac=…&arch=… ' +
'A 200 reply of {"target": "<entry-id>"} chains to that target; anything ' +
'else (404, timeout, error) falls through to the menu — a dead endpoint ' +
'can never block PXE.' ) ,
] ) ,
msg ,
el ( 'p' , { class : 'msg' , style : 'margin-top:10px' } ,
'Decision order per boot: exact MAC pin → first matching rule → webhook → interactive menu.' ) ,
el ( 'tbody' , { } , rows ) ,
] ) ,
] ) ;
}
@@ -507,6 +445,12 @@
el ( 'div' , { class : 'v' } , net . gateway || '?' ) ,
el ( 'div' , { class : 'k' } , 'Public base URL' ) ,
el ( 'div' , { class : 'v' } , net . public _base _url ) ,
// v0.7.2: physical link details (operstate · speed · duplex ·
// port MAC) so the operator can confirm WHICH port answers PXE
// in multi-NIC / trunked environments. Kept last — the joined
// value runs long, so it wraps cleanly at the bottom of the list.
el ( 'div' , { class : 'k' } , 'Link' ) ,
el ( 'div' , { class : 'v' } , net . nic _link || '—' ) ,
] ) ,
el ( 'p' , { class : 'msg' } ,
'Server IP, NIC, mask, and gateway are auto-detected at startup. ' +
@@ -896,6 +840,12 @@
render ( 'storage' ) ;
} ;
// v0.7.2: searchable haystack for the list filter — filename,
// detected family, category, and source all match.
const searchText = [
i . filename , familyLabel ( i . introspection . family ) , i . category || '' ,
isSmb ? 'smb' : isNfs ? 'nfs' : 'local' , i . id ,
] . join ( ' ' ) . toLowerCase ( ) ;
const tr = el ( 'tr' , b . ok ? { } : { class : 'unbootable' } , [
el ( 'td' , { } , [
el ( 'div' , { style : 'display:flex;align-items:center;gap:8px' } , [
@@ -940,8 +890,24 @@
} } , 'Remove' ) ,
] ) ,
] ) ;
tr . dataset . search = searchText ;
rowsAndEditors . push ( tr , editorRow ) ;
} ) ;
// v0.7.2: client-side filter over the image table. Rows travel in
// (row, password-editor) pairs; filtering hides both, and an open
// editor stays closed for filtered-out rows.
const isoSearch = el ( 'input' , { type : 'search' , placeholder : 'Filter images by name, type, or source' ,
spellcheck : 'false' , oninput : ( ) => {
const q = isoSearch . value . trim ( ) . toLowerCase ( ) ;
for ( let k = 0 ; k + 1 < rowsAndEditors . length ; k += 2 ) {
const row = rowsAndEditors [ k ] ;
const editor = rowsAndEditors [ k + 1 ] ;
const show = ! q || ( row . dataset . search || '' ) . includes ( q ) ;
row . style . display = show ? '' : 'none' ;
if ( ! show ) editor . style . display = 'none' ;
}
} } ) ;
const isoTable = isos . length
? el ( 'table' , { } , [
el ( 'thead' , { } , el ( 'tr' , { } , [
@@ -1279,7 +1245,10 @@
unattFile . onchange = ( ) => { if ( unattFile . files [ 0 ] ) uploadUnattended ( unattFile . files [ 0 ] ) ; } ;
const unattRows = unattendedFiles . length
? unattendedFiles . map ( f => el ( 'div' , { class : 'nfs-row' } , [
? unattendedFiles . map ( f => el ( 'div' , {
class : 'nfs-row' ,
'data-search' : ( f . filename + ' ' + unattendedKindLabel ( f . kind ) + ' ' + f . id ) . toLowerCase ( ) ,
} , [
el ( 'span' , { class : 'dot ok' } ) ,
el ( 'div' , { } , [
el ( 'div' , { class : 'id' } , [
@@ -1307,6 +1276,17 @@
] ) ,
el ( 'div' , { class : 'body' } , [
unattDrop , unattFile , unattMsg ,
// v0.7.2: filter for big answer-file libraries.
unattendedFiles . length > 1 ? ( ( ) => {
const search = el ( 'input' , { type : 'search' , placeholder : 'Filter files by name or kind' ,
spellcheck : 'false' , oninput : ( ) => {
const q = search . value . trim ( ) . toLowerCase ( ) ;
unattRows . forEach ( r => {
r . style . display = ( ! q || ( r . dataset . search || '' ) . includes ( q ) ) ? '' : 'none' ;
} ) ;
} } ) ;
return el ( 'label' , { class : 'field' , style : 'margin-top:14px;margin-bottom:0' } , search ) ;
} ) ( ) : null ,
el ( 'div' , { style : 'margin-top:16px;display:grid;gap:8px' } , unattRows ) ,
el ( 'p' , { class : 'msg' , style : 'margin-top:14px' } ,
'These answer files drive unattended installs. Attach one to a ' +
@@ -1353,6 +1333,9 @@
el ( 'h2' , { } , 'Available images' ) ,
el ( 'span' , { class : 'sub' } , isos . length + ' image' + ( isos . length === 1 ? '' : 's' ) ) ,
] ) ,
isos . length > 1
? el ( 'div' , { class : 'list-search' } , el ( 'label' , { class : 'field' , style : 'margin-bottom:0' } , isoSearch ) )
: null ,
isoTable ,
] ) ,
] ) , unattendedAdvanced ] ) ;
@@ -1378,8 +1361,22 @@
{ id : '_tools_menu' , title : '↳ Tools menu (built-in)' } ,
] ;
const macInput = el ( 'input' , { type : 'text' , placeholder : 'aa:bb:cc:dd:ee:ff' , spellcheck : 'false' } ) ;
const macInput = el ( 'input' , { type : 'text' , placeholder : 'aa:bb:cc:dd:ee:ff or aa:bb:cc ' , spellcheck : 'false' } ) ;
const labelInput = el ( 'input' , { type : 'text' , placeholder : 'optional, e.g. "rack-3 spine"' } ) ;
// v0.7.2: the former separate "Boot rules" card folded into this
// form. A full MAC with no architecture saves a per-host pin
// exactly as before; a MAC *prefix* and/or an architecture saves a
// first-match-wins group rule instead. Same form, one mental model.
const archSel = el ( 'select' , { } , [
[ '' , 'any (this exact MAC)' ] , [ 'bios' , 'BIOS' ] , [ 'uefi-x64' , 'UEFI x64' ] ,
[ 'uefi-ia32' , 'UEFI IA32' ] , [ 'uefi-arm64' , 'UEFI ARM64' ] ,
] . map ( ( [ v , t ] ) => el ( 'option' , { value : v } , t ) ) ) ;
// v0.7.1's boot-binary pin keeps its home here too (auto = let the
// escalation ladder learn; shim = known Secure Boot fleet).
const binSel = el ( 'select' , { } , [
[ '' , 'auto (learn per machine)' ] , [ 'firmware' , 'Firmware NIC' ] ,
[ 'builtin' , 'iPXE drivers' ] , [ 'shim' , 'Secure Boot (shim)' ] ,
] . map ( ( [ v , t ] ) => el ( 'option' , { value : v } , t ) ) ) ;
const targetSel = el ( 'select' , { } ,
[ el ( 'option' , { value : '' } , '— choose a target —' ) ]
. concat ( reserved . map ( t => el ( 'option' , { value : t . id } , t . title ) ) )
@@ -1392,21 +1389,40 @@
// hostname/IP templated into the served answer file.
const profileFields = buildProfileFields ( { } , unattendedFiles , 'form-row cols-3' ) ;
const FULL _MAC = /^([0-9a-f]{2}[:-]){5}[0-9a-f]{2}$/i ;
const upsertBtn = el ( 'button' , { onclick : async ( ) => {
if ( ! macInput . value || ! targetSel . value ) {
const mac = macInput . value . trim ( ) ;
const isGroup = ! ! archSel . value || ! ! binSel . value || ( mac !== '' && ! FULL _MAC . test ( mac ) ) ;
if ( ! isGroup ) {
// Exact-MAC pin — unchanged behavior.
if ( ! mac || ! targetSel . value ) {
msg . textContent = 'MAC and target are required.' ; msg . className = 'msg err' ; return ;
}
const r = await postJSON ( '/api/hosts' , Object . assign ( {
mac : macInput . value , target : targetSel . value , label : labelInput . value ,
mac , target : targetSel . value , label : labelInput . value ,
} , profileFields . read ( ) ) ) ;
if ( r . ok ) {
msg . textContent = 'Saved.' ; msg . className = 'msg ok ' ;
render ( 'hosts' ) ;
} else {
const t = await r . text ( ) ;
msg . textContent = 'Save failed: ' + t ; msg . className = 'msg err' ;
if ( r . ok ) { msg . textContent = 'Saved.' ; msg . className = 'msg ok' ; render ( 'hosts' ) ; }
else { msg . textContent = 'Save failed: ' + await r . text ( ) ; msg . className = 'msg err ' ; }
return ;
}
} } , 'Bind MAC to target' ) ;
// Group rule (prefix and/or architecture). Per-host profile
// fields don't apply to a group — they're per-machine values.
if ( ! targetSel . value && ! binSel . value ) {
msg . textContent = 'A group rule needs a target or a boot binary.' ; msg . className = 'msg err' ; return ;
}
const p = profileFields . read ( ) ;
if ( p . auto _hostname || p . auto _ip || p . unattended _file ) {
msg . textContent = 'Auto-deploy fields are per-machine — clear them, or use a full MAC.' ; msg . className = 'msg err' ; return ;
}
const cfg = await getJSON ( '/api/boot-rules' ) . catch ( ( ) => ( { rules : [ ] , webhook _url : '' } ) ) ;
( cfg . rules = cfg . rules || [ ] ) . push ( {
mac _prefix : mac , arch : archSel . value , target : targetSel . value ,
driver _mode : binSel . value , enabled : true , note : labelInput . value ,
} ) ;
const r = await putJSON ( '/api/boot-rules' , cfg ) ;
if ( r . ok ) { msg . textContent = 'Group rule saved.' ; msg . className = 'msg ok' ; render ( 'hosts' ) ; }
else { msg . textContent = 'Save failed: ' + await r . text ( ) ; msg . className = 'msg err' ; }
} } , 'Bind to target' ) ;
const rows = hosts . map ( h => {
// v0.5.0: Wake-on-LAN. Only shown for bound hosts (this whole
@@ -1463,23 +1479,32 @@
el ( 'div' , { class : 'card' } , [
el ( 'header' , { } , el ( 'h2' , { } , 'Pin MAC to boot target' ) ) ,
el ( 'div' , { class : 'body' } , [
// v0.7.3: MAC · Label · Architecture · Boot binary share one
// 4-up row so the controls line up across the page; the
// per-field guidance that used to sit under them moved into the
// note below to keep the inputs flush. Target spans full width
// on its own line beneath them.
el ( 'div' , { class : 'form-row' } , [
el ( 'label' , { class : 'field' } , [ el ( 'span' , { class : 'name' } , 'MAC address' ) , macInput ] ) ,
el ( 'label' , { class : 'field' } , [ el ( 'span' , { class : 'name' } , 'MAC address or prefix ' ) , macInput ] ) ,
el ( 'label' , { class : 'field' } , [ el ( 'span' , { class : 'name' } , 'Label (optional)' ) , labelInput ] ) ,
el ( 'label' , { class : 'field' , style : 'grid-column:1 / -1 '} , [
el ( 'label' , { class : 'field' } , [ el ( 'span' , { class : 'name' } , 'Architecture (optional) ') , archSel ] ) ,
el ( 'label' , { class : 'field' } , [ el ( 'span' , { class : 'name' } , 'Boot binary (optional)' ) , binSel ] ) ,
] ) ,
el ( 'label' , { class : 'field' , style : 'margin-top:14px' } , [
el ( 'span' , { class : 'name' } , 'Target' ) ,
targetSel ,
el ( 'span' , { class : 'hint' } ,
'Built-in shortcuts skip the menu entirely. Per-ISO entries chain straight to the boot script.' ) ,
] ) ,
] ) ,
el ( 'div' , { style : 'margin-top:16px' } , profileFields . wrap ) ,
upsertBtn , msg ,
el ( 'p' , { class : 'msg' , style : 'margin-top:14px' } ,
'When a client with a bound MAC requests boot.ipxe, OpenPXE ' +
'short-circuits past the interactive menu and chains directly. ' +
'If an unattended file is selected, the matching kernel argum ent ' +
'is injected and the hostname/IP are templated into the answer file.' ) ,
'A full MAC pins one machine; a MAC p refix (OUI) or an architecture ' +
'saves a first-match group rule. Pin the boot binary to “shim” for ' +
'Secure Boot racks — zero fa iled boot cycles. When a matching cli ent ' +
'requests boot.ipxe, OpenPXE short-circuits past the interactive menu ' +
'and chains directly; decision order is exact MAC pin → first matching ' +
'group rule → menu. If an unattended file is selected on a pin, the ' +
'matching kernel argument is injected and the hostname/IP are templated ' +
'into the answer file.' ) ,
] ) ,
] ) ,
el ( 'div' , { class : 'card' } , [
@@ -1489,7 +1514,7 @@
] ) ,
table ,
] ) ,
bootRulesCard ( rulesCfg , reserved . concat ( targets ) ) ,
groupRulesCard ( rulesCfg , reserved . concat ( targets ) ) ,
el ( 'div' , { class : 'card' } , [
el ( 'header' , { } , [
el ( 'h2' , { } , 'Host log' ) ,
@@ -2166,9 +2191,24 @@
el ( 'div' , { class : 'about-hero' } , [
el ( 'h2' , { } , 'OpenPXE' ) ,
el ( 'p' , { class : 'lead' } ,
'Air-gapped network PXE boot, container-native, that anyone can run. ' +
'No CDN calls, no telemetry, no surprise external dependencies — ship ' +
'the image once, run it forever.' ) ,
'The network-boot platform for modern infrastructure. Drop in an ISO ' +
'and every machine on your network — BIOS, UEFI, Secure Boot — can ' +
'boot it, image from it, and install unattended. One container, one ' +
'static binary, nothing installed on clients, nothing leaving your network.' ) ,
el ( 'div' , { style : 'display:grid;grid-template-columns:repeat(3,1fr);gap:14px;margin:18px 0' } , [
[ 'Boot anything' , 'Linux, Windows, hypervisors, rescue tools — uploaded ' +
'ISOs become menu entries automatically, served on demand from local ' +
'disk or your existing NFS, SMB, or SFTP libraries.' ] ,
[ 'Adapt to every machine' , 'Per-machine boot intelligence: firmware quirks, ' +
'NIC driver fallback, and a Microsoft-signed Secure Boot chain are ' +
'negotiated automatically and remembered — no toggles, no client prep.' ] ,
[ 'Run it in production' , 'SAML single sign-on, token-scoped answer files, ' +
'fleet routing rules, Wake-on-LAN, queued mass deployment, Prometheus ' +
'metrics. Built in Rust for boot infrastructure that cannot flinch.' ] ,
] . map ( ( [ h , body ] ) => el ( 'div' , { } , [
el ( 'h3' , { style : 'margin:0 0 6px;font-size:13.5px' } , h ) ,
el ( 'p' , { class : 'msg' , style : 'font-size:12px;margin:0' } , body ) ,
] ) ) ) ,
el ( 'div' , { class : 'who' } , [
el ( 'span' , { } , 'Developer: ' ) , el ( 'strong' , { } , 'Miles Ward' ) , el ( 'br' ) ,
el ( 'span' , { } , 'Version: ' ) , el ( 'strong' , { } , status . version || '?' ) , el ( 'br' ) ,
@@ -2179,15 +2219,16 @@
] ) ,
el ( 'div' , { style : 'margin-top:18px' } , [ updBtn , updMsg ] ) ,
el ( 'p' , { class : 'msg' , style : 'margin-top:18px' } ,
'iPXE is an internal implementation detail. Everything the firmwar e ' +
'executes is generated from the settings on these tabs — there is no ' +
'hand-written .ipxe path anywhere in this product.' ) ,
'Private by design: no telemetry, no CDN calls, no runtim e ' +
'dependencies on the outside world. Air-gapped labs, customer sites ' +
'without internet, and locked-down OpenShift clusters run the same ' +
'image, the same way, indefinitely.' ) ,
el ( 'p' , { class : 'msg' } ,
'Vision: a deployment-grade tool that wor ks o n first try in the most ' +
'awkward environments — air-gapped labs, customer sites without ' +
'internet, OpenShift clus ter s with strict SCCs — without ever asking ' +
'an operator to install drivers signed with test certificates or to ' +
'flip "testsigning" on a target machine .' ) ,
'Principled by default: OpenPXE never as ks a n operator to install ' +
'test-signed drivers, modify a client’ s trust store, or weaken ' +
'Secure Boot. Everything the firmware execu tes is generated from the ' +
'settings on these tabs — there are no hand-written boot scripts to ' +
'maintain and no internals to learn .' ) ,
] ) ,
] ) ;