Architecture Operating Model
SupportArchitecture Operating Model 2025–2035
:root{ --bg:#f6f8fb; --panel:#fff; --ink:#0f172a; --muted:#475569; --blue:#1e40af; --blue-soft:#e0e7ff; --amber:#b45309; --teal:#0f766e; --grid:#e5e7eb; --chip:#eef2ff; --ok:#16a34a; --warn:#f59e0b; --danger:#dc2626; --code-bg:#0b1020; --code-ink:#e6edf3; } *{box-sizing:border-box} body{margin:0;font:15px/1.5 system-ui,Segoe UI,Roboto,Helvetica,Arial; color:var(--ink);background:linear-gradient(#eef2ff,#fff)} .wrap{max-width:1200px;margin:32px auto;padding:0 16px} .toolbar{display:flex;gap:8px;flex-wrap:wrap;align-items:center;justify-content:space-between;margin-bottom:16px} .btn{appearance:none;border:1px solid var(--grid);background:#fff;border-radius:8px;padding:8px 12px;cursor:pointer} .btn:active{transform:translateY(1px)} .field{width:420px;max-width:100%;height:70px;border:1px solid var(--grid);border-radius:8px;padding:8px} .canvas{background:var(--panel);border:1px solid var(--grid);border-radius:16px;padding:24px;box-shadow:0 12px 30px rgba(2,6,23,.06)} h1{color:var(--blue);text-align:center;margin:8px 0 4px} .subtitle{color:var(--muted);text-align:center;margin:0 0 16px} .grid-3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:16px;align-items:center} .card{background:var(--panel);border:1px solid var(--grid);border-radius:12px;padding:16px} .title-sm{font-weight:700;margin:0 0 6px} .muted{color:var(--muted)} .center{display:flex;justify-content:center;align-items:center} .orb{height:220px;width:220px;border-radius:999px;background:radial-gradient(ellipse at 30% 30%, #3b82f6, #1e3a8a); border:8px solid #fff;box-shadow:0 12px 30px rgba(2,6,23,.15);color:#fff;text-align:center;display:grid;place-items:center} .orb h2{margin:0 0 4px;font-size:18px} .list{margin:0;padding-left:18px} .list li{margin:4px 0} .pillars .card{border-top:4px solid var(--grid)} .pillars .card:nth-child(1){border-top-color:#3b82f6} .pillars .card:nth-child(2){border-top-color:#1e40af} .pillars .card:nth-child(3){border-top-color:#0d9488} .runway{display:flex;gap:16px} .stage{flex:1} .bar{height:8px;border-radius:999px;background:#e5e7eb;margin:6px 0;overflow:hidden} .bar>span{display:block;height:100%;background:#3b82f6} .okr{margin-top:8px} .chip{display:inline-block;font-size:12px;padding:2px 8px;border-radius:999px;background:var(--chip);color:#3730a3;margin:2px 6px 0 0} .metrics{display:grid;grid-template-columns:repeat(4,1fr);gap:12px} .gauge{background:var(--blue-soft);border:1px solid var(--grid);border-radius:10px;padding:12px;text-align:center} .gauge h4{color:var(--blue);margin:4px 0} .gauge .val{color:var(--muted);font-size:13px} .tabs{margin-top:8px} .tab-head{display:flex;gap:6px;flex-wrap:wrap} .tab-head button{border:1px solid var(--grid);background:#fff;border-radius:8px;padding:6px 10px;font-size:13px;cursor:pointer} .tab-head button.active{background:#eef2ff;border-color:#c7d2fe} .badges{font-size:11px;margin-left:6px;color:#1e3a8a;background:#e0e7ff;border-radius:999px;padding:1px 6px} .tab-body{border:1px solid var(--grid);border-radius:10px;overflow:hidden;margin-top:6px} pre{margin:0;background:var(--code-bg);color:var(--code-ink);padding:12px;overflow:auto;font-size:12px} .flow{display:grid;grid-template-columns:1fr 40px 2fr 40px 1fr;gap:8px;align-items:center} .arrow{font-weight:700;text-align:center;color:#2563eb} .grid-variants{display:grid;grid-template-columns:repeat(4,1fr);gap:8px} .cap{border:1px solid var(--grid);border-radius:10px;padding:10px;font-size:13px} .cap.good{outline:2px solid rgba(22,163,74,.35)} .cap .state{padding:2px 6px;border-radius:999px;font-size:11px} .state.ok{background:#dcfce7;color:#166534} .state.partial{background:#f1f5f9;color:#64748b} .ruler{margin-top:16px;text-align:center;color:var(--muted);font-style:italic} .siem{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:16px} .input{width:100%;padding:8px;border:1px solid var(--grid);border-radius:8px;font-size:13px} @media (max-width:1000px){ .grid-3{grid-template-columns:1fr} .flow{grid-template-columns:1fr} .grid-variants{grid-template-columns:1fr 1fr} .metrics{grid-template-columns:1fr 1fr} .siem{grid-template-columns:1fr} }
Export PNG Export SVG
Canvas 4:3 A4 Portrait A4 Landscape
Apply JSON
Architecture Operating Model 2025–2035
Van documentatie naar een levend besturingssysteem
Drukfactoren
- AI demand
- DORA, 17 januari 2025
- NIS2
Architecture as Operating System
Policy as Code, Zero Trust, Telemetry
Uitkomsten
- License to operate
- Operational resilience
- Digital sovereignty
- Gefedereerde organisatie
- Onder COO of Transformatie
- RACI duidelijk
- Autonomie binnen guardrails
- Geautomatiseerd control plane
- Policy as Code, OPA en Kyverno
- Zero Trust, CI en CD gates
- API gateway als PEP
- Waardestroom integratie
- Capability Map gekoppeld aan OKR’s
- FinOps tagging
- Telemetrie naar business
Dagen 1–30, Baseline en Telemetrie
DF baseline SaaS discovery
Dagen 31–60, MVA en PaC
5–7 guardrails in audit API linting
Dagen 61–90, Enforce en Quick wins
Tagging enforced SaaS rationalisatie
DF
35
Deployment Frequency LT
12
Lead Time CFR
8
Change Failure Rate MTTR
45
Mean Time to Restore
OPA Rego CI, CD Kyverno YAML CD, Runtime Infracost Policy CI API Linting (Spectral) CI SBOM Checks (CycloneDX) CI, Runtime Export Rego skeleton
package terraform.azure.finops
denied_skus := {"Standard_D16s_v3", "Standard_E32s_v3", "Standard_M64ms"}
deny[msg] { resource := input.resource_changes[_] resource.type == "azurerm_linux_virtual_machine" resource.change.after.tags.environment == "dev" denied_skus[resource.change.after.size] msg := sprintf("FinOps Policy Violation: disallow %v in dev.", [resource.change.after.size]) } apiVersion: kyverno.io/v1 kind: ClusterPolicy metadata: name: disallow-root-user spec: validationFailureAction: Enforce rules:
- name: validate-runasnonroot
match:
any:
- resources:
kinds: [Pod]
validate:
message: "Root containers are not allowed"
pattern:
spec:
containers:
- securityContext: =(runAsNonRoot): true runAsUser: ">0" version: 0.1 policies:
- resources:
kinds: [Pod]
validate:
message: "Root containers are not allowed"
pattern:
spec:
containers:
- name: cap-dev-skus description: cap expensive instance types in dev severity: high rule: | resources.where(r, r.tags.environment == "dev" && r.monthlyCost > 200).count() == 0 extends: spectral:recommended
rules: no-http: error security-schemes-required: error require-oauth2-scopes: severity: error given: $.paths[][].security[].OAuth2[] then: function: truthy
cyclonedx-policy.toml, simple SBOM gating example
[policy] min_severity_block = "high" # block high and critical allow_licenses = ["MIT", "Apache-2.0", "BSD-3-Clause"]
[checks]
fail build if any component vulnerability severity >= high
vuln_threshold = { severity = ["high", "critical"], max = 0 }
require purl for traceability
require_purl = true
disallow components without version
no_unversioned = true
API Gateway as Policy Enforcement Point
ClientApp, Agent, Service →
API Gateway PEP
AuthN OIDC mTLS AuthZ OPA Rate limit Schema validate Threat detect
→ ServiceDomain API
Enforcement points: CI spec linting, CD policy checks, runtime gateway decisions.
Gateway Variants Vendor:
KongApigeeAzure APIMIstio
Indicatief. Integreer OPA via ext-auth waar geen native support is. Evidence logs naar SIEM via audit en decision logs.
Data residency ≠ legal sovereignty • EU-qualified provider required
SIEM Targets
Syslog OTLP gRPC OTLP HTTP Object Store Index Retention (days)
Evidence Sources
Export JSON Export YAML
// Defaults let metrics = { DF:35, LT:12, CFR:8, MTTR:45 };
// Apply JSON document.getElementById('applyJson').addEventListener('click', () => { try{ const v = JSON.parse(document.getElementById('json').value || '{}'); if(v.metrics){ metrics = Object.assign(metrics, v.metrics); renderMetrics(); } }catch{ alert('Invalid JSON'); } });
function renderMetrics(){ document.getElementById('mDF').textContent = String(metrics.DF); document.getElementById('mLT').textContent = String(metrics.LT); document.getElementById('mCFR').textContent = String(metrics.CFR); document.getElementById('mMTTR').textContent = String(metrics.MTTR); } renderMetrics();
// Tabs const head = document.getElementById('tabHead'); head.addEventListener('click', (e)=>{ const b = e.target.closest('button[data-tab]'); if(!b) return; [...head.querySelectorAll('button[data-tab]')].forEach(x=>x.classList.remove('active')); b.classList.add('active'); const key = b.dataset.tab; [...document.querySelectorAll('.tab-body pre')].forEach(p=>p.hidden=true); document.getElementById('tab-'+key).hidden = false; });
// Gateway variant matrix const support = { 'OIDC': { 'Kong':1, 'Apigee':1, 'Azure APIM':1, 'Istio':1 }, 'OPA/Ext auth':{ 'Kong':1, 'Apigee':0, 'Azure APIM':0, 'Istio':1 }, 'mTLS': { 'Kong':1, 'Apigee':1, 'Azure APIM':1, 'Istio':1 }, 'Rate limiting':{ 'Kong':1,'Apigee':1,'Azure APIM':1,'Istio':1 }, 'Schema validate':{ 'Kong':1,'Apigee':1,'Azure APIM':1,'Istio':1 }, 'WAF/Threat detect':{ 'Kong':1, 'Apigee':1, 'Azure APIM':1, 'Istio':1 }, 'Native Rego': { 'Kong':0, 'Apigee':0, 'Azure APIM':0, 'Istio':0 }, 'Ext Auth plugin':{ 'Kong':1,'Apigee':1,'Azure APIM':0,'Istio':1 } }; const vendorSel = document.getElementById('vendor'); const variants = document.getElementById('variants'); function renderVariants(){ const v = vendorSel.value; variants.innerHTML = ''; Object.keys(support).forEach(label=>{ const ok = support[label][v]===1; const el = document.createElement('div'); el.className = 'cap'+(ok?' good':''); el.innerHTML = ''+label+''+(ok?'Yes':'Partial/Ext')+''; variants.appendChild(el); }); } vendorSel.addEventListener('change', renderVariants); renderVariants();
// Evidence sources const sourceList = [ 'CI: SBOM scan', 'CI: Spectral OpenAPI lint', 'CD: OPA PaC gate', 'Runtime: Gateway decisions', 'Runtime: K8s Admission', 'CD: Infracost cap checks' ]; const sourcesDiv = document.getElementById('sources'); sourceList.forEach(s=>{ const lab = document.createElement('label'); lab.style.display='flex'; lab.style.alignItems='center'; lab.style.gap='6px'; const cb = document.createElement('input'); cb.type='checkbox'; cb.checked=true; cb.dataset.evidence=s; lab.appendChild(cb); lab.appendChild(document.createTextNode(s)); sourcesDiv.appendChild(lab); });
// Export Evidence Manifest
function buildManifest(){
const pick = id => document.getElementById(id).value || '';
const checked = [...document.querySelectorAll('[data-evidence]')].filter(n=>n.checked).map(n=>n.dataset.evidence);
return {
version:'1.0',
targets:{ syslog:pick('syslog'), otlp_grpc:pick('otlp'), otlp_http:pick('otlph'), object_store:pick('obj') },
index: pick('index') || 'aom-control-plane',
retention_days: Number(pick('retention') || '365'),
evidence_sources: checked
};
}
function download(name, mime, data){
const blob = new Blob([data], {type:mime}); const url=URL.createObjectURL(blob);
const a=document.createElement('a'); a.href=url; a.download=name; a.click(); URL.revokeObjectURL(url);
}
function buildYaml(m){
const esc = v => typeof v==='string' ? v.replace(/"/g,'\"') : v;
const bullets = (m.evidence_sources||[]).map(s=>' - '+s).join('\n');
return version: "$\{m.version\}" targets: syslog: "$\{esc(m.targets.syslog)\}" otlp_grpc: "$\{esc(m.targets.otlp_grpc)\}" otlp_http: "$\{esc(m.targets.otlp_http)\}" object_store: "$\{esc(m.targets.object_store)\}" index: "$\{esc(m.index)\}" retention_days: $\{m.retention_days\} evidence_sources: $\{bullets\};
}
document.getElementById('expJson').onclick = ()=> download('aom-evidence-manifest.json','application/json', JSON.stringify(buildManifest(),null,2));
document.getElementById('expYaml').onclick = ()=> download('aom-evidence-manifest.yaml','text/yaml', buildYaml(buildManifest()));
// Export Rego skeleton const spectralSample = String.raw`extends: spectral:recommended
rules:
no-http: error
security-schemes-required: error
require-oauth2-scopes:
severity: error
given: $.paths[][].security[].OAuth2[]
then:
function: truthy; document.getElementById('exportRego').onclick = ()=>\{ const rego = package api.pep
deny on HTTP-only schemes
deny["transport must be https"] { input.request.scheme == "http" }
deny when OAuth2 scopes missing
deny["missing oauth2 scopes"] { not input.auth.oauth2.scopes }
derived from Spectral rules
---
${spectralSample.replace(//g,'\\')}
`; download('gateway-pep.rego','text/plain',rego); };
// Simple PNG export using print as fallback document.getElementById('exportPng').onclick = ()=>{ // Fallback: open print dialog for quick export to PDF/PNG via system tools. window.print(); };
// Paper size hint (visual) document.getElementById('paper').addEventListener('change',(e)=>{ const m = e.target.value; const c = document.getElementById('canvas'); c.style.aspectRatio = m==='screen' ? '4 / 3' : (m==='a4p' ? '210 / 297' : '297 / 210'); });
// Self-checks console.assert(spectralSample.startsWith('extends: spectral:recommended'),'spectralSample header ontbreekt'); console.assert(/function:\s+truthy$/.test(spectralSample.trim()), 'spectralSample sluit niet correct af');
DjimIT Nieuwsbrief
AI updates, praktijkcases en tool reviews — tweewekelijks, direct in uw inbox.