> ## Documentation Index
> Fetch the complete documentation index at: https://docs.serval.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Data flow diagrams

export const HybridDataFlowDiagram = () => <div style={{
  overflowX: 'auto',
  width: '100%'
}} dangerouslySetInnerHTML={{
  __html: `<svg viewBox="0 0 1780 920" width="100%" style="height:auto" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Serval platform data flow diagram for hybrid self-hosted deployments" id="dfd-h"><style>
  svg { 
  --paper: #fdfcfe; --ink: #251f38; --ink-2: #4d4763; --ink-3: #8b86a0;
  --zone-fill: rgba(109,63,212,.024); --zone-line: #b9b2cf;
  --band-fill: rgba(109,63,212,.05);
  --node-fill: #f3eefc; --node-line: #8f6ade;
  --actor-fill: #ffffff; --actor-line: #6d3fd4;
  --ext-fill: #eef3f8; --ext-line: #7d95ad;
  --plat-fill: #eef1fb; --plat-line: #7d93d8;
  --storef: #f0ede1; --stores: #a89f7d;
  --e-user: #6d3fd4;
  --e-out: #0c7d6c;
  --e-in: #b4610e;
  --e-ai: #3d63c9;
  --e-store: #77708f;
 }
  @media (prefers-color-scheme: dark) { svg { 
  --paper: #16141d; --ink: #ece9f4; --ink-2: #b6b0c9; --ink-3: #7d7794;
  --zone-fill: rgba(166,135,236,.04); --zone-line: #4a4362;
  --band-fill: rgba(166,135,236,.07);
  --node-fill: #2a2440; --node-line: #8f6ade;
  --actor-fill: #1d1a28; --actor-line: #a687ec;
  --ext-fill: #1f2733; --ext-line: #5d7791;
  --plat-fill: #202940; --plat-line: #6d84c9;
  --storef: #2b2920; --stores: #8d845f;
  --e-user: #a687ec;
  --e-out: #3fb9a5;
  --e-in: #e59a4b;
  --e-ai: #7d9bee;
  --e-store: #8d86a6;
 } }
  :root[data-theme="light"] svg { 
  --paper: #fdfcfe; --ink: #251f38; --ink-2: #4d4763; --ink-3: #8b86a0;
  --zone-fill: rgba(109,63,212,.024); --zone-line: #b9b2cf;
  --band-fill: rgba(109,63,212,.05);
  --node-fill: #f3eefc; --node-line: #8f6ade;
  --actor-fill: #ffffff; --actor-line: #6d3fd4;
  --ext-fill: #eef3f8; --ext-line: #7d95ad;
  --plat-fill: #eef1fb; --plat-line: #7d93d8;
  --storef: #f0ede1; --stores: #a89f7d;
  --e-user: #6d3fd4;
  --e-out: #0c7d6c;
  --e-in: #b4610e;
  --e-ai: #3d63c9;
  --e-store: #77708f;
 }
  :root[data-theme="dark"] svg { 
  --paper: #16141d; --ink: #ece9f4; --ink-2: #b6b0c9; --ink-3: #7d7794;
  --zone-fill: rgba(166,135,236,.04); --zone-line: #4a4362;
  --band-fill: rgba(166,135,236,.07);
  --node-fill: #2a2440; --node-line: #8f6ade;
  --actor-fill: #1d1a28; --actor-line: #a687ec;
  --ext-fill: #1f2733; --ext-line: #5d7791;
  --plat-fill: #202940; --plat-line: #6d84c9;
  --storef: #2b2920; --stores: #8d845f;
  --e-user: #a687ec;
  --e-out: #3fb9a5;
  --e-in: #e59a4b;
  --e-ai: #7d9bee;
  --e-store: #8d86a6;
 }
  :root.light svg { 
  --paper: #fdfcfe; --ink: #251f38; --ink-2: #4d4763; --ink-3: #8b86a0;
  --zone-fill: rgba(109,63,212,.024); --zone-line: #b9b2cf;
  --band-fill: rgba(109,63,212,.05);
  --node-fill: #f3eefc; --node-line: #8f6ade;
  --actor-fill: #ffffff; --actor-line: #6d3fd4;
  --ext-fill: #eef3f8; --ext-line: #7d95ad;
  --plat-fill: #eef1fb; --plat-line: #7d93d8;
  --storef: #f0ede1; --stores: #a89f7d;
  --e-user: #6d3fd4;
  --e-out: #0c7d6c;
  --e-in: #b4610e;
  --e-ai: #3d63c9;
  --e-store: #77708f;
 }
  :root.dark svg { 
  --paper: #16141d; --ink: #ece9f4; --ink-2: #b6b0c9; --ink-3: #7d7794;
  --zone-fill: rgba(166,135,236,.04); --zone-line: #4a4362;
  --band-fill: rgba(166,135,236,.07);
  --node-fill: #2a2440; --node-line: #8f6ade;
  --actor-fill: #1d1a28; --actor-line: #a687ec;
  --ext-fill: #1f2733; --ext-line: #5d7791;
  --plat-fill: #202940; --plat-line: #6d84c9;
  --storef: #2b2920; --stores: #8d845f;
  --e-user: #a687ec;
  --e-out: #3fb9a5;
  --e-in: #e59a4b;
  --e-ai: #7d9bee;
  --e-store: #8d86a6;
 }

  svg { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; }
  .zone rect { fill: var(--zone-fill); stroke: var(--zone-line); stroke-width: 1.2; stroke-dasharray: 7 5; }
  .zone-title { font-size: 13.5px; font-weight: 650; fill: var(--ink-2); letter-spacing: .04em; }
  .zone-sub { font-size: 11px; fill: var(--ink-3); }
  .store-band { fill: var(--band-fill); }
  .band-label { font-family: ui-monospace, "SF Mono", Menlo, monospace; letter-spacing: .14em; font-size: 10px; fill: var(--ink-3); }

  .node-body { fill: var(--node-fill); stroke: var(--node-line); stroke-width: 1.4; }
  .kind-actor .node-body { fill: var(--actor-fill); stroke: var(--actor-line); }
  .kind-external .node-body { fill: var(--ext-fill); stroke: var(--ext-line); }
  .kind-platform .node-body { fill: var(--plat-fill); stroke: var(--plat-line); }
  .store-body { fill: var(--storef); stroke: var(--stores); stroke-width: 1.4; }
  .store-lid { fill: none; stroke: var(--stores); stroke-width: 1.4; }
  .node-label { font-size: 14.5px; font-weight: 640; fill: var(--ink); text-anchor: middle; }
  .node-sub { font-size: 10.5px; fill: var(--ink-3); text-anchor: middle; }
  .node-cap { font-size: 10.5px; font-weight: 570; fill: var(--ink-2); text-anchor: middle; }
  .kind-store .node-label { font-size: 13px; }

  .edge .wire { fill: none; stroke-width: 2; }
  .edge .hit { fill: none; stroke: transparent; stroke-width: 14; }
  .edge-label { font-size: 11.5px; font-weight: 570; fill: var(--ink-2);
    paint-order: stroke; stroke: var(--paper); stroke-width: 5px; stroke-linejoin: round; }
  .legend-label { font-size: 11.5px; fill: var(--ink-2); }
  .title { font-size: 19px; font-weight: 700; fill: var(--ink); letter-spacing: -.01em; }
  .subtitle { font-size: 11.5px; fill: var(--ink-3); }
  .fn, .fn-num { font-size: 10.5px; fill: var(--ink-3); }
  .fn-num { font-weight: 700; fill: var(--ink-2); }

  .edge, .node { transition: opacity .15s; }
  .edge .hit { pointer-events: stroke; }
  .node { pointer-events: bounding-box; }
  svg:has(.edge:hover) .edge:not(:hover) { opacity: .12; }
  svg:has(.edge:hover) .node { opacity: .3; }
svg:has(.edge[data-from="user"][data-to="api"]:hover) :is(.node[data-node="user"], .node[data-node="api"]) { opacity: 1; }
svg:has(.edge[data-from="idp"][data-to="api"]:hover) :is(.node[data-node="idp"], .node[data-node="api"]) { opacity: 1; }
svg:has(.edge[data-from="api"][data-to="engine"]:hover) :is(.node[data-node="api"], .node[data-node="engine"]) { opacity: 1; }
svg:has(.edge[data-from="api"][data-to="proxy"]:hover) :is(.node[data-node="api"], .node[data-node="proxy"]) { opacity: 1; }
svg:has(.edge[data-from="engine"][data-to="proxy"]:hover) :is(.node[data-node="engine"], .node[data-node="proxy"]) { opacity: 1; }
svg:has(.edge[data-from="engine"][data-to="temporal"]:hover) :is(.node[data-node="engine"], .node[data-node="temporal"]) { opacity: 1; }
svg:has(.edge[data-from="engine"][data-to="llm"]:hover) :is(.node[data-node="engine"], .node[data-node="llm"]) { opacity: 1; }
svg:has(.edge[data-from="webhooks"][data-to="engine"]:hover) :is(.node[data-node="webhooks"], .node[data-node="engine"]) { opacity: 1; }
svg:has(.edge[data-from="apps"][data-to="webhooks"]:hover) :is(.node[data-node="apps"], .node[data-node="webhooks"]) { opacity: 1; }
svg:has(.edge[data-from="tick"][data-to="webhooks"]:hover) :is(.node[data-node="tick"], .node[data-node="webhooks"]) { opacity: 1; }
svg:has(.edge[data-from="slack"][data-to="webhooks"]:hover) :is(.node[data-node="slack"], .node[data-node="webhooks"]) { opacity: 1; }
svg:has(.edge[data-from="email"][data-to="webhooks"]:hover) :is(.node[data-node="email"], .node[data-node="webhooks"]) { opacity: 1; }
svg:has(.edge[data-from="proxy"][data-to="slack"]:hover) :is(.node[data-node="proxy"], .node[data-node="slack"]) { opacity: 1; }
svg:has(.edge[data-from="proxy"][data-to="idp"]:hover) :is(.node[data-node="proxy"], .node[data-node="idp"]) { opacity: 1; }
svg:has(.edge[data-from="proxy"][data-to="apps"]:hover) :is(.node[data-node="proxy"], .node[data-node="apps"]) { opacity: 1; }
svg:has(.edge[data-from="proxy"][data-to="tick"]:hover) :is(.node[data-node="proxy"], .node[data-node="tick"]) { opacity: 1; }
svg:has(.edge[data-from="proxy"][data-to="hris"]:hover) :is(.node[data-node="proxy"], .node[data-node="hris"]) { opacity: 1; }
svg:has(.edge[data-from="engine"][data-to="email"]:hover) :is(.node[data-node="engine"], .node[data-node="email"]) { opacity: 1; }
svg:has(.edge[data-from="api"][data-to="postgres"]:hover) :is(.node[data-node="api"], .node[data-node="postgres"]) { opacity: 1; }
svg:has(.edge[data-from="engine"][data-to="postgres"]:hover) :is(.node[data-node="engine"], .node[data-node="postgres"]) { opacity: 1; }
svg:has(.edge[data-from="engine"][data-to="s3"]:hover) :is(.node[data-node="engine"], .node[data-node="s3"]) { opacity: 1; }
svg:has(.edge[data-from="engine"][data-to="vector"]:hover) :is(.node[data-node="engine"], .node[data-node="vector"]) { opacity: 1; }
svg:has(.edge[data-from="engine"][data-to="audit"]:hover) :is(.node[data-node="engine"], .node[data-node="audit"]) { opacity: 1; }
svg:has(.edge[data-from="proxy"][data-to="creds"]:hover) :is(.node[data-node="proxy"], .node[data-node="creds"]) { opacity: 1; }
svg:has(.node[data-node="user"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="user"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="user"]:hover) :is(.edge[data-from="user"], .edge[data-to="user"]) { opacity: 1; }
svg:has(.node[data-node="user"]:hover) :is(.node[data-node="api"], .node[data-node="user"]) { opacity: 1; }
svg:has(.node[data-node="temporal"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="temporal"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="temporal"]:hover) :is(.edge[data-from="temporal"], .edge[data-to="temporal"]) { opacity: 1; }
svg:has(.node[data-node="temporal"]:hover) :is(.node[data-node="engine"], .node[data-node="temporal"]) { opacity: 1; }
svg:has(.node[data-node="llm"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="llm"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="llm"]:hover) :is(.edge[data-from="llm"], .edge[data-to="llm"]) { opacity: 1; }
svg:has(.node[data-node="llm"]:hover) :is(.node[data-node="engine"], .node[data-node="llm"]) { opacity: 1; }
svg:has(.node[data-node="api"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="api"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="api"]:hover) :is(.edge[data-from="api"], .edge[data-to="api"]) { opacity: 1; }
svg:has(.node[data-node="api"]:hover) :is(.node[data-node="api"], .node[data-node="engine"], .node[data-node="idp"], .node[data-node="postgres"], .node[data-node="proxy"], .node[data-node="user"]) { opacity: 1; }
svg:has(.node[data-node="engine"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="engine"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="engine"]:hover) :is(.edge[data-from="engine"], .edge[data-to="engine"]) { opacity: 1; }
svg:has(.node[data-node="engine"]:hover) :is(.node[data-node="api"], .node[data-node="audit"], .node[data-node="email"], .node[data-node="engine"], .node[data-node="llm"], .node[data-node="postgres"], .node[data-node="proxy"], .node[data-node="s3"], .node[data-node="temporal"], .node[data-node="vector"], .node[data-node="webhooks"]) { opacity: 1; }
svg:has(.node[data-node="proxy"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="proxy"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="proxy"]:hover) :is(.edge[data-from="proxy"], .edge[data-to="proxy"]) { opacity: 1; }
svg:has(.node[data-node="proxy"]:hover) :is(.node[data-node="api"], .node[data-node="apps"], .node[data-node="creds"], .node[data-node="engine"], .node[data-node="hris"], .node[data-node="idp"], .node[data-node="proxy"], .node[data-node="slack"], .node[data-node="tick"]) { opacity: 1; }
svg:has(.node[data-node="creds"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="creds"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="creds"]:hover) :is(.edge[data-from="creds"], .edge[data-to="creds"]) { opacity: 1; }
svg:has(.node[data-node="creds"]:hover) :is(.node[data-node="creds"], .node[data-node="proxy"]) { opacity: 1; }
svg:has(.node[data-node="webhooks"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="webhooks"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="webhooks"]:hover) :is(.edge[data-from="webhooks"], .edge[data-to="webhooks"]) { opacity: 1; }
svg:has(.node[data-node="webhooks"]:hover) :is(.node[data-node="apps"], .node[data-node="email"], .node[data-node="engine"], .node[data-node="slack"], .node[data-node="tick"], .node[data-node="webhooks"]) { opacity: 1; }
svg:has(.node[data-node="postgres"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="postgres"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="postgres"]:hover) :is(.edge[data-from="postgres"], .edge[data-to="postgres"]) { opacity: 1; }
svg:has(.node[data-node="postgres"]:hover) :is(.node[data-node="api"], .node[data-node="engine"], .node[data-node="postgres"]) { opacity: 1; }
svg:has(.node[data-node="s3"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="s3"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="s3"]:hover) :is(.edge[data-from="s3"], .edge[data-to="s3"]) { opacity: 1; }
svg:has(.node[data-node="s3"]:hover) :is(.node[data-node="engine"], .node[data-node="s3"]) { opacity: 1; }
svg:has(.node[data-node="vector"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="vector"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="vector"]:hover) :is(.edge[data-from="vector"], .edge[data-to="vector"]) { opacity: 1; }
svg:has(.node[data-node="vector"]:hover) :is(.node[data-node="engine"], .node[data-node="vector"]) { opacity: 1; }
svg:has(.node[data-node="audit"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="audit"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="audit"]:hover) :is(.edge[data-from="audit"], .edge[data-to="audit"]) { opacity: 1; }
svg:has(.node[data-node="audit"]:hover) :is(.node[data-node="audit"], .node[data-node="engine"]) { opacity: 1; }
svg:has(.node[data-node="idp"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="idp"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="idp"]:hover) :is(.edge[data-from="idp"], .edge[data-to="idp"]) { opacity: 1; }
svg:has(.node[data-node="idp"]:hover) :is(.node[data-node="api"], .node[data-node="idp"], .node[data-node="proxy"]) { opacity: 1; }
svg:has(.node[data-node="apps"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="apps"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="apps"]:hover) :is(.edge[data-from="apps"], .edge[data-to="apps"]) { opacity: 1; }
svg:has(.node[data-node="apps"]:hover) :is(.node[data-node="apps"], .node[data-node="proxy"], .node[data-node="webhooks"]) { opacity: 1; }
svg:has(.node[data-node="tick"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="tick"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="tick"]:hover) :is(.edge[data-from="tick"], .edge[data-to="tick"]) { opacity: 1; }
svg:has(.node[data-node="tick"]:hover) :is(.node[data-node="proxy"], .node[data-node="tick"], .node[data-node="webhooks"]) { opacity: 1; }
svg:has(.node[data-node="hris"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="hris"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="hris"]:hover) :is(.edge[data-from="hris"], .edge[data-to="hris"]) { opacity: 1; }
svg:has(.node[data-node="hris"]:hover) :is(.node[data-node="hris"], .node[data-node="proxy"]) { opacity: 1; }
svg:has(.node[data-node="slack"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="slack"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="slack"]:hover) :is(.edge[data-from="slack"], .edge[data-to="slack"]) { opacity: 1; }
svg:has(.node[data-node="slack"]:hover) :is(.node[data-node="proxy"], .node[data-node="slack"], .node[data-node="webhooks"]) { opacity: 1; }
svg:has(.node[data-node="email"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="email"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="email"]:hover) :is(.edge[data-from="email"], .edge[data-to="email"]) { opacity: 1; }
svg:has(.node[data-node="email"]:hover) :is(.node[data-node="email"], .node[data-node="engine"], .node[data-node="webhooks"]) { opacity: 1; }</style><defs><marker id="ah-user-h" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-user)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="ah-user-h-r" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-user)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="ah-out-h" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-out)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="ah-out-h-r" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-out)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="ah-in-h" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-in)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="ah-in-h-r" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-in)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="ah-ai-h" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-ai)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="ah-ai-h-r" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-ai)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="ah-store-h" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-store)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="ah-store-h-r" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-store)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker></defs><rect x="0" y="0" width="1780" height="920" fill="var(--paper)"/><g class="zone"><rect x="290" y="82" width="950" height="796" rx="16"/><text class="zone-title" x="308" y="110">Serval Cloud (VPC)</text></g><g class="zone"><rect x="1272" y="72" width="494" height="724" rx="16"/><text class="zone-title" x="1290" y="100">Customer environment</text></g><g class="zone"><rect x="1290" y="112" width="166" height="214" rx="16"/><text class="zone-title" x="1308" y="140">Serval worker</text><text class="zone-sub" x="1308" y="157">your infrastructure</text></g><g class="zone"><rect x="36" y="424" width="172" height="100" rx="16"/><text class="zone-title" x="54" y="452">Execution platform</text></g><g class="zone"><rect x="36" y="569" width="172" height="100" rx="16"/><text class="zone-title" x="54" y="597">AI services</text></g><rect class="store-band" x="314" y="752" width="902" height="102" rx="12"/><text class="band-label" x="330" y="772">STORAGE</text><g class="edge cat-user " data-from="user" data-to="api"><path class="hit" d="M 192 210 L 336 210"/><path class="wire" d="M 192 210 L 336 210" stroke="var(--e-user)" marker-end="url(#ah-user-h)" marker-start="url(#ah-user-h-r)"/><text class="edge-label" x="264" y="202" text-anchor="middle">invoke · results</text></g><g class="edge cat-user " data-from="idp" data-to="api"><path class="hit" d="M 1674 132 L 1712 132 A 8 8 0 0 1 1728 132 L 1733 132 A 9 9 0 0 0 1742 123 L 1742 49 A 9 9 0 0 0 1733 40 L 429 40 A 9 9 0 0 0 420 49 L 420 183"/><path class="wire" d="M 1674 132 L 1712 132 A 8 8 0 0 1 1728 132 L 1733 132 A 9 9 0 0 0 1742 123 L 1742 49 A 9 9 0 0 0 1733 40 L 429 40 A 9 9 0 0 0 420 49 L 420 183" stroke="var(--e-user)" marker-end="url(#ah-user-h)"/><text class="edge-label" x="806" y="34" text-anchor="middle">SSO · SCIM directory sync</text></g><g class="edge cat-user " data-from="api" data-to="engine"><path class="hit" d="M 504 218 L 631 218 A 9 9 0 0 1 640 227 L 640 383"/><path class="wire" d="M 504 218 L 631 218 A 9 9 0 0 1 640 227 L 640 383" stroke="var(--e-user)" marker-end="url(#ah-user-h)" marker-start="url(#ah-user-h-r)"/><text class="edge-label" x="575" y="240" text-anchor="start">definitions · runs · results</text></g><g class="edge cat-user " data-from="api" data-to="proxy"><path class="hit" d="M 504 198 L 931 198 A 9 9 0 0 0 940 189 L 940 185 A 9 9 0 0 1 949 176 L 1298 176"/><path class="wire" d="M 504 198 L 931 198 A 9 9 0 0 0 940 189 L 940 185 A 9 9 0 0 1 949 176 L 1298 176" stroke="var(--e-user)" marker-end="url(#ah-user-h)"/><text class="edge-label" x="1100" y="168" text-anchor="middle">connection registration · OAuth setup</text></g><g class="edge cat-user " data-from="engine" data-to="proxy"><path class="hit" d="M 784 390 L 991 390 A 9 9 0 0 0 1000 381 L 1000 217 A 9 9 0 0 1 1009 208 L 1298 208"/><path class="wire" d="M 784 390 L 991 390 A 9 9 0 0 0 1000 381 L 1000 217 A 9 9 0 0 1 1009 208 L 1298 208" stroke="var(--e-user)" marker-end="url(#ah-user-h)" marker-start="url(#ah-user-h-r)"/><text class="edge-label" x="1008" y="300" text-anchor="start">worker-initiated HTTPS · signed integration calls</text></g><g class="edge cat-ai " data-from="engine" data-to="temporal"><path class="hit" d="M 616 395 L 428 395 A 8 8 0 0 0 412 395 L 249 395 A 9 9 0 0 0 240 404 L 240 461 A 9 9 0 0 1 231 470 L 200 470"/><path class="wire" d="M 616 395 L 428 395 A 8 8 0 0 0 412 395 L 249 395 A 9 9 0 0 0 240 404 L 240 461 A 9 9 0 0 1 231 470 L 200 470" stroke="var(--e-ai)" marker-end="url(#ah-ai-h)" marker-start="url(#ah-ai-h-r)"/><text class="edge-label" x="500" y="387" text-anchor="middle">durable orchestration</text></g><g class="edge cat-ai " data-from="engine" data-to="llm"><path class="hit" d="M 616 425 L 428 425 A 8 8 0 0 0 412 425 L 271 425 A 9 9 0 0 0 262 434 L 262 606 A 9 9 0 0 1 253 615 L 200 615"/><path class="wire" d="M 616 425 L 428 425 A 8 8 0 0 0 412 425 L 271 425 A 9 9 0 0 0 262 434 L 262 606 A 9 9 0 0 1 253 615 L 200 615" stroke="var(--e-ai)" marker-end="url(#ah-ai-h)"/><text class="edge-label" x="500" y="441" text-anchor="middle">AI steps · embeddings</text></g><g class="edge cat-in " data-from="webhooks" data-to="engine"><path class="hit" d="M 876 547 L 817 547 A 9 9 0 0 1 808 538 L 808 439 A 9 9 0 0 0 799 430 L 784 430"/><path class="wire" d="M 876 547 L 817 547 A 9 9 0 0 1 808 538 L 808 439 A 9 9 0 0 0 799 430 L 784 430" stroke="var(--e-in)" marker-end="url(#ah-in-h)"/><text class="edge-label" x="870" y="539" text-anchor="end">events trigger runs</text></g><g class="edge cat-in " data-from="apps" data-to="webhooks"><path class="hit" d="M 1506 290 L 1491 290 A 9 9 0 0 0 1482 299 L 1482 553 A 9 9 0 0 1 1473 562 L 1044 562"/><path class="wire" d="M 1506 290 L 1491 290 A 9 9 0 0 0 1482 299 L 1482 553 A 9 9 0 0 1 1473 562 L 1044 562" stroke="var(--e-in)" marker-end="url(#ah-in-h)"/></g><g class="edge cat-in " data-from="tick" data-to="webhooks"><path class="hit" d="M 1506 396 L 1490 396 A 8 8 0 0 0 1474 396 L 1145 396 A 9 9 0 0 0 1136 405 L 1136 539 A 9 9 0 0 1 1127 548 L 1044 548"/><path class="wire" d="M 1506 396 L 1490 396 A 8 8 0 0 0 1474 396 L 1145 396 A 9 9 0 0 0 1136 405 L 1136 539 A 9 9 0 0 1 1127 548 L 1044 548" stroke="var(--e-in)" marker-end="url(#ah-in-h)"/></g><g class="edge cat-in " data-from="slack" data-to="webhooks"><path class="hit" d="M 1506 598 L 1105 598 A 9 9 0 0 1 1096 589 L 1096 585 A 9 9 0 0 0 1087 576 L 1044 576"/><path class="wire" d="M 1506 598 L 1105 598 A 9 9 0 0 1 1096 589 L 1096 585 A 9 9 0 0 0 1087 576 L 1044 576" stroke="var(--e-in)" marker-end="url(#ah-in-h)"/><text class="edge-label" x="1180" y="590" text-anchor="middle">signed events · triggers</text></g><g class="edge cat-in " data-from="email" data-to="webhooks"><path class="hit" d="M 1506 708 L 1089 708 A 9 9 0 0 1 1080 699 L 1080 599 A 9 9 0 0 0 1071 590 L 1044 590"/><path class="wire" d="M 1506 708 L 1089 708 A 9 9 0 0 1 1080 699 L 1080 599 A 9 9 0 0 0 1071 590 L 1044 590" stroke="var(--e-in)" marker-end="url(#ah-in-h)"/></g><g class="edge cat-out " data-from="proxy" data-to="slack"><path class="hit" d="M 1448 192 L 1461 192 A 9 9 0 0 0 1470 183 L 1470 73 A 9 9 0 0 1 1479 64 L 1711 64 A 9 9 0 0 1 1720 73 L 1720 617 A 9 9 0 0 1 1711 626 L 1674 626"/><path class="wire" d="M 1448 192 L 1461 192 A 9 9 0 0 0 1470 183 L 1470 73 A 9 9 0 0 1 1479 64 L 1711 64 A 9 9 0 0 1 1720 73 L 1720 617 A 9 9 0 0 1 1711 626 L 1674 626" stroke="var(--e-out)" marker-end="url(#ah-out-h)" marker-start="url(#ah-out-h-r)"/><text class="edge-label" x="1448" y="58" text-anchor="middle">credential-injected calls</text></g><g class="edge cat-out branch" data-from="proxy" data-to="idp"><path class="hit" d="M 1720 150 L 1674 150"/><path class="wire" d="M 1720 150 L 1674 150" stroke="var(--e-out)" marker-end="url(#ah-out-h)"/></g><g class="edge cat-out branch" data-from="proxy" data-to="apps"><path class="hit" d="M 1720 264 L 1674 264"/><path class="wire" d="M 1720 264 L 1674 264" stroke="var(--e-out)" marker-end="url(#ah-out-h)"/></g><g class="edge cat-out branch" data-from="proxy" data-to="tick"><path class="hit" d="M 1720 378 L 1674 378"/><path class="wire" d="M 1720 378 L 1674 378" stroke="var(--e-out)" marker-end="url(#ah-out-h)"/></g><g class="edge cat-out branch" data-from="proxy" data-to="hris"><path class="hit" d="M 1720 492 L 1674 492"/><path class="wire" d="M 1720 492 L 1674 492" stroke="var(--e-out)" marker-end="url(#ah-out-h)"/></g><g class="edge cat-out " data-from="engine" data-to="email"><path class="hit" d="M 724 437 L 724 713 A 9 9 0 0 0 733 722 L 788 722 A 8 8 0 0 1 804 722 L 872 722 A 8 8 0 0 1 888 722 L 1506 722"/><path class="wire" d="M 724 437 L 724 713 A 9 9 0 0 0 733 722 L 788 722 A 8 8 0 0 1 804 722 L 872 722 A 8 8 0 0 1 888 722 L 1506 722" stroke="var(--e-out)" marker-end="url(#ah-out-h)"/><text class="edge-label" x="990" y="714" text-anchor="middle">outbound email (SES)</text></g><g class="edge cat-store " data-from="api" data-to="postgres"><path class="hit" d="M 420 237 L 420 769"/><path class="wire" d="M 420 237 L 420 769" stroke="var(--e-store)" marker-end="url(#ah-store-h)" marker-start="url(#ah-store-h-r)"/><text class="edge-label" x="430" y="630" text-anchor="start">accounts · sessions · records</text></g><g class="edge cat-store " data-from="engine" data-to="postgres"><path class="hit" d="M 636 437 L 636 701 A 9 9 0 0 1 627 710 L 479 710 A 9 9 0 0 0 470 719 L 470 769"/><path class="wire" d="M 636 437 L 636 701 A 9 9 0 0 1 627 710 L 479 710 A 9 9 0 0 0 470 719 L 470 769" stroke="var(--e-store)" marker-end="url(#ah-store-h)" marker-start="url(#ah-store-h-r)"/><text class="edge-label" x="553" y="702" text-anchor="middle">tickets · runs · state</text></g><g class="edge cat-store " data-from="engine" data-to="s3"><path class="hit" d="M 676 437 L 676 769"/><path class="wire" d="M 676 437 L 676 769" stroke="var(--e-store)" marker-end="url(#ah-store-h)" marker-start="url(#ah-store-h-r)"/><text class="edge-label" x="684" y="762" text-anchor="start">attachments · large results</text></g><g class="edge cat-store " data-from="engine" data-to="vector"><path class="hit" d="M 744 437 L 744 686 A 9 9 0 0 0 753 695 L 871 695 A 9 9 0 0 1 880 704 L 880 769"/><path class="wire" d="M 744 437 L 744 686 A 9 9 0 0 0 753 695 L 871 695 A 9 9 0 0 1 880 704 L 880 769" stroke="var(--e-store)" marker-end="url(#ah-store-h)" marker-start="url(#ah-store-h-r)"/><text class="edge-label" x="812" y="687" text-anchor="middle">embeddings · search</text></g><g class="edge cat-store " data-from="engine" data-to="audit"><path class="hit" d="M 784 412 L 787 412 A 9 9 0 0 1 796 421 L 796 735 A 9 9 0 0 0 805 744 L 872 744 A 8 8 0 0 1 888 744 L 1101 744 A 9 9 0 0 1 1110 753 L 1110 769"/><path class="wire" d="M 784 412 L 787 412 A 9 9 0 0 1 796 421 L 796 735 A 9 9 0 0 0 805 744 L 872 744 A 8 8 0 0 1 888 744 L 1101 744 A 9 9 0 0 1 1110 753 L 1110 769" stroke="var(--e-store)" marker-end="url(#ah-store-h)"/><text class="edge-label" x="1000" y="738" text-anchor="middle">audit events — all components</text></g><g class="edge cat-store " data-from="proxy" data-to="creds"><path class="hit" d="M 1373 219 L 1373 251"/><path class="wire" d="M 1373 219 L 1373 251" stroke="var(--e-store)" marker-end="url(#ah-store-h)" marker-start="url(#ah-store-h-r)"/></g><circle class="junction" cx="1720" cy="150" r="3.5" fill="var(--e-out)"/><circle class="junction" cx="1720" cy="264" r="3.5" fill="var(--e-out)"/><circle class="junction" cx="1720" cy="378" r="3.5" fill="var(--e-out)"/><circle class="junction" cx="1720" cy="492" r="3.5" fill="var(--e-out)"/><g class="node kind-actor" data-node="user"><rect class="node-body" x="52.0" y="184.0" width="140" height="52" rx="26.0"/><text class="node-label" x="122" y="209">User</text><text class="node-sub" x="122" y="227">browser</text></g><g class="node kind-platform" data-node="temporal"><rect class="node-body" x="44.0" y="457.0" width="156" height="54" rx="10"/><text class="node-label" x="122" y="483">Temporal</text><text class="node-sub" x="122" y="501">Cloud · self-hosted</text></g><g class="node kind-platform" data-node="llm"><rect class="node-body" x="44.0" y="602.0" width="156" height="54" rx="10"/><text class="node-label" x="122" y="628">LLM providers</text><text class="node-sub" x="122" y="646">Anthropic · OpenAI</text></g><g class="node kind-process" data-node="api"><rect class="node-body" x="336.0" y="183.0" width="168" height="54" rx="10"/><text class="node-label" x="420" y="209">API / Web UI</text><text class="node-sub" x="420" y="227">web app · public API · MCP</text></g><g class="node kind-process" data-node="engine"><rect class="node-body" x="616.0" y="383.0" width="168" height="54" rx="10"/><text class="node-label" x="700" y="409">Workflow engine</text><text class="node-sub" x="700" y="427">workflows · agents · workers</text></g><g class="node kind-process" data-node="proxy"><rect class="node-body" x="1298.0" y="165.0" width="150" height="54" rx="10"/><text class="node-label" x="1373" y="191">Credential Proxy</text><text class="node-sub" x="1373" y="209">on your Serval worker</text></g><g class="node kind-store" data-node="creds"><path class="store-body" d="M 1308.0 260.0 A 65.0 9 0 0 1 1438.0 260.0 L 1438.0 304.0 A 65.0 9 0 0 1 1308.0 304.0 Z"/><path class="store-lid" d="M 1308.0 260.0 A 65.0 9 0 0 0 1438.0 260.0"/><text class="node-label" x="1373" y="284">Credentials</text><text class="node-sub" x="1373" y="301">Kubernetes Secret</text></g><g class="node kind-process" data-node="webhooks"><rect class="node-body" x="876.0" y="543.0" width="168" height="54" rx="10"/><text class="node-label" x="960" y="569">Webhooks</text><text class="node-sub" x="960" y="587">signature-verified receiver</text></g><g class="node kind-store" data-node="postgres"><path class="store-body" d="M 355.0 778.0 A 75.0 9 0 0 1 505.0 778.0 L 505.0 822.0 A 75.0 9 0 0 1 355.0 822.0 Z"/><path class="store-lid" d="M 355.0 778.0 A 75.0 9 0 0 0 505.0 778.0"/><text class="node-label" x="430" y="802">Postgres</text><text class="node-sub" x="430" y="819">app data</text></g><g class="node kind-store" data-node="s3"><path class="store-body" d="M 615.0 778.0 A 65.0 9 0 0 1 745.0 778.0 L 745.0 822.0 A 65.0 9 0 0 1 615.0 822.0 Z"/><path class="store-lid" d="M 615.0 778.0 A 65.0 9 0 0 0 745.0 778.0"/><text class="node-label" x="680" y="802">S3</text><text class="node-sub" x="680" y="819">attachments</text></g><g class="node kind-store" data-node="vector"><path class="store-body" d="M 835.0 778.0 A 65.0 9 0 0 1 965.0 778.0 L 965.0 822.0 A 65.0 9 0 0 1 835.0 822.0 Z"/><path class="store-lid" d="M 835.0 778.0 A 65.0 9 0 0 0 965.0 778.0"/><text class="node-label" x="900" y="802">Vector store</text><text class="node-sub" x="900" y="819">pgvector</text></g><g class="node kind-store" data-node="audit"><path class="store-body" d="M 1045.0 778.0 A 65.0 9 0 0 1 1175.0 778.0 L 1175.0 822.0 A 65.0 9 0 0 1 1045.0 822.0 Z"/><path class="store-lid" d="M 1045.0 778.0 A 65.0 9 0 0 0 1175.0 778.0"/><text class="node-label" x="1110" y="802">Audit log</text><text class="node-sub" x="1110" y="819">append-only</text></g><g class="node kind-external" data-node="idp"><rect class="node-body" x="1506.0" y="116.0" width="168" height="68" rx="10"/><text class="node-label" x="1590" y="140.0">Identity provider</text><text class="node-sub" x="1590" y="156.0">Okta · Entra ID</text><text class="node-cap" x="1590" y="172.0">SSO · directory sync</text></g><g class="node kind-external" data-node="apps"><rect class="node-body" x="1506.0" y="230.0" width="168" height="68" rx="10"/><text class="node-label" x="1590" y="254.0">Internal apps</text><text class="node-sub" x="1590" y="270.0">REST APIs</text><text class="node-cap" x="1590" y="286.0">API calls · event triggers</text></g><g class="node kind-external" data-node="tick"><rect class="node-body" x="1506.0" y="344.0" width="168" height="68" rx="10"/><text class="node-label" x="1590" y="368.0">Ticketing</text><text class="node-sub" x="1590" y="384.0">Jira · ServiceNow</text><text class="node-cap" x="1590" y="400.0">ticket sync · events</text></g><g class="node kind-external" data-node="hris"><rect class="node-body" x="1506.0" y="458.0" width="168" height="68" rx="10"/><text class="node-label" x="1590" y="482.0">HRIS</text><text class="node-sub" x="1590" y="498.0">Workday · BambooHR</text><text class="node-cap" x="1590" y="514.0">employee · PTO data</text></g><g class="node kind-external" data-node="slack"><rect class="node-body" x="1506.0" y="592.0" width="168" height="68" rx="10"/><text class="node-label" x="1590" y="616.0">Slack</text><text class="node-sub" x="1590" y="632.0">Enterprise Grid</text><text class="node-cap" x="1590" y="648.0">messages · events · commands</text></g><g class="node kind-external" data-node="email"><rect class="node-body" x="1506.0" y="702.0" width="168" height="68" rx="10"/><text class="node-label" x="1590" y="726.0">Email</text><text class="node-sub" x="1590" y="742.0">SES in · out</text><text class="node-cap" x="1590" y="758.0">mail ingest · outbound mail</text></g><g class="legend"><line x1="330" y1="892" x2="356" y2="892" stroke="var(--e-user)" stroke-width="2.5"/><path d="M 350 888.5 L 356 892 L 350 895.5" fill="none" stroke="var(--e-user)" stroke-width="1.6"/><text class="legend-label" x="363" y="896">User &amp; application traffic</text><line x1="556.4" y1="892" x2="582.4" y2="892" stroke="var(--e-out)" stroke-width="2.5"/><path d="M 576.4 888.5 L 582.4 892 L 576.4 895.5" fill="none" stroke="var(--e-out)" stroke-width="1.6"/><text class="legend-label" x="589.4" y="896">Outbound (Serval-initiated)</text><line x1="789.2" y1="892" x2="815.2" y2="892" stroke="var(--e-in)" stroke-width="2.5"/><path d="M 809.2 888.5 L 815.2 892 L 809.2 895.5" fill="none" stroke="var(--e-in)" stroke-width="1.6"/><text class="legend-label" x="822.2" y="896">Inbound events</text><line x1="938.8000000000001" y1="892" x2="964.8000000000001" y2="892" stroke="var(--e-ai)" stroke-width="2.5"/><path d="M 958.8000000000001 888.5 L 964.8000000000001 892 L 958.8000000000001 895.5" fill="none" stroke="var(--e-ai)" stroke-width="1.6"/><text class="legend-label" x="971.8000000000001" y="896">Execution &amp; AI platform</text><line x1="1146.0" y1="892" x2="1172.0" y2="892" stroke="var(--e-store)" stroke-width="2.5"/><path d="M 1166.0 888.5 L 1172.0 892 L 1166.0 895.5" fill="none" stroke="var(--e-store)" stroke-width="1.6"/><text class="legend-label" x="1179.0" y="896">Storage I/O</text></g><g class="titleblock"><text class="title" x="44" y="34">Serval — platform data flow (hybrid self-hosted)</text><text class="subtitle" x="44" y="54">last revised 2026-09-02</text></g></svg>`
}} />;

