$('device-rows').replaceChildren();for(constdofdata.devices){constrow=document.createElement('tr');row.dataset.access=d.revoked?'revoked':'active';cell(row,d.name);cell(row,d.platform);cell(row,d.user);cell(row,d.policy_version);cell(row,newDate(d.last_seen).toLocaleString());constaction=cell(row,d.revoked?'Revoked':'');if(!d.revoked){constb=document.createElement('button');b.className='danger';b.textContent='Revoke';b.onclick=task(async()=>{awaitapi(`/api/admin/devices/${encodeURIComponent(d.id)}/revoke`,{});awaitload();status('Device revoked. Client access stops at its next management refresh.');});action.append(b);constscan=document.createElement('button');scan.className='secondary';scan.textContent=d.documentAnalysisEnabled?'Disable manual document scans':'Enable manual document scans';scan.onclick=task(async()=>{constresult=awaitapi(`/api/admin/devices/${encodeURIComponent(d.id)}/inspection`,{expectedVersion:policy.version,documentAnalysisEnabled:!d.documentAnalysisEnabled},'PATCH');awaitload();policyMutationStatus(result,'Device document analysis setting published. Mandatory DLP and YARA remain enforced.');});action.append(scan);}$('device-rows').append(row);}
$('event-rows').replaceChildren();for(consteofdata.events){constrow=document.createElement('tr');row.dataset.decision=e.metadata.decision||(e.kind.includes('block')?'block':'');cell(row,newDate(e.time).toLocaleString());cell(row,e.kind);cell(row,e.device_id?.slice(0,8)||'Control plane');constmetadataCell=cell(row,JSON.stringify(e.metadata));consttriage=document.createElement('button');triage.className='secondary';triage.textContent='Create incident';triage.dataset.incidentCreate='true';triage.hidden=e.kind.startsWith('incident.');triage.onclick=task(async()=>{constcreated=awaitapi('/api/admin/incidents',{eventId:Number(e.id),severity:'medium'});awaitloadIncidents();awaitopenIncident(created.incident?.id||created.id);$('incidents').scrollIntoView({behavior:'smooth'});status('Incident created for investigation.');});metadataCell.append(triage);$('event-rows').append(row);}
filterTable('device');filterTable('event');awaitloadTeaching();awaitloadIndependentReviews();awaitloadPolicyGroups();awaitloadUserProfiles();awaitloadIncidents();status(data.migrationRequired?'Legacy semantic policy remains blocked. Recreate references with the approved local model and explicitly publish a replacement policy.':'Connected. Policy and fleet loaded.',data.migrationRequired);
$('channel-key-download').onclick=task(async()=>{constchannel=awaitapi('/api/admin/management-channel');constlink=document.createElement('a'),url=URL.createObjectURL(newBlob([channel.signatureKey],{type:'application/x-pem-file'}));link.href=url;link.download='safetynet-management-mldsa87-public.pem';link.click();setTimeout(()=>URL.revokeObjectURL(url),1000);status('Public management signing key downloaded. Deploy through your trusted device-management channel.');});
$('add-rule').onclick=task(async()=>{constlabel=$('rule-label').value.trim(),value=$('rule-value').value.trim();if(!label||!value)throwError('Enter a rule name and phrase');policy.dlp.rules.push({id:crypto.randomUUID(),label,kind:'literal',value,action:'block'});renderRules();$('rule-value').value='';status('Rule added to draft. Publish to apply it.');});
$('fingerprint').onchange=task(async()=>{constfile=$('fingerprint').files[0];if(!file)return;if(file.size>20*1024*1024)throwError('Fingerprint input limit is 20 MB');consthash=awaitcrypto.subtle.digest('SHA-256',awaitfile.arrayBuffer());constvalue=Array.from(newUint8Array(hash),b=>b.toString(16).padStart(2,'0')).join('');policy.dlp.rules.push({id:crypto.randomUUID(),label:file.name,kind:'sha256',value,action:'block'});renderRules();$('fingerprint').value='';status('Fingerprint added to draft. Publish to apply it.');});
if(policy.dlp.semantic.categories.length&&['revision','model','profile','artifactFormat','quantization','runtimeRevision'].some(key=>policy.dlp.semantic[key]!==reference[key]))throwError('Model revision or profile changed; recreate existing references before publishing');
for(constkeyof['model','revision','dimensions','profile','artifactFormat','quantization','runtimeRevision'])policy.dlp.semantic[key]=reference[key];policy.dlp.semantic.categories.push({id:crypto.randomUUID(),label,threshold,prototype:reference.vector,prototypes:reference.vectors});renderSemantic();status(`Local semantic reference added (${reference.coverage.textChunks} chunks, ${reference.dimensions} dimensions). Calibrate and explicitly publish to apply.`);
}
$('semantic-add').onclick=task(async()=>{
constlabel=$('semantic-label').value.trim(),threshold=Number($('semantic-threshold').value);if(!label||threshold<=0||threshold>1)throwError('Enter a category name and a threshold greater than 0 and at most 1');
constfile=$('semantic-document').files[0],label=$('semantic-label').value.trim(),threshold=Number($('semantic-threshold').value);if(!file||file.size>1048576)throwError('Choose a local reference document up to 1 MiB');if(!label||threshold<=0||threshold>1)throwError('Enter a category name and threshold greater than 0 and at most 1');constbytes=newUint8Array(awaitfile.arrayBuffer());letraw='';for(letn=0;n<bytes.length;n+=8192)raw+=String.fromCharCode(...bytes.subarray(n,n+8192));constreference=awaitapi('/api/admin/semantic/document-reference',{data:btoa(raw),filename:file.name});addSemanticReference(reference,label,threshold);$('semantic-document').value='';
load().catch(()=>status('Sign in to manage your browser fleet.'));
asyncfunctionloadTeaching(){
constdata=awaitapi('/api/admin/teaching');$('teach-drafts').replaceChildren();for(constdofdata.drafts){constrow=document.createElement('div');row.className='rule';constlabel=document.createElement('span');label.textContent=`${d.label} · ${d.status} · ${d.reference.registrationReady?'inspection complete':'partial coverage: unextracted media is not fingerprinted'}`;row.append(label);if(d.status==='draft'){constbutton=document.createElement('button');button.textContent=independentApprovalRequired?(d.reference.registrationReady?'Prepare fingerprints for independent approval':'Prepare available fingerprints (partial coverage)'):(d.reference.registrationReady?'Approve and publish fingerprints':'Approve available fingerprints (partial coverage)');button.onclick=task(async()=>{constresult=awaitapi(`/api/admin/teaching/${encodeURIComponent(d.id)}/publish`,{expectedVersion:policy.version,approve:true,mode:d.reference.registrationReady?'complete':'available-fingerprints'});awaitload();policyMutationStatus(result,`Reference approved and signed policy published; coverage ${result.coverage}.`);});row.append(button);}$('teach-drafts').append(row);}
}
$('teach-submit').onclick=task(async()=>{constfile=$('teach-file').files[0];if(!file||file.size>1024*1024)throwError('Choose a reference file up to 1 MB');constbytes=newUint8Array(awaitfile.arrayBuffer());letraw='';for(letn=0;n<bytes.length;n+=8192)raw+=String.fromCharCode(...bytes.subarray(n,n+8192));awaitapi('/api/admin/teaching',{label:$('teach-label').value,data:btoa(raw),filename:file.name});awaitloadTeaching();status('Reference draft created. Review and approve to publish.');});
$('visual-add').onclick=task(async()=>{
constfile=$('visual-image').files[0];if(!file||file.size>1024*1024)throwError('Choose a trusted PNG reference up to 1 MB');constbytes=newUint8Array(awaitfile.arrayBuffer());letraw='';for(letn=0;n<bytes.length;n+=8192)raw+=String.fromCharCode(...bytes.subarray(n,n+8192));
constsecurity=JSON.parse($('security-json').value),brands=security.visualPhishing.brands,current=brands.find(b=>b.id===result.brand.id);if(current){if(current.references.length>=20)throwError('Each brand supports at most 20 references');current.references.push(result.brand.references[0]);}elsebrands.push(result.brand);$('security-json').value=JSON.stringify(security,null,2);$('visual-image').value='';status(result.warning);
$('preset-preview').onclick=task(async()=>{constresult=awaitapi('/api/admin/presets/preview',{id:$('preset-id').value,policy:draft()});result.sourceDraft=JSON.stringify(draft());presetPreview=result;$('preset-description').textContent=`${result.preset.description}${result.warning}`;$('preset-changes').replaceChildren();for(constchangeofresult.changes){constrow=document.createElement('div');row.className='rule';row.textContent=`${change.field}: ${JSON.stringify(change.before)} → ${JSON.stringify(change.after)}`;$('preset-changes').append(row);}if(!result.changes.length)$('preset-changes').textContent='No changes to existing policy controls.';renderReadiness(result.readiness);$('preset-apply').disabled=false;status('Template preview created. Nothing has been published.');});
$('preset-apply').onclick=task(async()=>{if(!presetPreview)throwError('Preview a template first');if(JSON.stringify(draft())!==presetPreview.sourceDraft)throwError('Draft changed after preview; preview the template again before applying');constpreview=presetPreview;policy=preview.policy;policy.version-=1;fill(policy);renderReadiness(preview.readiness);status('Template added to draft. Review controls and publish the signed policy to deploy.');});
$('enterprise-icap-probe').onclick=task(async()=>{constresult=awaitapi('/api/admin/integrations/icap/probe',draft().enterprise.icap);$('enterprise-icap-status').textContent=result.ready?'Management server OPTIONS check passed. Verify scanning from the deployed browser network too.':'Management server OPTIONS check failed. Check service, method, certificates and network reachability.';status('ICAP capability check completed; no company document was sent and nothing was published.');});
functionclearDeviceBookmarks(){bookmarkRevision=undefined;bookmarkDeviceId='';personalBookmarks=[];$('bookmark-device-editor').hidden=true;$('bookmark-device-status').textContent='Select a device and read its bookmarks. Published policy must permit administrator access.';}
functionaddBookmark(items,target){if(items.length>=100)throwError('A maximum of 100 bookmarks is supported');items.push({id:crypto.randomUUID(),title:'',url:'https://',folder:''});renderBookmarkRows(target,items);$(target).lastElementChild?.querySelector('input')?.focus();}
$('managed-bookmark-add').onclick=task(async()=>{addBookmark(managedBookmarks,'managed-bookmark-rows');status('Company bookmark added to draft. Complete its fields, then publish the signed policy.');});
constid=$('bookmark-device').value;if(!id)throwError('Select an active device');clearDeviceBookmarks();constdata=awaitapi(`/api/admin/devices/${encodeURIComponent(id)}/bookmarks`);if($('bookmark-device').value!==id)return;bookmarkDeviceId=id;bookmarkRevision=data.revision;personalBookmarks=structuredClone(data.items);renderBookmarkRows('personal-bookmark-rows',personalBookmarks);$('bookmark-device-editor').hidden=false;$('bookmark-device-status').textContent=`Loaded device revision ${bookmarkRevision}. Remote edits are not saved yet.`;
if(!bookmarkDeviceId||bookmarkRevision===undefined||bookmarkDeviceId!==$('bookmark-device').value)throwError('Read the selected device bookmarks first');
try{constdata=awaitapi(`/api/admin/devices/${encodeURIComponent(bookmarkDeviceId)}/bookmarks`,{expectedRevision:bookmarkRevision,policyVersion:policy.version,items:personalBookmarks},'PUT');bookmarkRevision=data.revision;personalBookmarks=structuredClone(data.items);renderBookmarkRows('personal-bookmark-rows',personalBookmarks);$('bookmark-device-status').textContent=`Saved revision ${bookmarkRevision}. The browser applies this at its next management refresh.`;status('Device bookmarks updated.');}catch(error){$('bookmark-device-status').textContent=`Save failed: ${error.message}. Refresh and review the latest revision before retrying.`;throwerror;}
});
// Console preferences affect this interface only, never managed browser policy.
functionupdateDraftStatus(){if(!policy)return;try{constcount=policyChanges().length;$('draft-state').textContent=count?`${count} unpublished policy ${count===1?'change':'changes'}`:`Published policy v${publishedSnapshot.version}`;$('draft-detail').textContent=count?'Review before publishing. Refresh discards unpublished edits.':'Changes stay in your draft until publication.';$('draft-state').classList.toggle('draft-dirty',count>0);}catch{$('draft-state').textContent='Draft requires correction';$('draft-detail').textContent='Check expiry and advanced JSON before publication.';}}
$('review-changes').onclick=task(async()=>{constchanges=policyChanges();$('change-review-list').replaceChildren();for(constchangeofchanges){constitem=document.createElement('article');item.className='change-item';consttitle=document.createElement('strong');title.textContent=change.path;constbefore=document.createElement('pre'),after=document.createElement('pre');before.textContent=`Published: ${JSON.stringify(change.before)??'unset'}`;after.textContent=`Draft: ${JSON.stringify(change.after)??'unset'}`;item.append(title,before,after);$('change-review-list').append(item);}$('change-review-summary').textContent=changes.length?`${changes.length} changed paths. Nothing has been published by this review.`:'No policy changes.';$('change-review').showModal();});$('review-close').onclick=()=>$('change-review').close();
functionfilterTable(kind){constquery=$(`${kind}-filter`).value.toLowerCase().trim(),value=$(kind==='device'?'device-access-filter':'event-decision-filter').value,rows=Array.from($(`${kind}-rows`).children);letvisible=0;for(constrowofrows){constmatch=(!query||row.textContent.toLowerCase().includes(query))&&(value==='all'||row.dataset[kind==='device'?'access':'decision']===value);row.hidden=!match;if(match)visible++;}$(`${kind}-filter-count`).textContent=`${visible} of ${rows.length}${kind==='device'?'devices':'events'}`;}
asyncfunctionloadSessions(){constdata=awaitapi('/api/admin/sessions');$('session-rows').replaceChildren();for(constsessionofdata.sessions){constrow=document.createElement('tr');cell(row,session.name===session.subject?session.subject:`${session.name} · ${session.subject}`);cell(row,session.role);cell(row,newDate(session.expiresAt).toLocaleString());constaction=cell(row,''),button=document.createElement('button');button.className='danger';button.textContent='Revoke session';button.onclick=task(async()=>{awaitapi(`/api/admin/sessions/${encodeURIComponent(session.id)}/revoke`,{});row.remove();$('sessions-status').textContent='Session revoked immediately. Identity-provider authorization may permit another sign-in.';});action.append(button);$('session-rows').append(row);}$('sessions-status').textContent=`${data.sessions.length} active management sessions${data.truncated?' · first 500 shown':''}.`;}
for(constitemofdata.versions){constrow=document.createElement('tr');cell(row,`v${item.version}${item.current?' - current':''}`);cell(row,newDate(item.time).toLocaleString());cell(row,item.actor);constaction=cell(row,''),button=document.createElement('button');button.className='secondary';button.textContent=item.current?'Review current version':'Compare with current';button.onclick=task(()=>reviewHistory(item.version));action.append(button);$('history-rows').append(row);}
historyNextBefore=data.nextBefore;$('history-more').hidden=!historyNextBefore;$('history-status').textContent=`${$('history-rows').children.length} persisted versions shown. Current signed policy: v${data.currentVersion}.`;
clearHistoryReview();constcurrent=awaitapi('/api/admin/policy/history?limit=1');if(current.currentVersion!==publishedSnapshot.version)throwError('Current policy changed. Refresh the workspace before comparing or restoring.');
constcomparison=awaitapi(`/api/admin/policy/diff?from=${version}&to=${current.currentVersion}`);if(!Array.isArray(comparison.changes)||!Array.isArray(comparison.groupDiff?.changes)||!Array.isArray(comparison.profileDiff?.changes))throwError('Complete baseline, group and user-profile differences are required before restoration. Refresh and compare again.');if(comparison.truncated||comparison.groupDiff.truncated||comparison.profileDiff.truncated)throwError('Policy difference exceeds the supported review size. Review the policy through the API before restoration.');
historyReview={sourceVersion:version,expectedVersion:current.currentVersion};renderHistoryDiff([...comparison.changes,...comparison.groupDiff.changes.map(change=>({...change,path:`/policyGroups${change.path}`})),...comparison.profileDiff.changes.map(change=>({...change,path:`/userProfiles${change.path}`}))]);$('history-review-title').textContent=`Review v${version} against current v${current.currentVersion}`;$('history-review-summary').textContent=`${comparison.changes.length} baseline paths, ${comparison.groupDiff.changes.length} group paths and ${comparison.profileDiff.changes.length} user-profile/category paths changed. Review verified-subject and device assignments, website access, category definitions and developer-tool grants below. Restoration publishes v${current.currentVersion+1} with a fresh seven-day expiry. Your unpublished draft will be replaced after successful restoration.`;constgrantChanges=[...comparison.changes,...comparison.groupDiff.changes,...comparison.profileDiff.changes].some(change=>/(?:^|\/)agents\/grants(?:\/|$)/.test(change.path)||[change.before,change.after].some(value=>value&&typeofvalue==='object'&&JSON.stringify(value).includes('"grants"')));if(grantChanges)$('history-review-summary').textContent+=' Agent credential warning: each enrolled browser preserves its highest observed credential generation. Restoring an older generation does not restore invalidated credentials and leaves that grant disabled on browsers that observed a higher generation. To reactivate safely, publish a generation greater than every previously issued generation and pair the agent again locally.'+(independentApprovalRequired?' Independent approval still applies; this checkbox only prepares restoration.':'');$('history-review').hidden=false;$('history-approve').disabled=!canWritePolicy()||version===current.currentVersion;$('history-restore').disabled=true;$('history-review').scrollIntoView({block:'nearest',behavior:'smooth'});
$('history-restore').onclick=task(async()=>{if(!historyReview||!$('history-approve').checked||!canWritePolicy())throwError('Review and approve the restoration first');constselection={...historyReview};$('history-restore').disabled=true;constresult=awaitapi('/api/admin/policy/restore',{...selection,approve:true});awaitload();awaitloadHistory();policyMutationStatus(result,`Configuration from v${result.sourceVersion} restored and signed as new policy v${result.version}.`);});
// The backend controls independent approval; renderer controls provide context only.
functionpolicyMutationStatus(result,publishedMessage){status(result.reviewRequired?`Changes prepared for independent approval (${result.reviewId}). Published policy is unchanged.`:publishedMessage);}
asyncfunctionloadIndependentReviews(){constdata=awaitapi('/api/admin/policy-reviews');clearIndependentReview();$('review-mode').textContent=data.requireIndependentPolicyApproval?'Independent approval is mandatory. Publish controls prepare changes for a different administrator.':'Independent approval is optional on this deployment. Direct publication remains enabled for authorized administrators.';for(constidof['publish','publish-shortcut'])$(id).textContent=independentApprovalRequired?'Prepare for independent approval':'Publish signed policy';$('history-restore').textContent=independentApprovalRequired?'Prepare restoration for independent approval':'Restore as new signed version';$('reviews-rows').replaceChildren();for(constreviewofdata.reviews){constrow=document.createElement('tr');cell(row,newDate(review.time).toLocaleString());cell(row,review.author);cell(row,review.kind);cell(row,`v${review.expectedVersion}`);cell(row,review.status);constaction=cell(row,''),button=document.createElement('button');button.className='secondary';button.textContent='Review changes';button.onclick=task(async()=>{selectedIndependentReview=awaitapi(`/api/admin/policy-reviews/${encodeURIComponent(review.id)}`);$('independent-review-confirm').checked=false;$('independent-review-title').textContent=`${selectedIndependentReview.kind} | ${selectedIndependentReview.status}`;$('independent-review-metadata').textContent=`Author: ${selectedIndependentReview.author} | Base policy: v${selectedIndependentReview.expectedVersion} | Payload: ${selectedIndependentReview.payloadHash}${selectedIndependentReview.author===currentPrincipal.sub?' | You cannot approve your own proposal.':''}${selectedIndependentReview.status==='stale'?' | Refresh and prepare a new proposal against the current policy.':''}`;$('independent-review-operation').textContent=independentOperationDescription(selectedIndependentReview.operation);$('independent-review-diff').replaceChildren();for(constchangeofselectedIndependentReview.diff.changes){constitem=document.createElement('article');item.className='change-item';consttitle=document.createElement('strong'),before=document.createElement('pre'),after=document.createElement('pre');title.textContent=change.path;before.textContent=`Current: ${JSON.stringify(change.before)??'unset'}`;after.textContent=`Proposed: ${JSON.stringify(change.after)??'unset'}`;item.append(title,before,after);$('independent-review-diff').append(item);}if(selectedIndependentReview.diff.truncated){constwarning=document.createElement('p');warning.textContent='Diff is truncated. Approval is disabled here; review the complete proposed policy using the authenticated review API.';$('independent-review-diff').append(warning);}$('independent-review-detail').hidden=false;updateIndependentReviewActions();});action.append(button);$('reviews-rows').append(row);}$('reviews-status').textContent=`${data.reviews.length} proposals loaded.`;}
$('independent-review-approve').onclick=task(async()=>{if(!canReviewIndependent()||!$('independent-review-confirm').checked)throwError('A different administrator must explicitly review and approve');constreview=selectedIndependentReview;constresult=awaitapi(`/api/admin/policy-reviews/${encodeURIComponent(review.id)}/approve`,{payloadHash:review.payloadHash,expectedVersion:review.expectedVersion,approve:true});awaitload();status(`Independent approval complete. Signed policy v${result.version} published.`);});
$('independent-review-reject').onclick=task(async()=>{if(!canReviewIndependent())throwError('Only a different administrator may reject this proposal');constreview=selectedIndependentReview;awaitapi(`/api/admin/policy-reviews/${encodeURIComponent(review.id)}/reject`,{payloadHash:review.payloadHash,reject:true});awaitloadIndependentReviews();status('Proposal rejected. Published policy is unchanged.');});
if(operation.type==='group-upsert'||operation.type==='group-delete')return`Group operation: ${operation.type}. Exact group and rollout: ${JSON.stringify(operation)}`;
if(operation.type==='teaching')return`Document reference: ${operation.id}. Coverage: ${operation.coverage}. Publication mode: ${operation.mode}. Only derived protection features will be published.`;
if(operation.type==='group-restore')return`Restore baseline and group assignments from v${operation.sourceVersion}. Exact restored groups: ${JSON.stringify(operation.groups)}`;
if(Number.isInteger(operation.sourceVersion))return`Restore configuration from policy v${operation.sourceVersion} as a new signed version.`;