RedFlag/desktop/qml/Main.qml
Fimeg 67e26be2d9 projection: begin the exact-path public epoch
The public tree and its history contain only the listed paths. Earlier projection history remains preserved internally.

Source-Sha: 913fde029b935671833254797f0f20f1eb9fabba

Policy-Sha: 913fde029b935671833254797f0f20f1eb9fabba

Tree-Digest: 180ae530c1058a2a5c89837bdce2d323ae83e669e38590ca72e75b8d92b7262f
2026-09-08 21:59:33 -04:00

4131 lines
160 KiB
QML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import QtQuick
import QtQuick.Controls.Basic
import QtQuick.Layouts
import com.redflag.desktop 1.0
ApplicationWindow {
id: app
property string page: "overview"
readonly property bool compact: width < 1000
readonly property int pageMargin: width < 760 ? 12 : 24
property var routeTrail: []
property bool softwareDetailLoading: false
property bool softwareDetailWanted: false
property var softwareTrail: []
property var softwareTarget: ({
})
property var savedQueries: ({
})
readonly property var pageKeys: ["overview", "performance", "processes", "network", "storage", "containers", "services", "software", "updates", "security", "history"]
property var history: parse(machine.historyJson, [])
property var system: parse(machine.systemJson, {
})
property var processes: parse(machine.processesJson, [])
property var connections: parse(machine.connectionsJson, [])
property var containers: parse(machine.containersJson, [])
property var services: parse(machine.servicesJson, [])
property var updates: parse(machine.updatesJson, [])
property var scanStatus: parse(machine.statusJson, ({}))
property string updateQuery: ""
property int updateFilter: 0
readonly property var filteredUpdates: updates.filter(u => {
if (updateFilter === 1 && ["critical", "high", "important"].indexOf(u.severity) < 0) return false;
if (updateFilter === 2 && !(u.cve_list || []).length) return false;
return !updateQuery.length || [u.package_name, u.package_description, u.package_type, u.repository_source, (u.cve_list || []).join(" ")].join(" ").toLowerCase().indexOf(updateQuery.toLowerCase()) >= 0;
})
property var security: parse(machine.securityJson, {
})
property var events: parse(machine.eventsJson, [])
property var processDetail: parse(machine.processDetailJson, {
})
property var softwareSnapshot: parse(machine.softwareJson, {
})
property var software: softwareSnapshot.packages || []
property var softwareDetail: parse(machine.softwareDetailJson, {
})
property var selectedUpdate: ({
})
property string pendingProcessQuery: ""
property var approval: parse(machine.approvalJson, {
})
// The capabilities that turn "a process" into "a process that can rewrite the
// machine". Tinted so a wall of chips still reads at a glance.
readonly property var sharpCapabilities: ["CAP_SYS_ADMIN", "CAP_SYS_MODULE", "CAP_SYS_RAWIO", "CAP_SYS_PTRACE", "CAP_SYS_BOOT", "CAP_BPF", "CAP_NET_ADMIN", "CAP_NET_RAW", "CAP_DAC_READ_SEARCH", "CAP_SETUID", "CAP_SETGID"]
function installedEdge(label) {
const name = String(label).replace(/[<>=].*$/, "");
const matches = software.filter((p) => {
return (p.name === name || p.identity === name) && p.package_type === softwareDetail.package_type;
});
return matches.length === 1 ? matches[0] : null;
}
function openSoftware(target, drill) {
if (softwareDetailLoading)
return ;
softwareDetailWanted = true;
softwareTrail = drill ? softwareTrail.concat([softwareTarget]) : [];
softwareTarget = target;
softwareDetailLoading = true;
machine.loadSoftware(target.package_type || "", target.identity || target.name || "");
}
function backSoftware() {
if (softwareDetailLoading)
return ;
if (!softwareTrail.length) {
softwareDialog.close();
return ;
}
const target = softwareTrail[softwareTrail.length - 1];
softwareTrail = softwareTrail.slice(0, -1);
softwareTarget = target;
softwareDetailLoading = true;
machine.loadSoftware(target.package_type || "", target.identity || target.name || "");
}
function pageLabel(key) {
return key.charAt(0).toUpperCase() + key.slice(1);
}
function navigate(key, drill) {
if (key === page)
return ;
routeTrail = drill ? routeTrail.concat([page]) : [];
page = key;
}
function goBack() {
if (!routeTrail.length)
return ;
const previous = routeTrail[routeTrail.length - 1];
routeTrail = routeTrail.slice(0, -1);
page = previous;
}
function rememberQuery(key, value) {
savedQueries[key] = value;
}
function parse(raw, fallback) {
try {
return JSON.parse(raw || "");
} catch (_) {
return fallback;
}
}
function bytes(value) {
const n = Number(value || 0);
if (n <= 0)
return "0 B";
const units = ["B", "KiB", "MiB", "GiB", "TiB"];
const i = Math.min(units.length - 1, Math.floor(Math.log(n) / Math.log(1024)));
const amount = n / Math.pow(1024, i);
return (amount >= 10 || i === 0 ? amount.toFixed(0) : amount.toFixed(1)) + " " + units[i];
}
function percent(value) {
return Number(value || 0).toFixed(0) + "%";
}
function endpoint(address, port) {
return (address || "*") + ":" + Number(port || 0);
}
// The Agent reports the kernel's full container ID; the container inventory
// carries the 12-character short form. The join is a prefix, never equality.
function containerFor(id) {
if (!id)
return null;
for (let i = 0; i < containers.length; ++i) if (containers[i].container_id && String(id).indexOf(String(containers[i].container_id)) === 0) {
return containers[i];
}
return null;
}
function ownerLabel(p) {
if (!p)
return "";
if (p.container_id) {
const c = containerFor(p.container_id);
return c && c.name ? c.name : (p.container_runtime || "container") + " " + String(p.container_id).substring(0, 12);
}
return p.unit || "";
}
function ownerColor(p) {
return p && p.container_id ? Theme.cyan : Theme.blue;
}
// Services report the unit name with .service stripped; processes carry it whole.
function unitProcessCount(name) {
if (!name)
return 0;
const unit = name + ".service";
let n = 0;
for (let i = 0; i < processes.length; ++i) if (processes[i].unit === unit) {
n++;
}
return n;
}
// CapEff arrives as a 64-bit hex mask. Split it into two 32-bit words rather
// than trusting a single parseInt, which loses precision above bit 52.
function holdsSharpCapability(mask) {
if (!mask)
return false;
const hex = String(mask);
const low = parseInt(hex.slice(-8) || "0", 16) || 0;
const high = parseInt(hex.slice(0, -8) || "0", 16) || 0;
// low word: DAC_READ_SEARCH 2, SETGID 6, SETUID 7, NET_ADMIN 12,
// SYS_MODULE 16, SYS_RAWIO 17, SYS_PTRACE 19, SYS_ADMIN 21, SYS_BOOT 22
const lowMask = (1 << 2) | (1 << 6) | (1 << 7) | (1 << 12) | (1 << 16) | (1 << 17) | (1 << 19) | (1 << 21) | (1 << 22);
// high word: PERFMON 38, BPF 39 → bits 6 and 7 of the high word
const highMask = (1 << 6) | (1 << 7);
return (low & lowMask) !== 0 || (high & highMask) !== 0;
}
function sharpProcessCount() {
let n = 0;
for (let i = 0; i < processes.length; ++i) if (holdsSharpCapability(processes[i].capabilities_effective)) {
n++;
}
return n;
}
function capabilityColor(name) {
return sharpCapabilities.indexOf(name) >= 0 ? Theme.warn : Theme.dim;
}
// The four stages the fleet dashboard shows, driven here by what the Agent
// actually returned rather than by a hardcoded chip.
function stageState(index) {
const a = approval;
const result = a.receipt || a.policy || {
};
if (index === 0)
return "done";
if (a.error)
return index === 1 ? "failed" : "idle";
if (machine.approvalRunning)
return index === 1 ? "running" : "idle";
if (a.request_id === undefined)
return "idle";
if (index === 1)
return "done";
if (index === 2)
return (result.authorization_id || result.token_id) ? "done" : "failed";
if (index === 3)
return result.executed ? "done" : "failed";
return "idle";
}
function stageColor(state) {
if (state === "done")
return Theme.good;
if (state === "running")
return Theme.warn;
if (state === "failed")
return Theme.bad;
return Theme.faint;
}
function updateRecord(update) {
for (let i = 0; i < events.length; ++i) {
const m = events[i].metadata || {};
if (m.package_name === update.package_name && m.package_type === update.package_type && m.target_version === update.available_version)
return events[i];
}
return null;
}
function updateState(update) {
if (machine.approvalRunning && selectedUpdate.package_name === update.package_name && selectedUpdate.package_type === update.package_type)
return "INSTALLING";
const record = updateRecord(update);
if (!record) return "DISCOVERED";
const m = record.metadata || {};
const result = (m.result || {}).receipt || (m.result || {}).policy || {};
if (result.executed) return "INSTALLED · RESCAN";
if (m.error || record.event_subtype === "failed" || result.decision === "denied" || result.decision === "failed") return "NEEDS ATTENTION";
return "RESULT RECORDED";
}
function locallyApprovable(u) {
return !!u && (u.package_type === "dnf" || u.package_type === "apt" || u.package_type === "pacman");
}
function approvalBlockReason(u) {
if (!u || !u.package_name)
return "";
if (machine.enrollment === "fleet enrolled")
return "This machine is fleet enrolled — authority belongs to the RedFlag Server, and the Agent refuses a local mint.";
if (!locallyApprovable(u))
return "Local approval covers DNF, APT, and pacman. " + String(u.package_type || "this ecosystem").toUpperCase() + " still executes through the fleet signed-command path.";
return "";
}
function approvalReasonRequired(u) {
return !!u && u.package_type === "pacman";
}
function approvalCanOverride() {
return String(approval.error || "").indexOf("osv_status=") >= 0;
}
function approvalResult() {
return approval.receipt || approval.policy || {
};
}
function verifiedActionCount() {
const result = approvalResult();
if (result.verified_actions !== undefined)
return result.verified_actions;
if (result.verified_artifacts !== undefined)
return result.verified_artifacts;
return "—";
}
function processCountLabel(n) {
return n ? n + (n === 1 ? " process" : " processes") : "";
}
function containerProcessCount(id) {
if (!id)
return 0;
let n = 0;
for (let i = 0; i < processes.length; ++i) if (processes[i].container_id && String(processes[i].container_id).indexOf(String(id)) === 0) {
n++;
}
return n;
}
function historyCeiling(first, second) {
let maximum = 1;
for (let i = 0; i < history.length; ++i) maximum = Math.max(maximum, Number(history[i][first] || 0) + Number(history[i][second] || 0))
return maximum;
}
function navBadge(key) {
if (key === "processes")
return String(machine.processCount);
if (key === "containers")
return String(machine.containerCount);
if (key === "services")
return String(machine.serviceCount);
if (key === "software")
return String(machine.softwareCount);
if (key === "updates")
return String(machine.updateCount);
if (key === "security" && machine.criticalCount > 0)
return String(machine.criticalCount);
return "";
}
function pageComponent() {
switch (page) {
case "performance":
return performancePage;
case "processes":
return processesPage;
case "network":
return networkPage;
case "storage":
return storagePage;
case "containers":
return containersPage;
case "services":
return servicesPage;
case "software":
return softwarePage;
case "updates":
return updatesPage;
case "security":
return securityPage;
case "history":
return historyPage;
default:
return overviewPage;
}
}
title: "RedFlag"
width: 1320
height: 860
minimumWidth: 600
minimumHeight: 480
visible: true
color: Theme.window
font.family: "Inter"
font.pixelSize: Theme.fontBody
palette.window: Theme.window
palette.windowText: Theme.text
palette.base: Theme.surface
palette.alternateBase: Theme.raised
palette.text: Theme.text
palette.button: Theme.raised
palette.buttonText: Theme.text
palette.highlight: Theme.red
palette.highlightedText: "white"
palette.mid: Theme.divider
Component.onCompleted: {
machine.refreshTelemetry();
machine.refreshOverview();
machine.refreshProcesses();
machine.refreshConnections();
machine.refreshSoftware();
}
// Containers and Services join against the process list; entering either page
// refreshes it once rather than polling a full snapshot every few seconds.
onPageChanged: {
if (page === "processes" || page === "containers" || page === "services" || page === "security")
machine.refreshProcesses();
if (page === "network")
machine.refreshConnections();
if (page === "software")
machine.refreshSoftware();
}
Shortcut {
sequence: "Alt+Left"
enabled: !processDialog.visible && !softwareDialog.visible && !updateDialog.visible
onActivated: app.goBack()
}
Machine {
id: machine
onProcessDetailUpdated: processDialog.open()
onSoftwareDetailUpdated: {
app.softwareDetailLoading = false;
if (app.softwareDetailWanted)
softwareDialog.open();
}
onApprovalFinished: {
machine.refreshOverview();
machine.refreshSoftware();
if (app.approvalResult().executed) machine.triggerScan();
}
onScanRunningChanged: {
if (!machine.scanRunning) machine.refreshOverview();
}
onOperationFinished: machine.refreshOverview()
}
Timer {
interval: 1000
repeat: true
running: true
onTriggered: machine.refreshTelemetry()
}
Timer {
interval: 15000
repeat: true
running: true
onTriggered: machine.refreshOverview()
}
Timer {
interval: 2000
repeat: true
running: machine.scanRunning || machine.scanRequestRunning
onTriggered: machine.refreshScanStatus()
}
Timer {
interval: 3000
repeat: true
running: true
onTriggered: {
if (app.page === "processes")
machine.refreshProcesses();
if (app.page === "network")
machine.refreshConnections();
}
}
RowLayout {
anchors.fill: parent
spacing: 0
Rectangle {
visible: !app.compact
Layout.preferredWidth: visible ? Theme.railWidth : 0
Layout.fillHeight: true
color: Theme.rail
border.width: 0
ScrollView {
id: railScroll
anchors.fill: parent
padding: 12
contentWidth: availableWidth
clip: true
ColumnLayout {
width: railScroll.availableWidth
height: Math.max(implicitHeight, railScroll.availableHeight)
spacing: 4
RowLayout {
Layout.fillWidth: true
Layout.preferredHeight: 44
Layout.leftMargin: 4
spacing: 10
Image {
Layout.preferredWidth: 22
Layout.preferredHeight: 22
source: "qrc:/qt/qml/com/redflag/desktop/icons/icon.png"
fillMode: Image.PreserveAspectFit
smooth: true
}
Column {
Text {
text: "RedFlag"
color: Theme.text
font.pixelSize: 15
font.weight: 750
}
Text {
text: "THIS MACHINE"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1.4
}
}
Item {
Layout.fillWidth: true
}
}
Text {
text: "OBSERVE"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1.2
Layout.leftMargin: 8
Layout.topMargin: 8
Layout.bottomMargin: 2
}
Repeater {
model: [{
"key": "overview",
"label": "Overview"
}, {
"key": "performance",
"label": "Performance"
}, {
"key": "processes",
"label": "Processes"
}, {
"key": "network",
"label": "Network"
}, {
"key": "storage",
"label": "Storage"
}]
NavItem {
required property var modelData
Layout.fillWidth: true
label: modelData.label
badge: app.navBadge(modelData.key)
selected: app.page === modelData.key
onClicked: app.navigate(modelData.key, false)
}
}
Text {
text: "OPERATE"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1.2
Layout.leftMargin: 8
Layout.topMargin: 10
Layout.bottomMargin: 2
}
Repeater {
model: [{
"key": "containers",
"label": "Containers"
}, {
"key": "services",
"label": "Services"
}, {
"key": "software",
"label": "Software"
}, {
"key": "updates",
"label": "Updates"
}]
NavItem {
required property var modelData
Layout.fillWidth: true
label: modelData.label
badge: app.navBadge(modelData.key)
selected: app.page === modelData.key
onClicked: app.navigate(modelData.key, false)
}
}
Text {
text: "TRUST"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1.2
Layout.leftMargin: 8
Layout.topMargin: 10
Layout.bottomMargin: 2
}
Repeater {
model: [{
"key": "security",
"label": "Security"
}, {
"key": "history",
"label": "History"
}]
NavItem {
required property var modelData
Layout.fillWidth: true
label: modelData.label
badge: app.navBadge(modelData.key)
selected: app.page === modelData.key
onClicked: app.navigate(modelData.key, false)
}
}
Item {
Layout.fillHeight: true
}
Rectangle {
Layout.fillWidth: true
height: 1
color: Theme.divider
}
RowLayout {
Layout.fillWidth: true
Layout.preferredHeight: 46
Layout.leftMargin: 5
Rectangle {
width: 8
height: 8
radius: 4
color: machine.connected ? Theme.good : Theme.bad
}
Column {
Text {
text: machine.connected ? "Agent connected" : "Agent unavailable"
color: Theme.dim
font.pixelSize: 11
}
Text {
text: machine.enrollment + (machine.agentVersion.length ? " · v" + machine.agentVersion : "")
color: Theme.faint
font.pixelSize: Theme.fontMeta
}
}
}
}
}
}
Rectangle {
Layout.preferredWidth: 1
Layout.fillHeight: true
color: Theme.divider
}
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
spacing: 0
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: Theme.headerHeight
color: Theme.window
border.width: 0
RowLayout {
anchors.fill: parent
anchors.leftMargin: app.pageMargin
anchors.rightMargin: app.pageMargin
spacing: 14
Rectangle {
width: 10
height: 10
radius: 5
color: !machine.connected ? Theme.faint : machine.healthState === "healthy" ? Theme.good : Theme.warn
}
ColumnLayout {
Layout.fillWidth: true
Layout.minimumWidth: 0
spacing: 1
Text {
text: machine.hostname
color: Theme.text
font.pixelSize: Theme.fontHeading
font.weight: 650
}
Text {
Layout.fillWidth: true
elide: Text.ElideRight
text: machine.machineSubtitle
color: Theme.dim
font.pixelSize: Theme.fontMeta
}
}
Rectangle {
width: healthLabel.implicitWidth + 18
height: 25
radius: 13
color: Theme.tint(machine.healthState === "healthy" ? Theme.good : machine.healthState === "degraded" ? Theme.warn : Theme.faint, 0.14)
Text {
id: healthLabel
anchors.centerIn: parent
text: machine.healthState.toUpperCase()
color: machine.healthState === "healthy" ? Theme.good : machine.healthState === "degraded" ? Theme.warn : Theme.faint
font.pixelSize: Theme.fontMeta
font.weight: 700
font.letterSpacing: 1
}
}
Item {
Layout.fillWidth: true
}
Text {
visible: !app.compact
text: machine.uptime.length ? "UP " + machine.uptime.toUpperCase() : ""
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
}
Rectangle {
width: 32
height: 32
radius: 16
color: refreshMouse.containsMouse ? Theme.hover : "transparent"
Text {
anchors.centerIn: parent
text: "↻"
color: Theme.dim
font.pixelSize: 20
}
MouseArea {
id: refreshMouse
anchors.fill: parent
hoverEnabled: true
onClicked: {
machine.refreshTelemetry();
machine.refreshOverview();
}
}
}
Rectangle {
width: 32
height: 32
radius: 16
color: themeMouse.containsMouse ? Theme.hover : "transparent"
Text {
anchors.centerIn: parent
text: Theme.dark ? "◐" : "◑"
color: Theme.dim
font.pixelSize: 15
}
MouseArea {
id: themeMouse
anchors.fill: parent
hoverEnabled: true
onClicked: Theme.dark = !Theme.dark
}
}
}
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: 1
color: Theme.divider
}
}
Rectangle {
visible: machine.connected && machine.agentGap.length > 0
Layout.fillWidth: true
Layout.preferredHeight: visible ? gapText.implicitHeight + 20 : 0
color: Theme.tint(Theme.warn, 0.12)
Text {
id: gapText
anchors.fill: parent
anchors.margins: 10
text: machine.agentGap
color: Theme.warn
font.pixelSize: 11
wrapMode: Text.Wrap
}
}
Rectangle {
visible: !machine.connected && machine.connectionError.length > 0
Layout.fillWidth: true
Layout.preferredHeight: visible ? errorText.implicitHeight + 20 : 0
color: Theme.redDim
Text {
id: errorText
anchors.fill: parent
anchors.margins: 10
text: machine.connectionError
color: Theme.bad
font.pixelSize: 11
font.family: Theme.mono
wrapMode: Text.Wrap
}
}
RowLayout {
Layout.fillWidth: true
Layout.leftMargin: app.pageMargin
Layout.rightMargin: app.pageMargin
Layout.topMargin: 8
Layout.bottomMargin: 4
spacing: 8
AppButton {
visible: app.routeTrail.length > 0
text: " Back to " + (app.routeTrail.length ? app.pageLabel(app.routeTrail[app.routeTrail.length - 1]) : "")
onClicked: app.goBack()
}
ComboBox {
visible: app.compact
Layout.fillWidth: true
model: app.pageKeys.map((key) => {
return app.pageLabel(key);
})
currentIndex: app.pageKeys.indexOf(app.page)
onActivated: app.navigate(app.pageKeys[currentIndex], false)
}
Text {
visible: !app.compact
Layout.fillWidth: true
text: machine.hostname + " / " + app.pageLabel(app.page)
color: Theme.dim
font.pixelSize: Theme.fontMeta
elide: Text.ElideRight
}
}
Loader {
id: pageLoader
Layout.fillWidth: true
Layout.fillHeight: true
sourceComponent: app.pageComponent()
}
}
}
Component {
id: overviewPage
ScrollView {
id: overviewScroll
clip: true
contentWidth: availableWidth
leftPadding: app.pageMargin
rightPadding: app.pageMargin
ColumnLayout {
width: overviewScroll.availableWidth
spacing: 16
anchors.margins: app.pageMargin
Text {
text: "This machine"
color: Theme.text
font.pixelSize: Theme.fontTitle
font.weight: 700
Layout.topMargin: 20
}
Text {
Layout.fillWidth: true
wrapMode: Text.Wrap
text: "Operational condition, live load, and the systems asking for attention."
color: Theme.dim
font.pixelSize: Theme.fontBody
}
GridLayout {
Layout.fillWidth: true
columns: width < 700 ? 2 : 4
columnSpacing: 12
rowSpacing: 12
MetricCard {
Layout.fillWidth: true
label: "CPU"
value: app.percent(machine.cpuUsage)
detail: "load " + machine.load1.toFixed(2)
accent: Theme.pressure(machine.cpuUsage)
}
MetricCard {
Layout.fillWidth: true
label: "Memory"
value: app.percent(machine.memoryPercent)
detail: app.bytes(machine.memoryUsed) + " / " + app.bytes(machine.memoryTotal)
accent: Theme.pressure(machine.memoryPercent)
}
MetricCard {
Layout.fillWidth: true
label: "Network"
value: "↓ " + app.bytes(machine.networkReceive) + "/s"
detail: "↑ " + app.bytes(machine.networkTransmit) + "/s"
accent: Theme.cyan
}
MetricCard {
Layout.fillWidth: true
label: "Temperature"
value: machine.temperature > 0 ? machine.temperature.toFixed(0) + " °C" : "Unavailable"
detail: machine.temperature > 0 ? "hottest reported sensor" : "no sensor evidence"
accent: machine.temperature >= 90 ? Theme.bad : Theme.purple
}
MetricCard {
Layout.fillWidth: true
label: "Updates"
value: String(machine.updateCount)
detail: machine.criticalCount + " critical"
accent: machine.criticalCount > 0 ? Theme.bad : machine.updateCount > 0 ? Theme.warn : Theme.good
}
MetricCard {
Layout.fillWidth: true
label: "Containers"
value: machine.containerRunning + " / " + machine.containerCount
detail: machine.containerUnhealthy + " unhealthy"
accent: machine.containerUnhealthy > 0 ? Theme.bad : Theme.good
}
MetricCard {
Layout.fillWidth: true
label: "Services"
value: machine.serviceRunning + " / " + machine.serviceCount
detail: machine.serviceFailed + " failed"
accent: machine.serviceFailed > 0 ? Theme.bad : Theme.good
}
MetricCard {
Layout.fillWidth: true
label: "Processes"
value: String(machine.processCount)
detail: machine.agentStatus + " · " + machine.enrollment
accent: Theme.blue
}
}
GridLayout {
Layout.fillWidth: true
columns: width < 700 ? 1 : 2
Layout.preferredHeight: columns === 1 ? 512 : 250
columnSpacing: 12
rowSpacing: 12
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: Theme.surface
radius: Theme.radius
border.width: 1
border.color: Theme.divider
Text {
text: "CPU — LAST " + app.history.length + " SECONDS"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1
anchors.left: parent.left
anchors.leftMargin: 16
anchors.top: parent.top
anchors.topMargin: 14
}
Text {
text: app.percent(machine.cpuUsage)
color: Theme.text
font.pixelSize: 28
font.weight: 650
anchors.right: parent.right
anchors.rightMargin: 16
anchors.top: parent.top
anchors.topMargin: 10
}
LineGraph {
anchors.fill: parent
anchors.margins: 16
anchors.topMargin: 54
values: app.history.map((p) => {
return p.cpu_percent;
})
ceiling: 100
lineColor: Theme.red
}
}
Rectangle {
Layout.preferredWidth: 360
Layout.fillHeight: true
color: Theme.surface
radius: Theme.radius
border.width: 1
border.color: Theme.divider
Text {
text: "RECENT"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1
anchors.left: parent.left
anchors.leftMargin: 16
anchors.top: parent.top
anchors.topMargin: 14
}
ListView {
anchors.fill: parent
anchors.topMargin: 40
clip: true
model: app.events.slice(0, 6)
Text {
visible: app.events.length === 0
anchors.centerIn: parent
text: "No local events awaiting sync."
color: Theme.faint
font.pixelSize: 11
}
delegate: Rectangle {
required property var modelData
width: ListView.view.width
height: 35
color: "transparent"
Rectangle {
width: 5
height: 5
radius: 3
color: modelData.severity === "critical" || modelData.severity === "error" ? Theme.bad : modelData.severity === "warning" ? Theme.warn : Theme.faint
anchors.left: parent.left
anchors.leftMargin: 16
anchors.top: parent.top
anchors.topMargin: 9
}
Text {
text: modelData.message || modelData.event_type || "event"
color: Theme.dim
font.pixelSize: 11
anchors.left: parent.left
anchors.leftMargin: 30
anchors.right: parent.right
anchors.rightMargin: 12
anchors.top: parent.top
anchors.topMargin: 4
elide: Text.ElideRight
}
Text {
text: modelData.component || "agent"
color: Theme.faint
font.pixelSize: Theme.fontMeta
anchors.left: parent.left
anchors.leftMargin: 30
anchors.bottom: parent.bottom
anchors.bottomMargin: 3
}
}
}
}
}
Item {
Layout.preferredHeight: 22
}
}
}
}
Component {
id: performancePage
ScrollView {
id: performanceScroll
clip: true
contentWidth: availableWidth
leftPadding: app.pageMargin
rightPadding: app.pageMargin
ColumnLayout {
width: performanceScroll.availableWidth
spacing: 14
anchors.margins: app.pageMargin
Text {
text: "Performance"
color: Theme.text
font.pixelSize: Theme.fontTitle
font.weight: 700
Layout.topMargin: 20
}
Text {
Layout.fillWidth: true
wrapMode: Text.Wrap
text: "Agent-owned one-second telemetry. History is bounded to five minutes in memory."
color: Theme.dim
font.pixelSize: Theme.fontBody
}
GridLayout {
Layout.fillWidth: true
columns: width < 700 ? 2 : 4
columnSpacing: 12
MetricCard {
Layout.fillWidth: true
label: "CPU"
value: app.percent(machine.cpuUsage)
detail: "load " + machine.load1.toFixed(2) + " · " + machine.load5.toFixed(2) + " · " + machine.load15.toFixed(2)
accent: Theme.red
}
MetricCard {
Layout.fillWidth: true
label: "Memory"
value: app.percent(machine.memoryPercent)
detail: app.bytes(machine.memoryUsed) + " used"
accent: Theme.blue
}
MetricCard {
Layout.fillWidth: true
label: "Swap"
value: app.percent(machine.swapPercent)
detail: "memory pressure reserve"
accent: Theme.purple
}
MetricCard {
Layout.fillWidth: true
label: "Disk I/O"
value: app.bytes(machine.diskRead + machine.diskWrite) + "/s"
detail: "↓ " + app.bytes(machine.diskRead) + " ↑ " + app.bytes(machine.diskWrite)
accent: Theme.warn
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 280
color: Theme.surface
radius: Theme.radius
border.width: 1
border.color: Theme.divider
Text {
text: "CPU UTILIZATION"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1
anchors.left: parent.left
anchors.leftMargin: 16
anchors.top: parent.top
anchors.topMargin: 14
}
Text {
text: app.percent(machine.cpuUsage)
color: Theme.text
font.pixelSize: 30
font.weight: 650
anchors.right: parent.right
anchors.rightMargin: 16
anchors.top: parent.top
anchors.topMargin: 9
}
LineGraph {
anchors.fill: parent
anchors.margins: 18
anchors.topMargin: 58
values: app.history.map((p) => {
return p.cpu_percent;
})
ceiling: 100
lineColor: Theme.red
}
}
GridLayout {
Layout.fillWidth: true
columns: width < 700 ? 1 : 2
Layout.preferredHeight: columns === 1 ? 452 : 220
columnSpacing: 12
rowSpacing: 12
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: Theme.surface
radius: Theme.radius
border.width: 1
border.color: Theme.divider
Text {
text: "MEMORY"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1
anchors.left: parent.left
anchors.leftMargin: 16
anchors.top: parent.top
anchors.topMargin: 14
}
LineGraph {
anchors.fill: parent
anchors.margins: 16
anchors.topMargin: 44
values: app.history.map((p) => {
return p.memory_percent;
})
ceiling: 100
lineColor: Theme.blue
}
}
Rectangle {
Layout.fillWidth: true
Layout.fillHeight: true
color: Theme.surface
radius: Theme.radius
border.width: 1
border.color: Theme.divider
Text {
text: "NETWORK THROUGHPUT"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1
anchors.left: parent.left
anchors.leftMargin: 16
anchors.top: parent.top
anchors.topMargin: 14
}
LineGraph {
anchors.fill: parent
anchors.margins: 16
anchors.topMargin: 44
values: app.history.map((p) => {
return Number(p.network_receive_per_second) + Number(p.network_transmit_per_second);
})
ceiling: app.historyCeiling("network_receive_per_second", "network_transmit_per_second")
lineColor: Theme.cyan
}
}
}
Item {
Layout.preferredHeight: 22
}
}
}
}
Component {
id: processesPage
Item {
id: processRoot
property string query: search.text.toLowerCase()
property var rows: app.processes.filter((p) => {
return !query.length || String(p.name).toLowerCase().includes(query) || String(p.cmdline).toLowerCase().includes(query) || String(p.user).toLowerCase().includes(query) || String(p.pid).includes(query) || String(app.ownerLabel(p)).toLowerCase().includes(query) || String(p.unit || "").toLowerCase().includes(query);
})
Component.onCompleted: {
if (app.pendingProcessQuery.length) {
search.text = app.pendingProcessQuery;
app.pendingProcessQuery = "";
}
}
ScrollView {
id: processesBody
anchors.fill: parent
padding: app.pageMargin
contentWidth: availableWidth
clip: true
ColumnLayout {
width: processesBody.availableWidth
height: Math.max(implicitHeight, processesBody.availableHeight)
spacing: 12
PageHeader {
Layout.fillWidth: true
title: "Processes"
subtitle: rows.length + " visible · select a process for sockets, files, namespaces, and identity"
TextField {
id: search
text: app.savedQueries.processes || ""
onTextChanged: app.rememberQuery("processes", text)
Layout.preferredWidth: 300
placeholderText: "Find process, PID, command, owner"
color: Theme.text
placeholderTextColor: Theme.faint
background: Rectangle {
color: Theme.surface
border.width: 1
border.color: search.activeFocus ? Theme.red : Theme.divider
radius: Theme.radiusSm
}
}
}
ScrollView {
id: processesTable
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: 200
clip: true
contentWidth: Math.max(availableWidth, 720)
contentHeight: availableHeight
bottomPadding: availableWidth < 720 ? 14 : 0
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
Rectangle {
width: Math.max(processesTable.availableWidth, 720)
height: processesTable.availableHeight
color: Theme.surface
radius: Theme.radius
border.width: 1
border.color: Theme.divider
ColumnLayout {
anchors.fill: parent
spacing: 0
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 34
color: Theme.raised
RowLayout {
anchors.fill: parent
anchors.leftMargin: 14
anchors.rightMargin: 14
Text {
text: "PROCESS"
color: Theme.faint
font.pixelSize: Theme.fontMeta
Layout.fillWidth: true
}
Text {
text: "PID"
color: Theme.faint
font.pixelSize: Theme.fontMeta
Layout.preferredWidth: 70
}
Text {
text: "OWNER"
color: Theme.faint
font.pixelSize: Theme.fontMeta
Layout.preferredWidth: 120
}
Text {
text: "CPU"
color: Theme.faint
font.pixelSize: Theme.fontMeta
Layout.preferredWidth: 70
}
Text {
text: "MEMORY"
color: Theme.faint
font.pixelSize: Theme.fontMeta
Layout.preferredWidth: 80
}
}
}
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
model: processRoot.rows
delegate: Rectangle {
required property var modelData
width: ListView.view.width
height: 47
color: rowMouse.containsMouse ? Theme.hover : "transparent"
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: 1
color: Theme.divider
}
RowLayout {
anchors.fill: parent
anchors.leftMargin: 14
anchors.rightMargin: 14
Column {
Layout.fillWidth: true
Text {
text: modelData.name || "unknown"
color: Theme.text
font.pixelSize: 12
font.family: Theme.mono
}
Row {
id: subRow
width: parent.width
spacing: 6
Text {
id: ownerTag
visible: text.length > 0
text: app.ownerLabel(modelData)
color: app.ownerColor(modelData)
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
}
Text {
id: ownerDot
visible: ownerTag.visible
text: "·"
color: Theme.faint
font.pixelSize: Theme.fontMeta
}
Text {
width: subRow.width - (ownerTag.visible ? ownerTag.width + ownerDot.width + subRow.spacing * 2 : 0)
text: modelData.cmdline || modelData.path || ""
color: Theme.faint
font.pixelSize: Theme.fontMeta
elide: Text.ElideRight
}
}
}
Text {
text: String(modelData.pid)
color: Theme.dim
font.pixelSize: 11
font.family: Theme.mono
Layout.preferredWidth: 70
}
Text {
text: modelData.user || String(modelData.uid)
color: Theme.dim
font.pixelSize: 11
Layout.preferredWidth: 120
elide: Text.ElideRight
}
Text {
text: Number(modelData.cpu_percent || 0).toFixed(1) + "%"
color: Theme.pressure(Number(modelData.cpu_percent || 0))
font.pixelSize: 11
Layout.preferredWidth: 70
}
Text {
text: app.bytes(modelData.rss_bytes)
color: Theme.dim
font.pixelSize: 11
Layout.preferredWidth: 80
}
}
MouseArea {
id: rowMouse
anchors.fill: parent
hoverEnabled: true
onClicked: machine.loadProcess(Number(modelData.pid))
}
}
}
}
}
}
}
}
}
}
Component {
id: networkPage
Item {
id: networkRoot
property string query: networkSearch.text.toLowerCase()
property var rows: app.connections.filter((c) => {
return !query.length || String(c.process).toLowerCase().includes(query) || String(c.local_addr).includes(query) || String(c.remote_addr).includes(query) || String(c.local_port).includes(query);
})
ScrollView {
id: networkBody
anchors.fill: parent
padding: app.pageMargin
contentWidth: availableWidth
clip: true
ColumnLayout {
width: networkBody.availableWidth
height: Math.max(implicitHeight, networkBody.availableHeight)
spacing: 12
PageHeader {
Layout.fillWidth: true
title: "Network"
subtitle: "Interfaces, throughput, listeners, and live socket ownership."
TextField {
id: networkSearch
text: app.savedQueries.network || ""
onTextChanged: app.rememberQuery("network", text)
Layout.preferredWidth: 280
placeholderText: "Find process, address, port"
color: Theme.text
placeholderTextColor: Theme.faint
background: Rectangle {
color: Theme.surface
border.width: 1
border.color: networkSearch.activeFocus ? Theme.red : Theme.divider
radius: Theme.radiusSm
}
}
}
GridLayout {
Layout.fillWidth: true
columns: width < 700 ? 2 : 4
columnSpacing: 12
rowSpacing: 12
MetricCard {
Layout.fillWidth: true
label: "Receive"
value: app.bytes(machine.networkReceive) + "/s"
detail: "all non-loopback interfaces"
accent: Theme.cyan
}
MetricCard {
Layout.fillWidth: true
label: "Transmit"
value: app.bytes(machine.networkTransmit) + "/s"
detail: "all non-loopback interfaces"
accent: Theme.purple
}
MetricCard {
Layout.fillWidth: true
label: "Sockets"
value: String(rows.length)
detail: "TCP and UDP · process-correlated"
accent: Theme.blue
}
}
ScrollView {
id: networkTable
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: 200
clip: true
contentWidth: Math.max(availableWidth, 860)
contentHeight: availableHeight
bottomPadding: availableWidth < 860 ? 14 : 0
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
Rectangle {
width: Math.max(networkTable.availableWidth, 860)
height: networkTable.availableHeight
color: Theme.surface
radius: Theme.radius
border.width: 1
border.color: Theme.divider
ColumnLayout {
anchors.fill: parent
spacing: 0
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 34
color: Theme.raised
RowLayout {
anchors.fill: parent
anchors.leftMargin: 14
anchors.rightMargin: 14
Text {
text: "PROCESS"
color: Theme.faint
font.pixelSize: Theme.fontMeta
Layout.preferredWidth: 180
}
Text {
text: "PROTOCOL"
color: Theme.faint
font.pixelSize: Theme.fontMeta
Layout.preferredWidth: 80
}
Text {
text: "LOCAL"
color: Theme.faint
font.pixelSize: Theme.fontMeta
Layout.fillWidth: true
}
Text {
text: "REMOTE"
color: Theme.faint
font.pixelSize: Theme.fontMeta
Layout.fillWidth: true
}
Text {
text: "STATE"
color: Theme.faint
font.pixelSize: Theme.fontMeta
Layout.preferredWidth: 100
}
}
}
ListView {
Layout.fillWidth: true
Layout.fillHeight: true
clip: true
model: networkRoot.rows
delegate: Rectangle {
required property var modelData
width: ListView.view.width
height: 38
color: "transparent"
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: 1
color: Theme.divider
}
RowLayout {
anchors.fill: parent
anchors.leftMargin: 14
anchors.rightMargin: 14
Text {
text: (modelData.process || "unknown") + (modelData.pid ? " · " + modelData.pid : "")
color: Theme.text
font.pixelSize: 11
font.family: Theme.mono
Layout.preferredWidth: 180
elide: Text.ElideRight
}
Text {
text: modelData.protocol + " " + modelData.family
color: Theme.dim
font.pixelSize: Theme.fontMeta
Layout.preferredWidth: 80
}
Text {
text: app.endpoint(modelData.local_addr, modelData.local_port)
color: Theme.dim
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
Layout.fillWidth: true
elide: Text.ElideRight
}
Text {
text: modelData.remote_port ? app.endpoint(modelData.remote_addr, modelData.remote_port) : "—"
color: Theme.dim
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
Layout.fillWidth: true
elide: Text.ElideRight
}
Text {
text: modelData.state
color: modelData.state === "LISTEN" ? Theme.warn : modelData.state === "ESTABLISHED" ? Theme.good : Theme.faint
font.pixelSize: Theme.fontMeta
Layout.preferredWidth: 100
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: parent.color = Theme.hover
onExited: parent.color = "transparent"
onClicked: {
if (modelData.pid)
machine.loadProcess(Number(modelData.pid));
}
}
}
}
}
}
}
}
}
}
}
Component {
id: storagePage
ScrollView {
id: storageScroll
clip: true
contentWidth: availableWidth
leftPadding: app.pageMargin
rightPadding: app.pageMargin
ColumnLayout {
width: storageScroll.availableWidth
anchors.margins: app.pageMargin
spacing: 14
Text {
text: "Storage"
color: Theme.text
font.pixelSize: Theme.fontTitle
font.weight: 700
Layout.topMargin: 20
}
Text {
Layout.fillWidth: true
wrapMode: Text.Wrap
text: "Filesystems and physical-device throughput observed by the Agent."
color: Theme.dim
font.pixelSize: Theme.fontBody
}
GridLayout {
Layout.fillWidth: true
columns: width < 700 ? 2 : 4
columnSpacing: 12
rowSpacing: 12
MetricCard {
Layout.fillWidth: true
label: "Read"
value: app.bytes(machine.diskRead) + "/s"
detail: "physical block devices"
accent: Theme.blue
}
MetricCard {
Layout.fillWidth: true
label: "Write"
value: app.bytes(machine.diskWrite) + "/s"
detail: "physical block devices"
accent: Theme.warn
}
}
Repeater {
model: app.system.disk_info || []
Rectangle {
required property var modelData
Layout.fillWidth: true
Layout.preferredHeight: 88
color: Theme.surface
radius: Theme.radius
border.width: 1
border.color: Theme.divider
RowLayout {
anchors.fill: parent
anchors.margins: 15
Column {
Layout.fillWidth: true
Text {
text: modelData.mountpoint + (modelData.is_root ? " · ROOT" : "")
color: Theme.text
font.pixelSize: 13
font.family: Theme.mono
font.weight: 600
}
Text {
text: modelData.filesystem + " · " + app.bytes(modelData.used) + " used · " + app.bytes(modelData.available) + " available"
color: Theme.dim
font.pixelSize: 11
}
}
Text {
text: app.percent(modelData.used_percent)
color: Theme.pressure(modelData.used_percent)
font.pixelSize: 22
font.weight: 650
}
}
Rectangle {
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 15
height: 4
radius: 2
color: Theme.divider
Rectangle {
width: parent.width * Math.min(1, Number(modelData.used_percent) / 100)
height: parent.height
radius: 2
color: Theme.pressure(modelData.used_percent)
}
}
}
}
Item {
Layout.preferredHeight: 22
}
}
}
}
Component {
id: containersPage
Item {
ScrollView {
id: containersBody
anchors.fill: parent
padding: app.pageMargin
contentWidth: availableWidth
clip: true
ColumnLayout {
width: containersBody.availableWidth
height: Math.max(implicitHeight, containersBody.availableHeight)
spacing: 12
Text {
text: "Containers"
color: Theme.text
font.pixelSize: Theme.fontTitle
font.weight: 700
}
Text {
Layout.fillWidth: true
wrapMode: Text.Wrap
text: "Docker containers and Compose/stack identity from the Agent's existing container model."
color: Theme.dim
font.pixelSize: Theme.fontBody
}
GridLayout {
Layout.fillWidth: true
columns: width < 700 ? 2 : 4
columnSpacing: 12
rowSpacing: 12
MetricCard {
Layout.fillWidth: true
label: "Containers"
value: machine.containerRunning + " / " + machine.containerCount
detail: "running / total"
accent: Theme.blue
}
MetricCard {
Layout.fillWidth: true
label: "Health"
value: machine.containerUnhealthy ? machine.containerUnhealthy + " unhealthy" : "Healthy"
detail: "reported Docker health checks"
accent: machine.containerUnhealthy ? Theme.bad : Theme.good
}
}
ScrollView {
id: containersTable
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: 200
clip: true
contentWidth: Math.max(availableWidth, 800)
contentHeight: availableHeight
bottomPadding: availableWidth < 800 ? 14 : 0
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
Rectangle {
width: Math.max(containersTable.availableWidth, 800)
height: containersTable.availableHeight
color: Theme.surface
radius: Theme.radius
border.width: 1
border.color: Theme.divider
ListView {
anchors.fill: parent
clip: true
model: app.containers
Text {
visible: app.containers.length === 0
anchors.centerIn: parent
text: "Docker unavailable or no containers are present."
color: Theme.faint
font.pixelSize: 12
}
delegate: Rectangle {
required property var modelData
width: ListView.view.width
height: 58
color: "transparent"
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: 1
color: Theme.divider
}
RowLayout {
anchors.fill: parent
anchors.margins: 13
Rectangle {
width: 8
height: 8
radius: 4
color: modelData.health === "unhealthy" ? Theme.bad : modelData.state === "running" ? Theme.good : Theme.faint
}
Column {
Layout.fillWidth: true
Text {
text: modelData.name || modelData.container_id
color: Theme.text
font.pixelSize: 12
font.family: Theme.mono
}
Text {
text: modelData.image + (modelData.stack_name ? " · " + modelData.stack_name : "")
color: Theme.dim
font.pixelSize: Theme.fontMeta
}
}
Text {
text: app.processCountLabel(app.containerProcessCount(modelData.container_id))
color: Theme.cyan
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
Layout.preferredWidth: 100
}
Text {
text: modelData.ports || "no published ports"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
Layout.preferredWidth: 260
elide: Text.ElideRight
}
Text {
text: (modelData.health || modelData.state || "unknown").toUpperCase()
color: modelData.health === "unhealthy" ? Theme.bad : modelData.state === "running" ? Theme.good : Theme.faint
font.pixelSize: Theme.fontMeta
font.weight: 700
Layout.preferredWidth: 90
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: parent.color = Theme.hover
onExited: parent.color = "transparent"
onClicked: {
app.pendingProcessQuery = String(modelData.name || modelData.container_id || "");
app.navigate("processes", true);
}
}
}
}
}
}
}
}
}
}
Component {
id: servicesPage
Item {
id: servicesRoot
property string query: serviceSearch.text.toLowerCase()
property var rows: app.services.filter((s) => {
return !query.length || String(s.name).toLowerCase().includes(query) || String(s.description).toLowerCase().includes(query);
})
ScrollView {
id: servicesBody
anchors.fill: parent
padding: app.pageMargin
contentWidth: availableWidth
clip: true
ColumnLayout {
width: servicesBody.availableWidth
height: Math.max(implicitHeight, servicesBody.availableHeight)
spacing: 12
PageHeader {
Layout.fillWidth: true
title: "Services"
subtitle: "Cross-platform service domain, currently backed by systemd observation on Linux."
TextField {
id: serviceSearch
text: app.savedQueries.services || ""
onTextChanged: app.rememberQuery("services", text)
Layout.preferredWidth: 280
placeholderText: "Find service"
color: Theme.text
placeholderTextColor: Theme.faint
background: Rectangle {
color: Theme.surface
border.width: 1
border.color: serviceSearch.activeFocus ? Theme.red : Theme.divider
radius: Theme.radiusSm
}
}
}
GridLayout {
Layout.fillWidth: true
columns: width < 700 ? 2 : 4
columnSpacing: 12
rowSpacing: 12
MetricCard {
Layout.fillWidth: true
label: "Running"
value: String(machine.serviceRunning)
detail: "active services"
accent: Theme.good
}
MetricCard {
Layout.fillWidth: true
label: "Failed"
value: String(machine.serviceFailed)
detail: "requires operator attention"
accent: machine.serviceFailed ? Theme.bad : Theme.good
}
}
ScrollView {
id: servicesTable
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: 200
clip: true
contentWidth: Math.max(availableWidth, 700)
contentHeight: availableHeight
bottomPadding: availableWidth < 700 ? 14 : 0
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
Rectangle {
width: Math.max(servicesTable.availableWidth, 700)
height: servicesTable.availableHeight
color: Theme.surface
radius: Theme.radius
border.width: 1
border.color: Theme.divider
ListView {
anchors.fill: parent
clip: true
model: servicesRoot.rows
delegate: Rectangle {
required property var modelData
width: ListView.view.width
height: 48
color: "transparent"
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: 1
color: Theme.divider
}
RowLayout {
anchors.fill: parent
anchors.margins: 13
Rectangle {
width: 7
height: 7
radius: 4
color: modelData.active_state === "failed" ? Theme.bad : modelData.active_state === "active" ? Theme.good : Theme.faint
}
Column {
Layout.fillWidth: true
Text {
text: modelData.name
color: Theme.text
font.pixelSize: 12
font.family: Theme.mono
}
Text {
text: modelData.description || ""
color: Theme.faint
font.pixelSize: Theme.fontMeta
elide: Text.ElideRight
}
}
Text {
text: app.processCountLabel(app.unitProcessCount(modelData.name))
color: Theme.blue
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
Layout.preferredWidth: 100
}
Text {
text: (modelData.active_state + " · " + modelData.sub_state).toUpperCase()
color: modelData.active_state === "failed" ? Theme.bad : modelData.active_state === "active" ? Theme.good : Theme.faint
font.pixelSize: Theme.fontMeta
font.weight: 700
Layout.preferredWidth: 130
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: parent.color = Theme.hover
onExited: parent.color = "transparent"
onClicked: {
app.pendingProcessQuery = String(modelData.name || "") + ".service";
app.navigate("processes", true);
}
}
}
}
}
}
}
}
}
}
Component {
id: softwarePage
Item {
id: softwareRoot
property string query: softwareSearch.text.toLowerCase()
property var rows: app.software.filter((p) => {
return !query.length || String(p.name).toLowerCase().includes(query) || String(p.description).toLowerCase().includes(query) || String(p.package_type).toLowerCase().includes(query);
})
ScrollView {
id: softwareBody
anchors.fill: parent
padding: app.pageMargin
contentWidth: availableWidth
clip: true
ColumnLayout {
width: softwareBody.availableWidth
height: Math.max(implicitHeight, softwareBody.availableHeight)
spacing: 12
PageHeader {
Layout.fillWidth: true
title: "Software"
subtitle: "Everything the machine's package managers say is installed. Select one to follow its ownership and dependency edges."
TextField {
id: softwareSearch
text: app.savedQueries.software || ""
onTextChanged: app.rememberQuery("software", text)
Layout.preferredWidth: 320
placeholderText: "Find installed software"
color: Theme.text
placeholderTextColor: Theme.faint
background: Rectangle {
color: Theme.surface
border.width: 1
border.color: softwareSearch.activeFocus ? Theme.red : Theme.divider
radius: Theme.radiusSm
}
}
AppButton {
text: machine.softwareLoading ? "Reading…" : "Refresh"
enabled: !machine.softwareLoading
onClicked: machine.refreshSoftware()
}
}
GridLayout {
Layout.fillWidth: true
columns: width < 700 ? 2 : 4
columnSpacing: 12
rowSpacing: 12
MetricCard {
Layout.fillWidth: true
label: "Installed"
value: String(machine.softwareCount)
detail: "known local packages"
accent: Theme.blue
}
MetricCard {
Layout.fillWidth: true
label: "Explicit"
value: String(machine.softwareExplicitCount)
detail: "chosen by an operator"
accent: Theme.cyan
}
MetricCard {
Layout.fillWidth: true
label: "Dependencies"
value: String(machine.softwareDependencyCount)
detail: "pulled into closures"
accent: Theme.purple
}
MetricCard {
Layout.fillWidth: true
label: "Foreign"
value: String(machine.softwareForeignCount)
detail: "outside known repositories"
accent: machine.softwareForeignCount > 0 ? Theme.warn : Theme.good
}
}
ScrollView {
id: softwareTable
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: 200
clip: true
contentWidth: Math.max(availableWidth, 800)
contentHeight: availableHeight
bottomPadding: availableWidth < 800 ? 14 : 0
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
Rectangle {
width: Math.max(softwareTable.availableWidth, 800)
height: softwareTable.availableHeight
color: Theme.surface
radius: Theme.radius
border.width: 1
border.color: Theme.divider
ListView {
anchors.fill: parent
clip: true
model: softwareRoot.rows
Text {
visible: app.software.length === 0 && !machine.softwareLoading
anchors.centerIn: parent
text: "The Agent reported no installed package inventory."
color: Theme.faint
font.pixelSize: 12
}
delegate: Rectangle {
required property var modelData
width: ListView.view.width
height: 58
color: packageMouse.containsMouse ? Theme.hover : "transparent"
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: 1
color: Theme.divider
}
RowLayout {
anchors.fill: parent
anchors.margins: 13
Column {
Layout.fillWidth: true
Text {
text: modelData.name || modelData.identity
color: Theme.text
font.pixelSize: 12
font.family: Theme.mono
font.weight: 600
}
Text {
text: modelData.description || "No description reported"
color: Theme.dim
font.pixelSize: Theme.fontMeta
elide: Text.ElideRight
width: parent.width
}
}
Text {
text: modelData.version || "unknown"
color: Theme.text
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
Layout.preferredWidth: 180
elide: Text.ElideRight
}
Text {
text: String(modelData.install_reason || "unknown").toUpperCase()
color: modelData.install_reason === "explicit" ? Theme.cyan : Theme.faint
font.pixelSize: Theme.fontMeta
font.weight: 700
Layout.preferredWidth: 100
}
Text {
text: String(modelData.package_type || "unknown").toUpperCase()
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.weight: 700
Layout.preferredWidth: 72
}
Text {
text: modelData.origin === "foreign" ? "FOREIGN" : ""
color: Theme.warn
font.pixelSize: Theme.fontMeta
font.weight: 700
Layout.preferredWidth: 64
}
}
MouseArea {
id: packageMouse
anchors.fill: parent
hoverEnabled: true
onClicked: app.openSoftware(modelData, false)
}
}
}
}
}
}
}
}
}
Component {
id: updatesPage
Item {
ScrollView {
id: updatesBody
anchors.fill: parent
padding: app.pageMargin
contentWidth: availableWidth
clip: true
ColumnLayout {
width: updatesBody.availableWidth
height: Math.max(implicitHeight, updatesBody.availableHeight)
spacing: 12
PageHeader {
Layout.fillWidth: true
title: "Updates"
subtitle: "Available changes, package context, and the results of this machines latest scans."
AppButton {
text: machine.scanRunning ? "Scanning…" : machine.scanRequestRunning ? "Requesting…" : "Scan now"
enabled: machine.connected && !machine.scanRunning && !machine.scanRequestRunning
onClicked: machine.triggerScan()
background: Rectangle {
color: parent.pressed ? Qt.darker(Theme.titlebar, 1.15) : Theme.titlebar
radius: Theme.radiusSm
}
contentItem: Text {
text: parent.text
color: "white"
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pixelSize: 11
font.weight: 650
}
}
}
ScanPanel {
Layout.fillWidth: true
snapshot: app.scanStatus
running: machine.scanRunning
}
RowLayout {
Layout.fillWidth: true
TextField {
id: updateSearch
Layout.fillWidth: true
text: app.updateQuery
placeholderText: "Find package, description, source, or advisory"
color: Theme.text
placeholderTextColor: Theme.faint
selectByMouse: true
onTextEdited: app.updateQuery = text.trim()
background: Rectangle { color: Theme.surface; border.color: updateSearch.activeFocus ? Theme.blue : Theme.divider; radius: Theme.radiusSm }
}
ComboBox {
model: ["All updates", "High & critical", "With advisories"]
currentIndex: app.updateFilter
onActivated: app.updateFilter = currentIndex
}
}
Rectangle {
visible: machine.operationMessage.length > 0
Layout.fillWidth: true
Layout.preferredHeight: operationText.implicitHeight + 20
color: Theme.redDim
radius: Theme.radiusSm
Text {
id: operationText
anchors.centerIn: parent
width: parent.width - 24
wrapMode: Text.Wrap
textFormat: Text.PlainText
text: machine.operationMessage
color: Theme.dim
font.pixelSize: 11
}
}
ScrollView {
id: updatesTable
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumHeight: 200
clip: true
contentWidth: Math.max(availableWidth, 760)
contentHeight: availableHeight
bottomPadding: availableWidth < 760 ? 14 : 0
ScrollBar.vertical.policy: ScrollBar.AlwaysOff
Rectangle {
width: Math.max(updatesTable.availableWidth, 760)
height: updatesTable.availableHeight
color: Theme.surface
radius: Theme.radius
border.width: 1
border.color: Theme.divider
ListView {
anchors.fill: parent
clip: true
model: app.filteredUpdates
Text {
visible: app.filteredUpdates.length === 0
anchors.centerIn: parent
width: Math.min(parent.width - 40, 520)
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.Wrap
text: app.updates.length ? "No updates match these filters." : machine.scanRunning ? "Checking for available updates…" : !Object.keys(app.scanStatus.scanners || {}).length ? "Run a scan to check this machine for updates." : Object.keys(app.scanStatus.scanners || {}).some(k => app.scanStatus.scanners[k].status !== "success") ? "No updates listed. Some scans did not complete; review scan status above." : "No updates reported by the completed scans."
color: Theme.dim
font.pixelSize: 12
}
delegate: Rectangle {
required property var modelData
width: ListView.view.width
height: 88
color: "transparent"
Rectangle {
anchors.bottom: parent.bottom
width: parent.width
height: 1
color: Theme.divider
}
RowLayout {
anchors.fill: parent
anchors.margins: 14
Rectangle {
width: 8
height: 8
radius: 4
color: modelData.severity === "critical" ? Theme.bad : modelData.severity === "high" || modelData.severity === "important" ? Theme.warn : Theme.faint
}
Column {
Layout.fillWidth: true
Text {
text: modelData.package_name
color: Theme.text
font.pixelSize: 13
font.family: Theme.mono
font.weight: 600
}
Text {
width: parent.width
text: modelData.package_description || modelData.repository_source || "No package description reported"
textFormat: Text.PlainText
elide: Text.ElideRight
color: Theme.dim
font.pixelSize: Theme.fontMeta
}
Text {
text: (modelData.current_version || "?") + " → " + (modelData.available_version || "?") + (modelData.cve_list && modelData.cve_list.length ? " · " + modelData.cve_list.length + " advisories" : "")
color: Theme.dim
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
}
}
Text {
text: String(modelData.package_type || "unknown").toUpperCase()
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.weight: 700
Layout.preferredWidth: 90
}
Column {
Layout.preferredWidth: 150
Text {
text: app.updateState(modelData)
color: text === "NEEDS ATTENTION" ? Theme.bad : text.indexOf("INSTALLED") === 0 ? Theme.good : Theme.warn
font.pixelSize: Theme.fontMeta
font.weight: 700
anchors.right: parent.right
}
Text {
text: app.locallyApprovable(modelData) && machine.enrollment !== "fleet enrolled" ? "authorize here" : "awaiting authorization"
color: Theme.faint
font.pixelSize: Theme.fontMeta
anchors.right: parent.right
}
}
}
MouseArea {
anchors.fill: parent
hoverEnabled: true
onEntered: parent.color = Theme.hover
onExited: parent.color = "transparent"
onClicked: {
app.selectedUpdate = modelData;
const record = app.updateRecord(modelData);
const context = record ? record.metadata || {} : {};
machine.approvalJson = JSON.stringify(context.result || (context.error ? {error: context.error} : {}));
updateDialog.open();
}
}
}
}
}
}
Text {
text: app.filteredUpdates.length + " of " + app.updates.length + " updates · Select a package to inspect advisories and installation options."
color: Theme.faint
font.pixelSize: Theme.fontMeta
Layout.alignment: Qt.AlignHCenter
}
}
}
}
}
Component {
id: securityPage
ScrollView {
id: securityScroll
clip: true
contentWidth: availableWidth
leftPadding: app.pageMargin
rightPadding: app.pageMargin
ColumnLayout {
width: securityScroll.availableWidth
anchors.margins: app.pageMargin
spacing: 14
Text {
text: "Security & supply chain"
color: Theme.text
font.pixelSize: Theme.fontTitle
font.weight: 700
Layout.topMargin: 20
}
Text {
Layout.fillWidth: true
wrapMode: Text.Wrap
text: "Evidence and enforcement state. Unknown stays unknown; no invented green checkmarks."
color: Theme.dim
font.pixelSize: Theme.fontBody
}
GridLayout {
Layout.fillWidth: true
columns: width < 560 ? 1 : 2
columnSpacing: 12
rowSpacing: 12
Repeater {
model: [{
"label": "Command signing",
"value": app.security.command_signing_enabled ? "Enabled" : "Disabled",
"detail": app.security.command_enforcement || "unknown",
"ok": app.security.command_signing_enabled && app.security.command_enforcement === "strict"
}, {
"label": "TLS verification",
"value": app.security.tls_verification ? "Enforced" : "Bypassed",
"detail": "Agent to fleet server",
"ok": app.security.tls_verification
}, {
"label": "Security logging",
"value": app.security.security_logging ? "Enabled" : "Disabled",
"detail": "local security event channel",
"ok": app.security.security_logging
}, {
"label": "Kernel enforcement",
"value": app.security.kernel_enforcement ? "Configured" : "Not configured",
"detail": app.security.kernel_fail_closed ? "fail closed" : "fail open / unknown",
"ok": app.security.kernel_enforcement && app.security.kernel_fail_closed
}, {
"label": "Critical updates",
"value": String(machine.criticalCount),
"detail": "reported package findings",
"ok": machine.criticalCount === 0
}, {
"label": "Agent mode",
"value": app.security.degraded_mode ? "Degraded" : "Normal",
"detail": machine.enrollment,
"ok": !app.security.degraded_mode
}, {
"label": "Capability tokens",
"value": String((app.security.capabilities || {
}).pending_count || 0) + " pending",
"detail": ((app.security.capabilities || {
}).last_failed_count || 0) + " failed · " + ((app.security.capabilities || {
}).last_processed_count || 0) + " processed",
"ok": ((app.security.capabilities || {
}).last_failed_count || 0) === 0
}, {
"label": "Privileged processes",
"value": app.processes.length ? String(app.sharpProcessCount()) : "unscanned",
"detail": app.processes.length ? "hold a capability that can rewrite this machine" : "open Processes to scan",
"ok": app.processes.length > 0 && app.sharpProcessCount() === 0
}]
Rectangle {
required property var modelData
Layout.fillWidth: true
Layout.preferredHeight: 94
color: Theme.surface
radius: Theme.radius
border.width: 1
border.color: Theme.divider
Rectangle {
width: 4
height: parent.height - 26
radius: 2
color: modelData.ok ? Theme.good : Theme.warn
anchors.left: parent.left
anchors.leftMargin: 14
anchors.verticalCenter: parent.verticalCenter
}
Column {
anchors.left: parent.left
anchors.leftMargin: 31
anchors.verticalCenter: parent.verticalCenter
Text {
text: modelData.label.toUpperCase()
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1
}
Text {
text: modelData.value
color: modelData.ok ? Theme.good : Theme.warn
font.pixelSize: 19
font.weight: 650
}
Text {
text: modelData.detail
color: Theme.dim
font.pixelSize: Theme.fontMeta
}
}
}
}
}
Rectangle {
Layout.fillWidth: true
Layout.preferredHeight: 100
color: Theme.redDim
radius: Theme.radius
border.width: 1
border.color: Theme.tint(Theme.red, 0.45)
Column {
anchors.fill: parent
anchors.margins: 15
spacing: 5
Text {
text: "AUTHORITY BOUNDARY"
color: Theme.red
font.pixelSize: Theme.fontMeta
font.weight: 750
font.letterSpacing: 1
}
Text {
width: parent.width
text: "QML observes RedFlag state and expresses operator intent. The Agent resolves policy and authorization. The privileged helper verifies a bounded mutation envelope before any platform backend runs."
color: Theme.text
font.pixelSize: 12
wrapMode: Text.Wrap
}
Text {
text: "Override uses the same machinery and must leave actor, reason, target, bypass, and aftermath in history."
color: Theme.dim
font.pixelSize: Theme.fontMeta
}
}
}
Item {
Layout.preferredHeight: 22
}
}
}
}
Component {
id: historyPage
HistoryPanel {
events: app.events
loading: machine.overviewLoading
onRefreshRequested: machine.refreshOverview()
}
}
Dialog {
id: processDialog
padding: 20
width: Math.min(760, app.width - (app.compact ? 24 : 80))
height: Math.min(650, app.height - (app.compact ? 24 : 80))
anchors.centerIn: parent
modal: true
title: app.processDetail.name ? app.processDetail.name + " · PID " + app.processDetail.pid : "Process"
background: Rectangle {
color: Theme.surface
radius: Theme.radius
border.width: 1
border.color: Theme.divider
}
header: Rectangle {
width: parent.width
height: 54
color: Theme.raised
Text {
anchors.left: parent.left
anchors.leftMargin: 16
anchors.verticalCenter: parent.verticalCenter
text: processDialog.title
color: Theme.text
font.pixelSize: 16
font.weight: 650
}
}
contentItem: ScrollView {
clip: true
ColumnLayout {
width: processDialog.availableWidth
spacing: 8
Text {
text: app.processDetail.error || app.processDetail.path || "Path unavailable"
color: app.processDetail.error ? Theme.bad : Theme.dim
font.pixelSize: 11
font.family: Theme.mono
wrapMode: Text.Wrap
Layout.fillWidth: true
}
Text {
text: app.processDetail.cmdline || ""
color: Theme.text
font.pixelSize: 11
font.family: Theme.mono
wrapMode: Text.Wrap
Layout.fillWidth: true
}
Text {
visible: text.length > 0
text: app.processDetail.cgroup || ""
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
wrapMode: Text.Wrap
Layout.fillWidth: true
}
GridLayout {
columns: width < 700 ? 2 : 4
Layout.fillWidth: true
columnSpacing: 12
rowSpacing: 8
Repeater {
model: [{
"l": "Owner",
"v": app.processDetail.user || app.processDetail.uid || "—"
}, {
"l": "State",
"v": app.processDetail.state || "—"
}, {
"l": "CPU",
"v": Number(app.processDetail.cpu_percent || 0).toFixed(1) + "%"
}, {
"l": "Memory",
"v": app.bytes(app.processDetail.rss_bytes)
}, {
"l": "Threads",
"v": app.processDetail.threads || 0
}, {
"l": "Parent PID",
"v": app.processDetail.parent_pid || 0
}, {
"l": "Read",
"v": app.bytes(app.processDetail.disk_bytes_read)
}, {
"l": "Written",
"v": app.bytes(app.processDetail.disk_bytes_written)
}, {
"l": "Belongs to",
"v": app.ownerLabel(app.processDetail) || "unattributed"
}, {
"l": "Package",
"v": app.processDetail.package_name ? app.processDetail.package_name + " · " + String(app.processDetail.package_manager || "").toUpperCase() : "unattributed"
}, {
"l": "Elevation",
"v": app.processDetail.elevation_status || "normal"
}]
Rectangle {
required property var modelData
Layout.fillWidth: true
Layout.preferredHeight: 62
color: Theme.raised
radius: Theme.radiusSm
Column {
anchors.centerIn: parent
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: modelData.l.toUpperCase()
color: Theme.faint
font.pixelSize: Theme.fontMeta
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: modelData.v
color: Theme.text
font.pixelSize: 12
font.family: Theme.mono
}
}
}
}
}
Text {
text: "LISTENERS & CONNECTIONS"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1
Layout.topMargin: 8
}
Repeater {
model: app.processDetail.open_sockets || []
Rectangle {
required property var modelData
Layout.fillWidth: true
Layout.preferredHeight: 32
color: Theme.raised
radius: Theme.radiusSm
RowLayout {
anchors.fill: parent
anchors.margins: 8
Text {
text: modelData.protocol + " " + modelData.family
color: Theme.dim
font.pixelSize: Theme.fontMeta
Layout.preferredWidth: 70
}
Text {
text: app.endpoint(modelData.local_addr, modelData.local_port)
color: Theme.text
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
Layout.fillWidth: true
}
Text {
text: modelData.remote_port ? app.endpoint(modelData.remote_addr, modelData.remote_port) : modelData.path || "—"
color: Theme.dim
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
Layout.fillWidth: true
}
Text {
text: modelData.state || ""
color: Theme.faint
font.pixelSize: Theme.fontMeta
}
}
}
}
Text {
text: "EFFECTIVE CAPABILITIES · " + ((app.processDetail.capabilities || []).length || "none")
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1
Layout.topMargin: 8
}
Flow {
Layout.fillWidth: true
spacing: 6
Repeater {
model: app.processDetail.capabilities || []
Rectangle {
required property var modelData
width: capText.implicitWidth + 14
height: 25
radius: 12
color: Theme.raised
Text {
id: capText
anchors.centerIn: parent
text: modelData
color: app.capabilityColor(modelData)
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
}
}
}
}
Text {
text: "NAMESPACES"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1
Layout.topMargin: 8
}
Flow {
Layout.fillWidth: true
spacing: 6
Repeater {
model: app.processDetail.namespaces || []
Rectangle {
required property var modelData
width: nsText.implicitWidth + 14
height: 25
radius: 12
color: Theme.raised
Text {
id: nsText
anchors.centerIn: parent
text: modelData.type + ":" + modelData.inode
color: Theme.dim
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
}
}
}
}
}
}
footer: Rectangle {
implicitHeight: 60
color: Theme.raised
AppButton {
anchors.right: parent.right
anchors.rightMargin: 16
anchors.verticalCenter: parent.verticalCenter
text: " Back to " + app.pageLabel(app.page)
onClicked: processDialog.close()
}
}
}
Dialog {
id: softwareDialog
onClosed: app.softwareDetailWanted = false
padding: 20
width: Math.min(840, app.width - (app.compact ? 24 : 80))
height: Math.min(700, app.height - (app.compact ? 24 : 80))
anchors.centerIn: parent
modal: true
title: app.softwareDetail.name || app.softwareDetail.identity || "Installed software"
background: Rectangle {
color: Theme.surface
radius: Theme.radius
border.width: 1
border.color: Theme.divider
}
header: Rectangle {
width: parent.width
height: 68
color: Theme.raised
RowLayout {
anchors.fill: parent
anchors.leftMargin: 17
anchors.rightMargin: 17
Column {
Layout.fillWidth: true
Text {
text: softwareDialog.title
color: Theme.text
font.pixelSize: 17
font.weight: 650
font.family: Theme.mono
}
Text {
text: (app.softwareDetail.version || "unknown") + (app.softwareDetail.architecture ? " · " + app.softwareDetail.architecture : "")
color: Theme.dim
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
}
}
Text {
text: String(app.softwareDetail.package_type || "unknown").toUpperCase()
color: Theme.cyan
font.pixelSize: Theme.fontMeta
font.weight: 700
font.letterSpacing: 1
}
}
}
contentItem: ScrollView {
clip: true
contentWidth: availableWidth
ColumnLayout {
width: softwareDialog.availableWidth
spacing: 10
Text {
Layout.fillWidth: true
text: (app.softwareDetailLoading ? "Loading… · " : "") + app.pageLabel(app.page) + " / " + app.softwareTrail.map((p) => {
return p.name || p.identity;
}).concat([app.softwareDetail.name || app.softwareDetail.identity || "Package"]).join(" / ")
color: Theme.dim
font.pixelSize: Theme.fontMeta
wrapMode: Text.Wrap
}
Text {
visible: app.softwareDetail.error !== undefined
text: app.softwareDetail.error || "Software detail unavailable"
color: Theme.bad
font.pixelSize: 11
font.family: Theme.mono
wrapMode: Text.Wrap
Layout.fillWidth: true
}
Text {
visible: app.softwareDetail.error === undefined
text: app.softwareDetail.description || "No description reported by the package manager."
color: Theme.text
font.pixelSize: 12
wrapMode: Text.Wrap
Layout.fillWidth: true
}
GridLayout {
visible: app.softwareDetail.error === undefined
columns: width < 700 ? 2 : 4
Layout.fillWidth: true
columnSpacing: 10
rowSpacing: 8
Repeater {
model: [{
"l": "Reason",
"v": app.softwareDetail.install_reason || "unknown"
}, {
"l": "Origin",
"v": app.softwareDetail.origin || "unknown"
}, {
"l": "Installed size",
"v": app.bytes(app.softwareDetail.installed_size_bytes)
}, {
"l": "Files",
"v": String(app.softwareDetail.file_count || 0) + (app.softwareDetail.files_truncated ? "+" : "")
}, {
"l": "Packager",
"v": app.softwareDetail.packager || "unknown"
}, {
"l": "Build date",
"v": app.softwareDetail.build_date || "unknown"
}, {
"l": "Installed",
"v": app.softwareDetail.installed_at || "unknown"
}, {
"l": "Identity",
"v": app.softwareDetail.identity || "unknown"
}]
Rectangle {
required property var modelData
Layout.fillWidth: true
Layout.preferredHeight: 62
color: Theme.raised
radius: Theme.radiusSm
Column {
anchors.centerIn: parent
width: parent.width - 14
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: modelData.l.toUpperCase()
color: Theme.faint
font.pixelSize: Theme.fontMeta
}
Text {
width: parent.width
horizontalAlignment: Text.AlignHCenter
text: modelData.v
color: Theme.text
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
elide: Text.ElideMiddle
}
}
}
}
}
Text {
visible: (app.softwareDetail.depends_on || []).length > 0
text: "DEPENDENCY CLOSURE EDGES"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1
Layout.topMargin: 5
}
Flow {
visible: (app.softwareDetail.depends_on || []).length > 0
Layout.fillWidth: true
spacing: 6
Repeater {
model: app.softwareDetail.depends_on || []
AppButton {
required property var modelData
readonly property var target: app.installedEdge(modelData)
text: String(modelData) + (target ? " " : "")
enabled: target !== null && !app.softwareDetailLoading
onClicked: app.openSoftware(target, true)
ToolTip.visible: hovered
ToolTip.text: target ? "Open installed package" : "No unique installed package identity was reported for this edge"
}
}
}
Text {
visible: (app.softwareDetail.required_by || []).length > 0
text: "REQUIRED BY"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1
Layout.topMargin: 5
}
Flow {
visible: (app.softwareDetail.required_by || []).length > 0
Layout.fillWidth: true
spacing: 6
Repeater {
model: app.softwareDetail.required_by || []
AppButton {
required property var modelData
readonly property var target: app.installedEdge(modelData)
text: String(modelData) + (target ? " " : "")
enabled: target !== null && !app.softwareDetailLoading
onClicked: app.openSoftware(target, true)
ToolTip.visible: hovered
ToolTip.text: target ? "Open installed package" : "No unique installed package identity was reported for this edge"
}
}
}
Text {
visible: (app.softwareDetail.provides || []).length > 0
text: "PROVIDES"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1
Layout.topMargin: 5
}
Text {
visible: (app.softwareDetail.provides || []).length > 0
Layout.fillWidth: true
text: (app.softwareDetail.provides || []).join(" · ")
color: Theme.dim
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
wrapMode: Text.Wrap
}
Text {
visible: (app.softwareDetail.files || []).length > 0
text: "FILES"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1
Layout.topMargin: 5
}
Repeater {
model: (app.softwareDetail.files || []).slice(0, 80)
Text {
required property var modelData
Layout.fillWidth: true
text: modelData
color: Theme.dim
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
elide: Text.ElideMiddle
}
}
Text {
visible: (app.softwareDetail.file_count || 0) > 80
text: "Showing 80 of " + app.softwareDetail.file_count + " paths"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
}
}
}
footer: Rectangle {
implicitHeight: 60
color: Theme.raised
AppButton {
anchors.right: parent.right
anchors.rightMargin: 16
anchors.verticalCenter: parent.verticalCenter
text: " Back to " + (app.softwareTrail.length ? (app.softwareTrail[app.softwareTrail.length - 1].name || app.softwareTrail[app.softwareTrail.length - 1].identity) : app.pageLabel(app.page))
enabled: !app.softwareDetailLoading
onClicked: app.backSoftware()
}
}
}
Dialog {
id: updateDialog
padding: 20
width: Math.min(820, app.width - (app.compact ? 24 : 80))
height: Math.min(700, app.height - (app.compact ? 24 : 80))
anchors.centerIn: parent
modal: true
onOpened: {
overrideField.text = "";
overrideToggle.checked = false;
}
onClosed: machine.refreshOverview()
background: Rectangle {
color: Theme.surface
radius: Theme.radius
border.width: 1
border.color: Theme.divider
}
header: Rectangle {
width: parent.width
height: 62
color: Theme.raised
Column {
anchors.left: parent.left
anchors.leftMargin: 16
anchors.verticalCenter: parent.verticalCenter
spacing: 2
Text {
text: app.selectedUpdate.package_name || "Update"
color: Theme.text
font.pixelSize: 16
font.weight: 650
font.family: Theme.mono
}
Text {
text: (app.selectedUpdate.current_version || "?") + " → " + (app.selectedUpdate.available_version || "?") + " · " + String(app.selectedUpdate.package_type || "").toUpperCase()
color: Theme.dim
font.pixelSize: 11
font.family: Theme.mono
}
}
Text {
anchors.right: parent.right
anchors.rightMargin: 16
anchors.verticalCenter: parent.verticalCenter
text: String(app.selectedUpdate.severity || "unknown").toUpperCase()
color: app.selectedUpdate.severity === "critical" ? Theme.bad : app.selectedUpdate.severity === "high" || app.selectedUpdate.severity === "important" ? Theme.warn : Theme.faint
font.pixelSize: Theme.fontMeta
font.weight: 700
font.letterSpacing: 1
}
}
contentItem: ScrollView {
clip: true
ColumnLayout {
width: updateDialog.availableWidth
spacing: 10
Text {
text: app.selectedUpdate.package_description || "No package description reported by this source."
Layout.fillWidth: true
wrapMode: Text.Wrap
textFormat: Text.PlainText
color: Theme.text
font.pixelSize: Theme.fontBody
}
Text {
Layout.fillWidth: true
text: "Source: " + (app.selectedUpdate.repository_source || "not reported") + (app.selectedUpdate.size_bytes ? " · Download: " + app.bytes(app.selectedUpdate.size_bytes) : "")
wrapMode: Text.Wrap
color: Theme.dim
font.pixelSize: Theme.fontMeta
}
Text {
text: "INSTALLATION STATUS"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1
}
GridLayout {
id: lifecycleGrid
Layout.fillWidth: true
columns: width < 640 ? 2 : 4
columnSpacing: 12
rowSpacing: 16
Repeater {
model: [{
"label": "Discovered",
"detail": app.selectedUpdate.repository_source || "reported by the scanner"
}, {
"label": "Checked",
"detail": app.selectedUpdate.package_type === "pacman" ? "closure · hashes · package signatures" : "closure resolved · hashes pinned · OSV"
}, {
"label": "Authorized",
"detail": app.selectedUpdate.package_type === "pacman" ? "mutation envelope signed for this host" : "capability minted for this host"
}, {
"label": "Installed",
"detail": "helper verified and executed"
}]
Item {
required property var modelData
required property int index
Layout.fillWidth: true
Layout.preferredHeight: 110
Rectangle {
id: dot
width: 28
height: 28
radius: 14
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 4
color: app.stageColor(app.stageState(index))
opacity: app.stageState(index) === "idle" ? 0.5 : 1
Text {
anchors.centerIn: parent
text: app.stageState(index) === "done" ? "✓" : app.stageState(index) === "failed" ? "!" : String(index + 1)
color: Theme.window
font.pixelSize: Theme.fontBody
font.bold: true
}
}
Rectangle {
visible: index < 3 && (index + 1) % lifecycleGrid.columns !== 0
height: 2
anchors.left: dot.right
anchors.right: parent.right
anchors.verticalCenter: dot.verticalCenter
color: app.stageColor(app.stageState(index + 1))
opacity: 0.35
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: dot.bottom
anchors.topMargin: 7
text: modelData.label
color: Theme.text
font.pixelSize: 11
font.weight: 600
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: dot.bottom
anchors.topMargin: 28
width: parent.width - 12
horizontalAlignment: Text.AlignHCenter
text: modelData.detail
color: Theme.faint
font.pixelSize: Theme.fontMeta
wrapMode: Text.Wrap
}
}
}
}
Text {
visible: (app.selectedUpdate.cve_list || []).length > 0
text: "ADVISORIES"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1
Layout.topMargin: 6
}
Flow {
Layout.fillWidth: true
spacing: 6
Repeater {
model: app.selectedUpdate.cve_list || []
Rectangle {
required property var modelData
width: cveText.implicitWidth + 14
height: 25
radius: 12
color: Theme.redDim
Text {
id: cveText
anchors.centerIn: parent
text: modelData
color: Theme.bad
font.pixelSize: Theme.fontMeta
font.family: Theme.mono
}
}
}
}
Text {
visible: app.approval.request_id !== undefined || app.approval.error !== undefined
text: "GATE EVIDENCE"
color: Theme.faint
font.pixelSize: Theme.fontMeta
font.letterSpacing: 1
Layout.topMargin: 6
}
GridLayout {
visible: app.approval.request_id !== undefined
columns: width < 700 ? 2 : 4
Layout.fillWidth: true
columnSpacing: 10
rowSpacing: 8
Repeater {
model: [{
"l": "Closure",
"v": String(app.approval.closure_size !== undefined ? app.approval.closure_size : "—")
}, {
"l": "OSV",
"v": app.approval.osv_status || "—"
}, {
"l": "Vulnerabilities",
"v": String(app.approval.osv_vuln_count !== undefined ? app.approval.osv_vuln_count : "—")
}, {
"l": "Verified artifacts",
"v": String(app.verifiedActionCount())
}, {
"l": "Decision",
"v": app.approvalResult().decision || "—"
}, {
"l": "Exit code",
"v": String(app.approvalResult().exit_code !== undefined ? app.approvalResult().exit_code : "—")
}, {
"l": "Executed",
"v": app.approvalResult().executed ? "yes" : "no"
}, {
"l": "Request",
"v": String(app.approval.request_id || "—").substring(0, 12)
}]
Rectangle {
required property var modelData
Layout.fillWidth: true
Layout.preferredHeight: 58
color: Theme.raised
radius: Theme.radiusSm
Column {
anchors.centerIn: parent
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: modelData.l.toUpperCase()
color: Theme.faint
font.pixelSize: Theme.fontMeta
}
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: modelData.v
color: Theme.text
font.pixelSize: 11
font.family: Theme.mono
}
}
}
}
}
Text {
visible: text.length > 0
Layout.fillWidth: true
text: app.approval.error || app.approvalResult().reason || ""
color: app.approval.error ? Theme.bad : Theme.dim
font.pixelSize: 11
font.family: Theme.mono
wrapMode: Text.Wrap
}
Rectangle {
visible: app.approvalBlockReason(app.selectedUpdate).length > 0
Layout.fillWidth: true
Layout.preferredHeight: blockText.implicitHeight + 22
color: Theme.raised
radius: Theme.radiusSm
Text {
id: blockText
anchors.fill: parent
anchors.margins: 11
text: app.approvalBlockReason(app.selectedUpdate)
color: Theme.dim
font.pixelSize: 11
wrapMode: Text.Wrap
}
}
Rectangle {
visible: app.approvalReasonRequired(app.selectedUpdate)
Layout.fillWidth: true
Layout.preferredHeight: pacmanEvidenceText.implicitHeight + 22
color: Theme.raised
radius: Theme.radiusSm
border.width: 1
border.color: Theme.warn
Text {
id: pacmanEvidenceText
anchors.fill: parent
anchors.margins: 11
text: "OSV has no Arch Linux package mapping in this path. RedFlag can resolve and cryptographically verify the exact pacman transaction, but it cannot call the dependency closure advisory-clear. State why you accept that uncertainty; the root helper records the reason beside the request and authorization in its journal."
color: Theme.warn
font.pixelSize: 11
wrapMode: Text.Wrap
}
}
CheckBox {
id: overrideToggle
visible: !app.approvalReasonRequired(app.selectedUpdate) && app.approvalCanOverride() && app.approvalBlockReason(app.selectedUpdate).length === 0 && !app.approvalResult().executed
text: "Override this advisory refusal with recorded operator intent"
onToggled: {
if (!checked)
overrideField.text = "";
}
contentItem: Text {
text: parent.text
color: parent.checked ? Theme.warn : Theme.dim
font.pixelSize: 11
leftPadding: parent.indicator.width + parent.spacing
}
}
TextArea {
id: overrideField
visible: app.approvalReasonRequired(app.selectedUpdate) || overrideToggle.checked
Layout.fillWidth: true
Layout.preferredHeight: 76
placeholderText: app.approvalReasonRequired(app.selectedUpdate) ? "Why this exact signed pacman transaction should proceed without OSV advisory coverage." : "Why the operator is breaking glass. The root helper records this reason beside the authorization in durable history."
wrapMode: TextEdit.Wrap
color: Theme.text
placeholderTextColor: Theme.faint
background: Rectangle {
color: Theme.surface
border.width: 1
border.color: overrideField.activeFocus ? Theme.red : Theme.divider
radius: Theme.radiusSm
}
}
Text {
Layout.fillWidth: true
Layout.topMargin: 4
text: app.selectedUpdate.package_type === "pacman" ? "The Agent resolves a private pacman transaction and downloads every exact archive and detached signature. The root helper stages and verifies identity, hashes, Arch signatures, and forward-only versions before signing the mutation envelope, then repeats those checks before pacman runs. OSV has no Arch mapping here, so acceptance stays explicit. This window never touches pacman." : "Approval resolves the dependency closure, pins every artifact hash it can, checks the set against OSV, mints an Ed25519 capability bound to this host, and hands it to the privileged helper. The helper verifies independently. This window never touches a package manager."
color: Theme.faint
font.pixelSize: Theme.fontMeta
wrapMode: Text.Wrap
}
}
}
footer: DialogButtonBox {
padding: 12
AppButton {
text: machine.approvalRunning ? "Authorizing…" : (app.approvalResult().executed ? "Installed" : app.approvalReasonRequired(app.selectedUpdate) ? "Approve with reason and install" : overrideToggle.checked ? "Override and install" : "Approve and install")
enabled: !machine.approvalRunning && app.approvalBlockReason(app.selectedUpdate).length === 0 && !app.approvalResult().executed && (!(app.approvalReasonRequired(app.selectedUpdate) || overrideToggle.checked) || overrideField.text.trim().length > 0)
DialogButtonBox.buttonRole: DialogButtonBox.AcceptRole
onClicked: machine.approveUpdate(app.selectedUpdate.package_type || "", app.selectedUpdate.package_name || "", app.selectedUpdate.available_version || "", app.approvalReasonRequired(app.selectedUpdate) || overrideToggle.checked ? overrideField.text.trim() : "")
background: Rectangle {
color: !parent.enabled ? Theme.raised : parent.pressed ? Qt.darker(Theme.red, 1.15) : Theme.red
radius: Theme.radiusSm
}
contentItem: Text {
text: parent.text
color: parent.enabled ? "white" : Theme.faint
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pixelSize: 11
font.weight: 650
}
}
Button {
text: "Close"
DialogButtonBox.buttonRole: DialogButtonBox.RejectRole
onClicked: updateDialog.close()
background: Rectangle {
color: parent.pressed ? Theme.hover : "transparent"
border.width: 1
border.color: Theme.divider
radius: Theme.radiusSm
}
contentItem: Text {
text: parent.text
color: Theme.dim
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pixelSize: 11
}
}
background: Rectangle {
color: Theme.raised
}
}
}
}