export const DataFlowDiagram = () => <div style={{
  overflowX: 'auto',
  width: '100%'
}} dangerouslySetInnerHTML={{
  __html: `<svg viewBox="0 0 1560 920" width="100%" style="height:auto" xmlns="http://www.w3.org/2000/svg" role="img" aria-label="Serval platform data flow diagram" id="dfd"><style>
  svg { 
  --paper: #fdfcfe; --ink: #251f38; --ink-2: #4d4763; --ink-3: #8b86a0;
  --zone-fill: rgba(109,63,212,.024); --zone-line: #b9b2cf;
  --band-fill: rgba(109,63,212,.05);
  --node-fill: #f3eefc; --node-line: #8f6ade;
  --actor-fill: #ffffff; --actor-line: #6d3fd4;
  --ext-fill: #eef3f8; --ext-line: #7d95ad;
  --plat-fill: #eef1fb; --plat-line: #7d93d8;
  --storef: #f0ede1; --stores: #a89f7d;
  --e-user: #6d3fd4;
  --e-out: #0c7d6c;
  --e-in: #b4610e;
  --e-ai: #3d63c9;
  --e-store: #77708f;
 }
  @media (prefers-color-scheme: dark) { svg { 
  --paper: #16141d; --ink: #ece9f4; --ink-2: #b6b0c9; --ink-3: #7d7794;
  --zone-fill: rgba(166,135,236,.04); --zone-line: #4a4362;
  --band-fill: rgba(166,135,236,.07);
  --node-fill: #2a2440; --node-line: #8f6ade;
  --actor-fill: #1d1a28; --actor-line: #a687ec;
  --ext-fill: #1f2733; --ext-line: #5d7791;
  --plat-fill: #202940; --plat-line: #6d84c9;
  --storef: #2b2920; --stores: #8d845f;
  --e-user: #a687ec;
  --e-out: #3fb9a5;
  --e-in: #e59a4b;
  --e-ai: #7d9bee;
  --e-store: #8d86a6;
 } }
  :root[data-theme="light"] svg { 
  --paper: #fdfcfe; --ink: #251f38; --ink-2: #4d4763; --ink-3: #8b86a0;
  --zone-fill: rgba(109,63,212,.024); --zone-line: #b9b2cf;
  --band-fill: rgba(109,63,212,.05);
  --node-fill: #f3eefc; --node-line: #8f6ade;
  --actor-fill: #ffffff; --actor-line: #6d3fd4;
  --ext-fill: #eef3f8; --ext-line: #7d95ad;
  --plat-fill: #eef1fb; --plat-line: #7d93d8;
  --storef: #f0ede1; --stores: #a89f7d;
  --e-user: #6d3fd4;
  --e-out: #0c7d6c;
  --e-in: #b4610e;
  --e-ai: #3d63c9;
  --e-store: #77708f;
 }
  :root[data-theme="dark"] svg { 
  --paper: #16141d; --ink: #ece9f4; --ink-2: #b6b0c9; --ink-3: #7d7794;
  --zone-fill: rgba(166,135,236,.04); --zone-line: #4a4362;
  --band-fill: rgba(166,135,236,.07);
  --node-fill: #2a2440; --node-line: #8f6ade;
  --actor-fill: #1d1a28; --actor-line: #a687ec;
  --ext-fill: #1f2733; --ext-line: #5d7791;
  --plat-fill: #202940; --plat-line: #6d84c9;
  --storef: #2b2920; --stores: #8d845f;
  --e-user: #a687ec;
  --e-out: #3fb9a5;
  --e-in: #e59a4b;
  --e-ai: #7d9bee;
  --e-store: #8d86a6;
 }
  :root.light svg { 
  --paper: #fdfcfe; --ink: #251f38; --ink-2: #4d4763; --ink-3: #8b86a0;
  --zone-fill: rgba(109,63,212,.024); --zone-line: #b9b2cf;
  --band-fill: rgba(109,63,212,.05);
  --node-fill: #f3eefc; --node-line: #8f6ade;
  --actor-fill: #ffffff; --actor-line: #6d3fd4;
  --ext-fill: #eef3f8; --ext-line: #7d95ad;
  --plat-fill: #eef1fb; --plat-line: #7d93d8;
  --storef: #f0ede1; --stores: #a89f7d;
  --e-user: #6d3fd4;
  --e-out: #0c7d6c;
  --e-in: #b4610e;
  --e-ai: #3d63c9;
  --e-store: #77708f;
 }
  :root.dark svg { 
  --paper: #16141d; --ink: #ece9f4; --ink-2: #b6b0c9; --ink-3: #7d7794;
  --zone-fill: rgba(166,135,236,.04); --zone-line: #4a4362;
  --band-fill: rgba(166,135,236,.07);
  --node-fill: #2a2440; --node-line: #8f6ade;
  --actor-fill: #1d1a28; --actor-line: #a687ec;
  --ext-fill: #1f2733; --ext-line: #5d7791;
  --plat-fill: #202940; --plat-line: #6d84c9;
  --storef: #2b2920; --stores: #8d845f;
  --e-user: #a687ec;
  --e-out: #3fb9a5;
  --e-in: #e59a4b;
  --e-ai: #7d9bee;
  --e-store: #8d86a6;
 }

  svg { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; }
  .zone rect { fill: var(--zone-fill); stroke: var(--zone-line); stroke-width: 1.2; stroke-dasharray: 7 5; }
  .zone-title { font-size: 13.5px; font-weight: 650; fill: var(--ink-2); letter-spacing: .04em; }
  .zone-sub { font-size: 11px; fill: var(--ink-3); }
  .store-band { fill: var(--band-fill); }
  .band-label { font-family: ui-monospace, "SF Mono", Menlo, monospace; letter-spacing: .14em; font-size: 10px; fill: var(--ink-3); }

  .node-body { fill: var(--node-fill); stroke: var(--node-line); stroke-width: 1.4; }
  .kind-actor .node-body { fill: var(--actor-fill); stroke: var(--actor-line); }
  .kind-external .node-body { fill: var(--ext-fill); stroke: var(--ext-line); }
  .kind-platform .node-body { fill: var(--plat-fill); stroke: var(--plat-line); }
  .store-body { fill: var(--storef); stroke: var(--stores); stroke-width: 1.4; }
  .store-lid { fill: none; stroke: var(--stores); stroke-width: 1.4; }
  .node-label { font-size: 14.5px; font-weight: 640; fill: var(--ink); text-anchor: middle; }
  .node-sub { font-size: 10.5px; fill: var(--ink-3); text-anchor: middle; }
  .node-cap { font-size: 10.5px; font-weight: 570; fill: var(--ink-2); text-anchor: middle; }
  .kind-store .node-label { font-size: 13px; }

  .edge .wire { fill: none; stroke-width: 2; }
  .edge .hit { fill: none; stroke: transparent; stroke-width: 14; }
  .edge-label { font-size: 11.5px; font-weight: 570; fill: var(--ink-2);
    paint-order: stroke; stroke: var(--paper); stroke-width: 5px; stroke-linejoin: round; }
  .legend-label { font-size: 11.5px; fill: var(--ink-2); }
  .title { font-size: 19px; font-weight: 700; fill: var(--ink); letter-spacing: -.01em; }
  .subtitle { font-size: 11.5px; fill: var(--ink-3); }
  .fn, .fn-num { font-size: 10.5px; fill: var(--ink-3); }
  .fn-num { font-weight: 700; fill: var(--ink-2); }

  .edge, .node { transition: opacity .15s; }
  .edge .hit { pointer-events: stroke; }
  .node { pointer-events: bounding-box; }
  svg:has(.edge:hover) .edge:not(:hover) { opacity: .12; }
  svg:has(.edge:hover) .node { opacity: .3; }
svg:has(.edge[data-from="user"][data-to="api"]:hover) :is(.node[data-node="user"], .node[data-node="api"]) { opacity: 1; }
svg:has(.edge[data-from="idp"][data-to="api"]:hover) :is(.node[data-node="idp"], .node[data-node="api"]) { opacity: 1; }
svg:has(.edge[data-from="api"][data-to="engine"]:hover) :is(.node[data-node="api"], .node[data-node="engine"]) { opacity: 1; }
svg:has(.edge[data-from="api"][data-to="proxy"]:hover) :is(.node[data-node="api"], .node[data-node="proxy"]) { opacity: 1; }
svg:has(.edge[data-from="engine"][data-to="proxy"]:hover) :is(.node[data-node="engine"], .node[data-node="proxy"]) { opacity: 1; }
svg:has(.edge[data-from="engine"][data-to="temporal"]:hover) :is(.node[data-node="engine"], .node[data-node="temporal"]) { opacity: 1; }
svg:has(.edge[data-from="engine"][data-to="llm"]:hover) :is(.node[data-node="engine"], .node[data-node="llm"]) { opacity: 1; }
svg:has(.edge[data-from="webhooks"][data-to="engine"]:hover) :is(.node[data-node="webhooks"], .node[data-node="engine"]) { opacity: 1; }
svg:has(.edge[data-from="apps"][data-to="webhooks"]:hover) :is(.node[data-node="apps"], .node[data-node="webhooks"]) { opacity: 1; }
svg:has(.edge[data-from="tick"][data-to="webhooks"]:hover) :is(.node[data-node="tick"], .node[data-node="webhooks"]) { opacity: 1; }
svg:has(.edge[data-from="slack"][data-to="webhooks"]:hover) :is(.node[data-node="slack"], .node[data-node="webhooks"]) { opacity: 1; }
svg:has(.edge[data-from="email"][data-to="webhooks"]:hover) :is(.node[data-node="email"], .node[data-node="webhooks"]) { opacity: 1; }
svg:has(.edge[data-from="proxy"][data-to="slack"]:hover) :is(.node[data-node="proxy"], .node[data-node="slack"]) { opacity: 1; }
svg:has(.edge[data-from="proxy"][data-to="idp"]:hover) :is(.node[data-node="proxy"], .node[data-node="idp"]) { opacity: 1; }
svg:has(.edge[data-from="proxy"][data-to="apps"]:hover) :is(.node[data-node="proxy"], .node[data-node="apps"]) { opacity: 1; }
svg:has(.edge[data-from="proxy"][data-to="tick"]:hover) :is(.node[data-node="proxy"], .node[data-node="tick"]) { opacity: 1; }
svg:has(.edge[data-from="proxy"][data-to="hris"]:hover) :is(.node[data-node="proxy"], .node[data-node="hris"]) { opacity: 1; }
svg:has(.edge[data-from="engine"][data-to="email"]:hover) :is(.node[data-node="engine"], .node[data-node="email"]) { opacity: 1; }
svg:has(.edge[data-from="api"][data-to="postgres"]:hover) :is(.node[data-node="api"], .node[data-node="postgres"]) { opacity: 1; }
svg:has(.edge[data-from="engine"][data-to="postgres"]:hover) :is(.node[data-node="engine"], .node[data-node="postgres"]) { opacity: 1; }
svg:has(.edge[data-from="engine"][data-to="s3"]:hover) :is(.node[data-node="engine"], .node[data-node="s3"]) { opacity: 1; }
svg:has(.edge[data-from="engine"][data-to="vector"]:hover) :is(.node[data-node="engine"], .node[data-node="vector"]) { opacity: 1; }
svg:has(.edge[data-from="engine"][data-to="audit"]:hover) :is(.node[data-node="engine"], .node[data-node="audit"]) { opacity: 1; }
svg:has(.node[data-node="user"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="user"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="user"]:hover) :is(.edge[data-from="user"], .edge[data-to="user"]) { opacity: 1; }
svg:has(.node[data-node="user"]:hover) :is(.node[data-node="api"], .node[data-node="user"]) { opacity: 1; }
svg:has(.node[data-node="temporal"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="temporal"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="temporal"]:hover) :is(.edge[data-from="temporal"], .edge[data-to="temporal"]) { opacity: 1; }
svg:has(.node[data-node="temporal"]:hover) :is(.node[data-node="engine"], .node[data-node="temporal"]) { opacity: 1; }
svg:has(.node[data-node="llm"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="llm"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="llm"]:hover) :is(.edge[data-from="llm"], .edge[data-to="llm"]) { opacity: 1; }
svg:has(.node[data-node="llm"]:hover) :is(.node[data-node="engine"], .node[data-node="llm"]) { opacity: 1; }
svg:has(.node[data-node="api"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="api"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="api"]:hover) :is(.edge[data-from="api"], .edge[data-to="api"]) { opacity: 1; }
svg:has(.node[data-node="api"]:hover) :is(.node[data-node="api"], .node[data-node="engine"], .node[data-node="idp"], .node[data-node="postgres"], .node[data-node="proxy"], .node[data-node="user"]) { opacity: 1; }
svg:has(.node[data-node="engine"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="engine"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="engine"]:hover) :is(.edge[data-from="engine"], .edge[data-to="engine"]) { opacity: 1; }
svg:has(.node[data-node="engine"]:hover) :is(.node[data-node="api"], .node[data-node="audit"], .node[data-node="email"], .node[data-node="engine"], .node[data-node="llm"], .node[data-node="postgres"], .node[data-node="proxy"], .node[data-node="s3"], .node[data-node="temporal"], .node[data-node="vector"], .node[data-node="webhooks"]) { opacity: 1; }
svg:has(.node[data-node="proxy"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="proxy"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="proxy"]:hover) :is(.edge[data-from="proxy"], .edge[data-to="proxy"]) { opacity: 1; }
svg:has(.node[data-node="proxy"]:hover) :is(.node[data-node="api"], .node[data-node="apps"], .node[data-node="engine"], .node[data-node="hris"], .node[data-node="idp"], .node[data-node="proxy"], .node[data-node="slack"], .node[data-node="tick"]) { opacity: 1; }
svg:has(.node[data-node="webhooks"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="webhooks"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="webhooks"]:hover) :is(.edge[data-from="webhooks"], .edge[data-to="webhooks"]) { opacity: 1; }
svg:has(.node[data-node="webhooks"]:hover) :is(.node[data-node="apps"], .node[data-node="email"], .node[data-node="engine"], .node[data-node="slack"], .node[data-node="tick"], .node[data-node="webhooks"]) { opacity: 1; }
svg:has(.node[data-node="postgres"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="postgres"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="postgres"]:hover) :is(.edge[data-from="postgres"], .edge[data-to="postgres"]) { opacity: 1; }
svg:has(.node[data-node="postgres"]:hover) :is(.node[data-node="api"], .node[data-node="engine"], .node[data-node="postgres"]) { opacity: 1; }
svg:has(.node[data-node="s3"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="s3"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="s3"]:hover) :is(.edge[data-from="s3"], .edge[data-to="s3"]) { opacity: 1; }
svg:has(.node[data-node="s3"]:hover) :is(.node[data-node="engine"], .node[data-node="s3"]) { opacity: 1; }
svg:has(.node[data-node="vector"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="vector"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="vector"]:hover) :is(.edge[data-from="vector"], .edge[data-to="vector"]) { opacity: 1; }
svg:has(.node[data-node="vector"]:hover) :is(.node[data-node="engine"], .node[data-node="vector"]) { opacity: 1; }
svg:has(.node[data-node="audit"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="audit"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="audit"]:hover) :is(.edge[data-from="audit"], .edge[data-to="audit"]) { opacity: 1; }
svg:has(.node[data-node="audit"]:hover) :is(.node[data-node="audit"], .node[data-node="engine"]) { opacity: 1; }
svg:has(.node[data-node="idp"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="idp"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="idp"]:hover) :is(.edge[data-from="idp"], .edge[data-to="idp"]) { opacity: 1; }
svg:has(.node[data-node="idp"]:hover) :is(.node[data-node="api"], .node[data-node="idp"], .node[data-node="proxy"]) { opacity: 1; }
svg:has(.node[data-node="apps"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="apps"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="apps"]:hover) :is(.edge[data-from="apps"], .edge[data-to="apps"]) { opacity: 1; }
svg:has(.node[data-node="apps"]:hover) :is(.node[data-node="apps"], .node[data-node="proxy"], .node[data-node="webhooks"]) { opacity: 1; }
svg:has(.node[data-node="tick"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="tick"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="tick"]:hover) :is(.edge[data-from="tick"], .edge[data-to="tick"]) { opacity: 1; }
svg:has(.node[data-node="tick"]:hover) :is(.node[data-node="proxy"], .node[data-node="tick"], .node[data-node="webhooks"]) { opacity: 1; }
svg:has(.node[data-node="hris"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="hris"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="hris"]:hover) :is(.edge[data-from="hris"], .edge[data-to="hris"]) { opacity: 1; }
svg:has(.node[data-node="hris"]:hover) :is(.node[data-node="hris"], .node[data-node="proxy"]) { opacity: 1; }
svg:has(.node[data-node="slack"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="slack"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="slack"]:hover) :is(.edge[data-from="slack"], .edge[data-to="slack"]) { opacity: 1; }
svg:has(.node[data-node="slack"]:hover) :is(.node[data-node="proxy"], .node[data-node="slack"], .node[data-node="webhooks"]) { opacity: 1; }
svg:has(.node[data-node="email"]:hover) .edge { opacity: .12; }
svg:has(.node[data-node="email"]:hover) .node { opacity: .3; }
svg:has(.node[data-node="email"]:hover) :is(.edge[data-from="email"], .edge[data-to="email"]) { opacity: 1; }
svg:has(.node[data-node="email"]:hover) :is(.node[data-node="email"], .node[data-node="engine"], .node[data-node="webhooks"]) { opacity: 1; }</style><defs><marker id="ah-user" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-user)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="ah-user-r" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-user)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="ah-out" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-out)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="ah-out-r" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-out)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="ah-in" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-in)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="ah-in-r" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-in)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="ah-ai" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-ai)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="ah-ai-r" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-ai)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="ah-store" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-store)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker><marker id="ah-store-r" viewBox="0 0 10 10" refX="8.5" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M 1 1.2 L 8.5 5 L 1 8.8" fill="none" stroke="var(--e-store)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></marker></defs><rect x="0" y="0" width="1560" height="920" fill="var(--paper)"/><g class="zone"><rect x="290" y="82" width="950" height="796" rx="16"/><text class="zone-title" x="308" y="110">Serval Cloud (VPC)</text></g><g class="zone"><rect x="1272" y="72" width="274" height="724" rx="16"/><text class="zone-title" x="1290" y="100">Customer environment</text></g><g class="zone"><rect x="36" y="424" width="172" height="100" rx="16"/><text class="zone-title" x="54" y="452">Execution platform</text></g><g class="zone"><rect x="36" y="569" width="172" height="100" rx="16"/><text class="zone-title" x="54" y="597">AI services</text></g><rect class="store-band" x="314" y="752" width="902" height="102" rx="12"/><text class="band-label" x="330" y="772">STORAGE</text><g class="edge cat-user " data-from="user" data-to="api"><path class="hit" d="M 192 210 L 336 210"/><path class="wire" d="M 192 210 L 336 210" stroke="var(--e-user)" marker-end="url(#ah-user)" marker-start="url(#ah-user-r)"/><text class="edge-label" x="264" y="202" text-anchor="middle">invoke · results</text></g><g class="edge cat-user " data-from="idp" data-to="api"><path class="hit" d="M 1454 132 L 1513 132 A 9 9 0 0 0 1522 123 L 1522 49 A 9 9 0 0 0 1513 40 L 429 40 A 9 9 0 0 0 420 49 L 420 183"/><path class="wire" d="M 1454 132 L 1513 132 A 9 9 0 0 0 1522 123 L 1522 49 A 9 9 0 0 0 1513 40 L 429 40 A 9 9 0 0 0 420 49 L 420 183" stroke="var(--e-user)" marker-end="url(#ah-user)"/><text class="edge-label" x="806" y="34" text-anchor="middle">SSO · SCIM directory sync</text></g><g class="edge cat-user " data-from="api" data-to="engine"><path class="hit" d="M 504 218 L 631 218 A 9 9 0 0 1 640 227 L 640 383"/><path class="wire" d="M 504 218 L 631 218 A 9 9 0 0 1 640 227 L 640 383" stroke="var(--e-user)" marker-end="url(#ah-user)" marker-start="url(#ah-user-r)"/><text class="edge-label" x="575" y="240" text-anchor="start">definitions · runs · results</text></g><g class="edge cat-user " data-from="api" data-to="proxy"><path class="hit" d="M 504 202 L 876 202"/><path class="wire" d="M 504 202 L 876 202" stroke="var(--e-user)" marker-end="url(#ah-user)"/><text class="edge-label" x="690" y="194" text-anchor="middle">integration setup · OAuth</text></g><g class="edge cat-user " data-from="engine" data-to="proxy"><path class="hit" d="M 784 390 L 911 390 A 9 9 0 0 0 920 381 L 920 237"/><path class="wire" d="M 784 390 L 911 390 A 9 9 0 0 0 920 381 L 920 237" stroke="var(--e-user)" marker-end="url(#ah-user)" marker-start="url(#ah-user-r)"/><text class="edge-label" x="928" y="300" text-anchor="start">integration calls</text></g><g class="edge cat-ai " data-from="engine" data-to="temporal"><path class="hit" d="M 616 395 L 428 395 A 8 8 0 0 0 412 395 L 249 395 A 9 9 0 0 0 240 404 L 240 461 A 9 9 0 0 1 231 470 L 200 470"/><path class="wire" d="M 616 395 L 428 395 A 8 8 0 0 0 412 395 L 249 395 A 9 9 0 0 0 240 404 L 240 461 A 9 9 0 0 1 231 470 L 200 470" stroke="var(--e-ai)" marker-end="url(#ah-ai)" marker-start="url(#ah-ai-r)"/><text class="edge-label" x="500" y="387" text-anchor="middle">durable orchestration</text></g><g class="edge cat-ai " data-from="engine" data-to="llm"><path class="hit" d="M 616 425 L 428 425 A 8 8 0 0 0 412 425 L 271 425 A 9 9 0 0 0 262 434 L 262 606 A 9 9 0 0 1 253 615 L 200 615"/><path class="wire" d="M 616 425 L 428 425 A 8 8 0 0 0 412 425 L 271 425 A 9 9 0 0 0 262 434 L 262 606 A 9 9 0 0 1 253 615 L 200 615" stroke="var(--e-ai)" marker-end="url(#ah-ai)"/><text class="edge-label" x="500" y="441" text-anchor="middle">AI steps · embeddings</text></g><g class="edge cat-in " data-from="webhooks" data-to="engine"><path class="hit" d="M 876 547 L 817 547 A 9 9 0 0 1 808 538 L 808 439 A 9 9 0 0 0 799 430 L 784 430"/><path class="wire" d="M 876 547 L 817 547 A 9 9 0 0 1 808 538 L 808 439 A 9 9 0 0 0 799 430 L 784 430" stroke="var(--e-in)" marker-end="url(#ah-in)"/><text class="edge-label" x="870" y="539" text-anchor="end">events trigger runs</text></g><g class="edge cat-in " data-from="apps" data-to="webhooks"><path class="hit" d="M 1286 282 L 1121 282 A 9 9 0 0 0 1112 291 L 1112 539 A 9 9 0 0 1 1103 548 L 1044 548"/><path class="wire" d="M 1286 282 L 1121 282 A 9 9 0 0 0 1112 291 L 1112 539 A 9 9 0 0 1 1103 548 L 1044 548" stroke="var(--e-in)" marker-end="url(#ah-in)"/></g><g class="edge cat-in " data-from="tick" data-to="webhooks"><path class="hit" d="M 1286 396 L 1145 396 A 9 9 0 0 0 1136 405 L 1136 553 A 9 9 0 0 1 1127 562 L 1044 562"/><path class="wire" d="M 1286 396 L 1145 396 A 9 9 0 0 0 1136 405 L 1136 553 A 9 9 0 0 1 1127 562 L 1044 562" stroke="var(--e-in)" marker-end="url(#ah-in)"/></g><g class="edge cat-in " data-from="slack" data-to="webhooks"><path class="hit" d="M 1286 598 L 1105 598 A 9 9 0 0 1 1096 589 L 1096 585 A 9 9 0 0 0 1087 576 L 1044 576"/><path class="wire" d="M 1286 598 L 1105 598 A 9 9 0 0 1 1096 589 L 1096 585 A 9 9 0 0 0 1087 576 L 1044 576" stroke="var(--e-in)" marker-end="url(#ah-in)"/><text class="edge-label" x="1180" y="590" text-anchor="middle">signed events · triggers</text></g><g class="edge cat-in " data-from="email" data-to="webhooks"><path class="hit" d="M 1286 708 L 1089 708 A 9 9 0 0 1 1080 699 L 1080 599 A 9 9 0 0 0 1071 590 L 1044 590"/><path class="wire" d="M 1286 708 L 1089 708 A 9 9 0 0 1 1080 699 L 1080 599 A 9 9 0 0 0 1071 590 L 1044 590" stroke="var(--e-in)" marker-end="url(#ah-in)"/></g><g class="edge cat-out " data-from="proxy" data-to="slack"><path class="hit" d="M 960 183 L 960 73 A 9 9 0 0 1 969 64 L 1491 64 A 9 9 0 0 1 1500 73 L 1500 617 A 9 9 0 0 1 1491 626 L 1454 626"/><path class="wire" d="M 960 183 L 960 73 A 9 9 0 0 1 969 64 L 1491 64 A 9 9 0 0 1 1500 73 L 1500 617 A 9 9 0 0 1 1491 626 L 1454 626" stroke="var(--e-out)" marker-end="url(#ah-out)" marker-start="url(#ah-out-r)"/><text class="edge-label" x="1228" y="58" text-anchor="middle">credential-injected calls</text></g><g class="edge cat-out branch" data-from="proxy" data-to="idp"><path class="hit" d="M 1500 150 L 1454 150"/><path class="wire" d="M 1500 150 L 1454 150" stroke="var(--e-out)" marker-end="url(#ah-out)"/></g><g class="edge cat-out branch" data-from="proxy" data-to="apps"><path class="hit" d="M 1500 264 L 1454 264"/><path class="wire" d="M 1500 264 L 1454 264" stroke="var(--e-out)" marker-end="url(#ah-out)"/></g><g class="edge cat-out branch" data-from="proxy" data-to="tick"><path class="hit" d="M 1500 378 L 1454 378"/><path class="wire" d="M 1500 378 L 1454 378" stroke="var(--e-out)" marker-end="url(#ah-out)"/></g><g class="edge cat-out branch" data-from="proxy" data-to="hris"><path class="hit" d="M 1500 492 L 1454 492"/><path class="wire" d="M 1500 492 L 1454 492" stroke="var(--e-out)" marker-end="url(#ah-out)"/></g><g class="edge cat-out " data-from="engine" data-to="email"><path class="hit" d="M 724 437 L 724 713 A 9 9 0 0 0 733 722 L 788 722 A 8 8 0 0 1 804 722 L 872 722 A 8 8 0 0 1 888 722 L 1286 722"/><path class="wire" d="M 724 437 L 724 713 A 9 9 0 0 0 733 722 L 788 722 A 8 8 0 0 1 804 722 L 872 722 A 8 8 0 0 1 888 722 L 1286 722" stroke="var(--e-out)" marker-end="url(#ah-out)"/><text class="edge-label" x="990" y="714" text-anchor="middle">outbound email (SES)</text></g><g class="edge cat-store " data-from="api" data-to="postgres"><path class="hit" d="M 420 237 L 420 769"/><path class="wire" d="M 420 237 L 420 769" stroke="var(--e-store)" marker-end="url(#ah-store)" marker-start="url(#ah-store-r)"/><text class="edge-label" x="430" y="630" text-anchor="start">accounts · sessions · records</text></g><g class="edge cat-store " data-from="engine" data-to="postgres"><path class="hit" d="M 636 437 L 636 701 A 9 9 0 0 1 627 710 L 479 710 A 9 9 0 0 0 470 719 L 470 769"/><path class="wire" d="M 636 437 L 636 701 A 9 9 0 0 1 627 710 L 479 710 A 9 9 0 0 0 470 719 L 470 769" stroke="var(--e-store)" marker-end="url(#ah-store)" marker-start="url(#ah-store-r)"/><text class="edge-label" x="553" y="702" text-anchor="middle">tickets · runs · state</text></g><g class="edge cat-store " data-from="engine" data-to="s3"><path class="hit" d="M 676 437 L 676 769"/><path class="wire" d="M 676 437 L 676 769" stroke="var(--e-store)" marker-end="url(#ah-store)" marker-start="url(#ah-store-r)"/><text class="edge-label" x="684" y="762" text-anchor="start">attachments · large results</text></g><g class="edge cat-store " data-from="engine" data-to="vector"><path class="hit" d="M 744 437 L 744 686 A 9 9 0 0 0 753 695 L 871 695 A 9 9 0 0 1 880 704 L 880 769"/><path class="wire" d="M 744 437 L 744 686 A 9 9 0 0 0 753 695 L 871 695 A 9 9 0 0 1 880 704 L 880 769" stroke="var(--e-store)" marker-end="url(#ah-store)" marker-start="url(#ah-store-r)"/><text class="edge-label" x="812" y="687" text-anchor="middle">embeddings · search</text></g><g class="edge cat-store " data-from="engine" data-to="audit"><path class="hit" d="M 784 412 L 787 412 A 9 9 0 0 1 796 421 L 796 735 A 9 9 0 0 0 805 744 L 872 744 A 8 8 0 0 1 888 744 L 1101 744 A 9 9 0 0 1 1110 753 L 1110 769"/><path class="wire" d="M 784 412 L 787 412 A 9 9 0 0 1 796 421 L 796 735 A 9 9 0 0 0 805 744 L 872 744 A 8 8 0 0 1 888 744 L 1101 744 A 9 9 0 0 1 1110 753 L 1110 769" stroke="var(--e-store)" marker-end="url(#ah-store)"/><text class="edge-label" x="1000" y="738" text-anchor="middle">audit events — all components</text></g><circle class="junction" cx="1500" cy="150" r="3.5" fill="var(--e-out)"/><circle class="junction" cx="1500" cy="264" r="3.5" fill="var(--e-out)"/><circle class="junction" cx="1500" cy="378" r="3.5" fill="var(--e-out)"/><circle class="junction" cx="1500" cy="492" r="3.5" fill="var(--e-out)"/><g class="node kind-actor" data-node="user"><rect class="node-body" x="52.0" y="184.0" width="140" height="52" rx="26.0"/><text class="node-label" x="122" y="209">User</text><text class="node-sub" x="122" y="227">browser</text></g><g class="node kind-platform" data-node="temporal"><rect class="node-body" x="44.0" y="457.0" width="156" height="54" rx="10"/><text class="node-label" x="122" y="483">Temporal</text><text class="node-sub" x="122" y="501">Cloud · self-hosted</text></g><g class="node kind-platform" data-node="llm"><rect class="node-body" x="44.0" y="602.0" width="156" height="54" rx="10"/><text class="node-label" x="122" y="628">LLM providers</text><text class="node-sub" x="122" y="646">Anthropic · OpenAI</text></g><g class="node kind-process" data-node="api"><rect class="node-body" x="336.0" y="183.0" width="168" height="54" rx="10"/><text class="node-label" x="420" y="209">API / Web UI</text><text class="node-sub" x="420" y="227">web app · public API · MCP</text></g><g class="node kind-process" data-node="engine"><rect class="node-body" x="616.0" y="383.0" width="168" height="54" rx="10"/><text class="node-label" x="700" y="409">Workflow engine</text><text class="node-sub" x="700" y="427">workflows · agents · workers</text></g><g class="node kind-process" data-node="proxy"><rect class="node-body" x="876.0" y="183.0" width="168" height="54" rx="10"/><text class="node-label" x="960" y="209">Credential Proxy</text><text class="node-sub" x="960" y="227">credential-injecting proxy</text></g><g class="node kind-process" data-node="webhooks"><rect class="node-body" x="876.0" y="543.0" width="168" height="54" rx="10"/><text class="node-label" x="960" y="569">Webhooks</text><text class="node-sub" x="960" y="587">signature-verified receiver</text></g><g class="node kind-store" data-node="postgres"><path class="store-body" d="M 355.0 778.0 A 75.0 9 0 0 1 505.0 778.0 L 505.0 822.0 A 75.0 9 0 0 1 355.0 822.0 Z"/><path class="store-lid" d="M 355.0 778.0 A 75.0 9 0 0 0 505.0 778.0"/><text class="node-label" x="430" y="802">Postgres</text><text class="node-sub" x="430" y="819">app data · credentials</text></g><g class="node kind-store" data-node="s3"><path class="store-body" d="M 615.0 778.0 A 65.0 9 0 0 1 745.0 778.0 L 745.0 822.0 A 65.0 9 0 0 1 615.0 822.0 Z"/><path class="store-lid" d="M 615.0 778.0 A 65.0 9 0 0 0 745.0 778.0"/><text class="node-label" x="680" y="802">S3</text><text class="node-sub" x="680" y="819">attachments</text></g><g class="node kind-store" data-node="vector"><path class="store-body" d="M 835.0 778.0 A 65.0 9 0 0 1 965.0 778.0 L 965.0 822.0 A 65.0 9 0 0 1 835.0 822.0 Z"/><path class="store-lid" d="M 835.0 778.0 A 65.0 9 0 0 0 965.0 778.0"/><text class="node-label" x="900" y="802">Vector store</text><text class="node-sub" x="900" y="819">pgvector</text></g><g class="node kind-store" data-node="audit"><path class="store-body" d="M 1045.0 778.0 A 65.0 9 0 0 1 1175.0 778.0 L 1175.0 822.0 A 65.0 9 0 0 1 1045.0 822.0 Z"/><path class="store-lid" d="M 1045.0 778.0 A 65.0 9 0 0 0 1175.0 778.0"/><text class="node-label" x="1110" y="802">Audit log</text><text class="node-sub" x="1110" y="819">append-only</text></g><g class="node kind-external" data-node="idp"><rect class="node-body" x="1286.0" y="116.0" width="168" height="68" rx="10"/><text class="node-label" x="1370" y="140.0">Identity provider</text><text class="node-sub" x="1370" y="156.0">Okta · Entra ID</text><text class="node-cap" x="1370" y="172.0">SSO · directory sync</text></g><g class="node kind-external" data-node="apps"><rect class="node-body" x="1286.0" y="230.0" width="168" height="68" rx="10"/><text class="node-label" x="1370" y="254.0">Internal apps</text><text class="node-sub" x="1370" y="270.0">REST APIs</text><text class="node-cap" x="1370" y="286.0">API calls · event triggers</text></g><g class="node kind-external" data-node="tick"><rect class="node-body" x="1286.0" y="344.0" width="168" height="68" rx="10"/><text class="node-label" x="1370" y="368.0">Ticketing</text><text class="node-sub" x="1370" y="384.0">Jira · ServiceNow</text><text class="node-cap" x="1370" y="400.0">ticket sync · events</text></g><g class="node kind-external" data-node="hris"><rect class="node-body" x="1286.0" y="458.0" width="168" height="68" rx="10"/><text class="node-label" x="1370" y="482.0">HRIS</text><text class="node-sub" x="1370" y="498.0">Workday · BambooHR</text><text class="node-cap" x="1370" y="514.0">employee · PTO data</text></g><g class="node kind-external" data-node="slack"><rect class="node-body" x="1286.0" y="592.0" width="168" height="68" rx="10"/><text class="node-label" x="1370" y="616.0">Slack</text><text class="node-sub" x="1370" y="632.0">Enterprise Grid</text><text class="node-cap" x="1370" y="648.0">messages · events · commands</text></g><g class="node kind-external" data-node="email"><rect class="node-body" x="1286.0" y="702.0" width="168" height="68" rx="10"/><text class="node-label" x="1370" y="726.0">Email</text><text class="node-sub" x="1370" y="742.0">SES in · out</text><text class="node-cap" x="1370" y="758.0">mail ingest · outbound mail</text></g><g class="legend"><line x1="330" y1="892" x2="356" y2="892" stroke="var(--e-user)" stroke-width="2.5"/><path d="M 350 888.5 L 356 892 L 350 895.5" fill="none" stroke="var(--e-user)" stroke-width="1.6"/><text class="legend-label" x="363" y="896">User &amp; application traffic</text><line x1="556.4" y1="892" x2="582.4" y2="892" stroke="var(--e-out)" stroke-width="2.5"/><path d="M 576.4 888.5 L 582.4 892 L 576.4 895.5" fill="none" stroke="var(--e-out)" stroke-width="1.6"/><text class="legend-label" x="589.4" y="896">Outbound (Serval-initiated)</text><line x1="789.2" y1="892" x2="815.2" y2="892" stroke="var(--e-in)" stroke-width="2.5"/><path d="M 809.2 888.5 L 815.2 892 L 809.2 895.5" fill="none" stroke="var(--e-in)" stroke-width="1.6"/><text class="legend-label" x="822.2" y="896">Inbound events</text><line x1="938.8000000000001" y1="892" x2="964.8000000000001" y2="892" stroke="var(--e-ai)" stroke-width="2.5"/><path d="M 958.8000000000001 888.5 L 964.8000000000001 892 L 958.8000000000001 895.5" fill="none" stroke="var(--e-ai)" stroke-width="1.6"/><text class="legend-label" x="971.8000000000001" y="896">Execution &amp; AI platform</text><line x1="1146.0" y1="892" x2="1172.0" y2="892" stroke="var(--e-store)" stroke-width="2.5"/><path d="M 1166.0 888.5 L 1172.0 892 L 1166.0 895.5" fill="none" stroke="var(--e-store)" stroke-width="1.6"/><text class="legend-label" x="1179.0" y="896">Storage I/O</text></g><g class="titleblock"><text class="title" x="44" y="34">Serval — platform data flow</text><text class="subtitle" x="44" y="54">last revised 2026-09-02</text></g></svg>`
}} />;

