74 lines
9.7 KiB
TypeScript
74 lines
9.7 KiB
TypeScript
import { useId, useState } from 'react';
|
|
|
|
/** Presentation-only architecture map; never imports engine implementations.
|
|
* Every current/future engine declares engine: true to inherit its persistent neutral frame.
|
|
* Only selection highlights connected arrows.
|
|
*/
|
|
const nodes = [
|
|
{ id: 'input', name: 'Source inputs', caption: 'Scanner reports · GPO backups · catalogs', description: 'Original scanner reports, GPO backups and reference catalogs enter the Core import and evaluation pipeline.' },
|
|
{ id: 'core', engine: true, name: 'Core Engine', caption: 'Authoritative results · always active', description: 'All existing ASSESSOR functions: import, normalization, evaluation, scoring, client records and disclosure. Owns canonical PostgreSQL data. Works independently of optional engines.' },
|
|
{ id: 'database', name: 'Canonical PostgreSQL', caption: 'Core-owned data · disclosed Result DB', description: 'Canonical local datastore. The Core owns persistence, including the stored and verified Result DB projection.' },
|
|
{ id: 'port', name: 'Core read-only port', caption: 'Scoped results · provenance', description: 'Declared read-only Core port. Optional engines read authoritative results without changing stored data. Context and Report Engine read through read-only database access; the Compliance Engine still reads through the running Core repository.' },
|
|
{ id: 'context', engine: true, name: 'Context Engine', caption: 'Derived context · optional', description: 'Reads findings and GPO verdicts through the read-only Core port and read-only database access. Produces versioned clusters, confidence and evidence. In-memory results; no own persistence or Core writes.' },
|
|
{ id: 'report', engine: true, name: 'Report Engine', caption: 'Read-only reporting · optional', description: 'Reads Core results through the read-only Core port and a separate read-only PostgreSQL pool. Produces report projections, management and technical reports. Own file archive; no Core writes.' },
|
|
{ id: 'ui', name: 'Core UI & exports', caption: 'Findings · scores · Result DB', description: 'Application views show authoritative Core findings, verdicts and scores. Result DB exports obey the Core disclosure policy.' },
|
|
{ id: 'context-ui', name: 'Context in the UI', caption: 'Work · Client Info · Pathfinder', description: 'Published Context results appear through existing UI extension points. They never override technical results or client records.' },
|
|
{ id: 'archive', name: 'Report outputs', caption: 'File archive · downloads', description: 'Generated reports are stored in the separate Report Engine file archive and offered for download; no report storage in Core tables.' },
|
|
{ id: 'compliance', engine: true, name: 'Compliance Engine', caption: 'Regulatory control mapping · optional', description: 'Maps findings and GPO baseline comparisons to regulatory controls; ISO/IEC 27001/27002 is the first framework. Reads Core results through the read-only Core port and writes only its own tables. Proposals never change verdicts, scores or client records.' },
|
|
{ id: 'compliance-tables', name: 'Compliance tables', caption: 'Mappings · decisions · batches', description: 'Mapping runs, accept/reject decisions and batches in three engine-owned tables of the canonical PostgreSQL. Written through a separate small pool; engine writes never advance the Core revision.' },
|
|
{ id: 'retrieval', name: 'Retrieval & model', caption: 'Qdrant index · LiteLLM', description: 'The rebuildable Qdrant index of the uploaded control corpus and the configured LiteLLM model. They receive privacy-safe finding text and corpus evidence, never assets or customer values.' },
|
|
{ id: 'compliance-ui', name: 'ISO mapping in the UI', caption: 'Finding Reader · Result DB · Data Hub', description: 'Control proposals, accept/reject decisions, filtered batches and the ISO corpus upload appear through existing UI extension points. A proposal indicates relevance, not proof of compliance.' }
|
|
];
|
|
const relationships = [
|
|
{ from: 'input', to: 'core', label: 'Import', detail: 'Import and evaluation', path: 'M240 340 H310' },
|
|
{ from: 'core', to: 'database', label: 'Read / write', detail: 'Owns, reads and writes Core data, including disclosed Result DB rows', path: 'M420 390 V520', bidirectional: true },
|
|
{ from: 'database', to: 'port', label: 'Read only', detail: 'Read-only database access for optional engines', path: 'M530 570 H600' },
|
|
{ from: 'port', to: 'context', label: 'Read only', detail: 'Scoped authoritative results and provenance', path: 'M710 520 V390' },
|
|
{ from: 'port', to: 'report', label: 'Read only', detail: 'Scoped results, client records and disclosed projections', path: 'M820 570 H890' },
|
|
{ from: 'core', to: 'ui', label: 'Core results', detail: 'Authoritative findings, verdicts, scores and disclosure rules', path: 'M420 290 V160' },
|
|
{ from: 'context', to: 'context-ui', label: 'Context', detail: 'Published context results through existing UI extension points', path: 'M710 290 V160' },
|
|
{ from: 'report', to: 'archive', label: 'Reports', detail: 'Generated reports; separate from Core database storage', path: 'M1000 520 V390' },
|
|
{ from: 'context', to: 'report', label: 'Published', detail: 'Versioned Context publication for the Management Report, composed by the Core backend; neither engine imports the other', path: 'M820 340 H855 V490 H940 V520', planned: false },
|
|
{ from: 'port', to: 'compliance', label: 'Read only', detail: 'Single Result DB row by client, filtered Result DB pages, client, assessment and scan-run lists', path: 'M710 620 V750' },
|
|
{ from: 'compliance', to: 'compliance-tables', label: 'Own data', detail: 'Mapping runs, decisions and batches; never Core tables', path: 'M600 800 H530', bidirectional: true },
|
|
{ from: 'compliance', to: 'retrieval', label: 'Retrieval', detail: 'Privacy-safe query, retrieved control evidence and model proposals', path: 'M820 800 H890', bidirectional: true },
|
|
{ from: 'compliance', to: 'compliance-ui', label: 'Proposals', detail: 'Grounded control proposals and decisions through existing UI extension points', path: 'M710 850 V900 H130 V850' }
|
|
];
|
|
const nodeName = (id: string) => nodes.find(node => node.id === id)!.name;
|
|
|
|
export function EngineDataFlow() {
|
|
const markerId = useId().replace(/:/g, '');
|
|
const [selected, setSelected] = useState('core');
|
|
const active = nodes.find(node => node.id === selected);
|
|
const connections = relationships.filter(edge => edge.from === selected || edge.to === selected);
|
|
const related = new Set(connections.flatMap(edge => [edge.from, edge.to]));
|
|
return <section className="db-engine-flow" aria-label="Engine data flow">
|
|
<header className="db-engine-flow__heading"><h3>Engines & relationships</h3><span>Solid: implemented · Dashed: planned</span></header>
|
|
<div className="db-engine-flow__scroll" role="region" aria-label="Interactive engine diagram" tabIndex={0}>
|
|
<div className="db-engine-flow__map">
|
|
<svg className="db-engine-flow__edges" width="1130" height="940" aria-hidden="true">
|
|
<defs><marker id={markerId} viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0 L10 5 L0 10 Z" /></marker></defs>
|
|
{relationships.map(edge => <path key={`${edge.from}-${edge.to}`} d={edge.path} markerEnd={`url(#${markerId})`} markerStart={edge.bidirectional ? `url(#${markerId})` : undefined}
|
|
className={`${edge.planned ? 'is-planned' : ''} ${connections.includes(edge) ? 'is-related' : ''}`} />)}
|
|
</svg>
|
|
{relationships.map(edge => <span key={`${edge.from}-${edge.to}`} data-edge={`${edge.from}-${edge.to}`} className={`db-engine-flow__edge-label${edge.planned ? ' is-planned' : ''}`}>{edge.label}</span>)}
|
|
{nodes.map(node => <button type="button" key={node.id} aria-pressed={selected === node.id}
|
|
className={`db-engine-flow__node${node.engine ? ' is-engine' : ''}${related.has(node.id) ? ' is-related' : ''}`}
|
|
data-node={node.id} onClick={() => setSelected(node.id === selected ? '' : node.id)}>
|
|
<strong>{node.name}</strong><span>{node.caption}</span>
|
|
</button>)}
|
|
</div>
|
|
</div>
|
|
{active ? <section className="db-engine-flow__inspector" aria-label="Selected flow details" aria-live="polite">
|
|
<div><h4>{active.name}</h4><p>{active.description}</p></div>
|
|
<ul>{connections.map(edge => <li key={`${edge.from}-${edge.to}`}><strong>{nodeName(edge.from)} {edge.bidirectional ? '↔' : '→'} {nodeName(edge.to)}</strong><span>{edge.detail}{edge.planned ? ' · Planned' : ''}</span></li>)}</ul>
|
|
</section> : <p className="db-engine-flow__boundary">Select a module to inspect its data flow. Modules also support keyboard selection.</p>}
|
|
<p className="db-engine-flow__boundary">Arrows show data delivery. Engine dependencies point toward the Core; the Core never depends on an optional engine. Engines cannot override verdicts, scores or client records. Every output follows Core privacy and Result DB disclosure rules.</p>
|
|
<details className="db-engine-flow__dictionary"><summary>All relationships ({relationships.length})</summary><div className="db-engine-flow__scroll"><table aria-label="Directed engine relationships">
|
|
<thead><tr><th scope="col">From</th><th scope="col">To</th><th scope="col">Data flow</th><th scope="col">Implementation</th></tr></thead>
|
|
<tbody>{relationships.map(edge => <tr key={`${edge.from}-${edge.to}`}><td>{nodeName(edge.from)}</td><td>{edge.bidirectional ? '↔' : '→'} {nodeName(edge.to)}</td><td>{edge.detail}</td><td>{edge.planned ? 'Planned' : 'Implemented'}</td></tr>)}</tbody>
|
|
</table></div></details>
|
|
<p className="db-engine-flow__boundary">This architecture overview includes disabled engines. The shared Engines control governs optional UI requests; disabling an engine does not cancel submitted backend work.</p>
|
|
</section>;
|
|
}
|