## End-to-end flows between Serval and Customer applications

The diagram below highlights system boundaries, data transmission paths, and key security controls within Serval. Hover over a component to isolate its flows, or over a line to trace it.

Customer applications are provided as examples only — Serval can be connected with as many or as few external applications as needed.

<Frame caption="System boundaries, data transmission paths, and security controls within Serval">
  <DataFlowDiagram />
</Frame>

<a href="/images/security/serval-data-flow-diagram.svg" target="_blank" rel="noopener">View full screen ↗</a>

## Hybrid self-hosted deployments

In a [Hybrid Self-Hosted](/sections/self-hosting/hybrid) deployment the Credential Proxy runs on the Serval worker inside your own infrastructure. Integration credentials live in a Kubernetes Secret on that worker and never leave your network. The worker opens an outbound HTTPS connection to Serval Cloud; the workflow engine sends it signed integration requests, the worker injects the credential and makes the call, and the result flows back. Everything else — the web app, the workflow engine, inbound webhooks, and application storage — stays in Serval Cloud.

<Frame caption="Hybrid self-hosted: the Credential Proxy and integration credentials live on your worker">
  <HybridDataFlowDiagram />
</Frame>

<a href="/images/security/serval-data-flow-diagram-hybrid.svg" target="_blank" rel="noopener">View full screen ↗</a>
