← Examples

PII data-flow diagram with audit history

Generate and maintain data-flow diagrams that show where sensitive data moves, what each system stores, and how the architecture changed between audit periods. Revision history serves as audit evidence.

architecture Compliance inventory / data catalog / codebase classification classifygenerateupdate

What this example shows

A KYC / customer-onboarding pipeline rendered as a data-flow architecture diagram, with sensitive-data edges visually distinct from regular flows. The diagram captures where personally identifiable information enters the system, every system that stores or processes it, and where it crosses a trust boundary into a third-party service. Each revision of the scene becomes an audit-evidence snapshot - “here is what the architecture looked like on the day of the audit.”

When to use it

Reach for this when you need GDPR / CCPA / SOC 2 evidence, when a vendor is reviewing your data flows, when a security team is assessing PII exposure, or when a customer’s procurement team asks for a diagram of where their data lives. The diagram is generated from a classified inventory rather than hand-drawn - agents are good at this kind of comprehensive sweep, humans are good at reviewing the result.

What the agent does

The agent ingests a data classification source (compliance inventory, data catalog, or codebase scan tagged with PII annotations), then applies typed operations to the persisted scene: createNode for each system that touches PII, createEdge with sensitive-data styling for each flow, frame containers for trust boundaries (internal / customer-facing / third-party). Every change creates an immutable revision; the agent stores dsp_list_revisions output as part of the audit packet.

When the architecture changes - a vendor swap, a new processor, a deprecated cache - the agent applies the delta and the visual diff highlights exactly what’s different from the prior audit period. The revision history IS the audit trail.

What the output includes

  • Frame-grouped diagram with explicit trust boundaries (customer device, application backend, third-party processors).
  • Sensitive-data edges visually styled distinctly from regular flows.
  • A complete revision history accessible via dsp_list_revisions with timestamps and per-revision messages - each revision documents what changed and why.
  • Visual diff between any two revisions for “what changed since the last audit?” reviews.
  • Persistent stable IDs so a system that’s been in the architecture for years has the same elementId across all audit periods, simplifying cross-period comparison.

Rendered diagram

PII data-flow diagram with audit history — rendered diagram

Built by Zindex from the canonical scene below. Open in Playground to swap themes (clean / dark / blueprint / sketch), or POST the scene to /v1/scenes/render with format: "png" for a rasterised version.

Scene JSON

Raw

The canonical DSP scene used to render the diagram above. Drop into the Playground or POST to /v1/scenes/render to reproduce.

{
  "schemaVersion": "0.1",
  "scene": {
    "id": "compliance-pii-flow",
    "title": "KYC onboarding - PII data flow with trust boundary",
    "units": "px",
    "canvas": {
      "width": 1200,
      "height": 820,
      "background": "#1a1a22"
    }
  },
  "diagramFamily": "architecture",
  "layoutStrategy": {
    "algorithm": "hierarchical",
    "direction": "LR",
    "nodeSpacing": 40,
    "rankSpacing": 120
  },
  "palette": {
    "background": "#1a1a22",
    "foreground": "#ece8dc",
    "nodeFill": "#222230",
    "nodeStroke": "#d4a843",
    "edgeStroke": "#96918a",
    "mutedForeground": "#96918a",
    "containerFill": "#1f1f28",
    "containerStroke": "#c0544e",
    "accentPrimary": "#c0544e",
    "accentSecondary": "#4db89a"
  },
  "styles": {
    "safe": {
      "fill": "#222230",
      "stroke": "#4db89a",
      "strokeWidth": 2
    },
    "unsafe": {
      "fill": "#222230",
      "stroke": "#c0544e",
      "strokeWidth": 2,
      "textColor": "#c0544e",
      "dash": [
        6,
        4
      ]
    },
    "internal": {
      "fill": "#222230",
      "stroke": "#d4a843",
      "strokeWidth": 1.5
    },
    "vendorNode": {
      "fill": "#222230",
      "stroke": "#c0544e",
      "strokeWidth": 2,
      "textColor": "#ece8dc"
    },
    "safeStorage": {
      "fill": "#222230",
      "stroke": "#4db89a",
      "strokeWidth": 2,
      "textColor": "#ece8dc"
    },
    "boundary": {
      "fill": "#1f1f28",
      "stroke": "#c0544e",
      "strokeWidth": 1,
      "dash": [
        4,
        4
      ],
      "textColor": "#ece8dc"
    },
    "plainEdge": {
      "stroke": "#96918a",
      "strokeWidth": 1.5,
      "textColor": "#ece8dc"
    }
  },
  "elements": [
    {
      "id": "customer",
      "kind": "node",
      "nodeType": "actor",
      "shape": "roundedRect",
      "label": "Customer",
      "icon": "lucide:user",
      "style": "internal"
    },
    {
      "id": "webapp",
      "kind": "node",
      "nodeType": "service",
      "shape": "roundedRect",
      "label": "Onboarding\nWeb App",
      "icon": "lucide:globe",
      "style": "internal"
    },
    {
      "id": "orch",
      "kind": "node",
      "nodeType": "service",
      "shape": "roundedRect",
      "label": "KYC\nOrchestrator",
      "icon": "lucide:shield",
      "style": "internal"
    },
    {
      "id": "vendor",
      "kind": "node",
      "nodeType": "service",
      "shape": "cloud",
      "label": "KYC Vendor\n3rd party (DPA)",
      "icon": "lucide:cloud",
      "style": "vendorNode"
    },
    {
      "id": "trust-frame",
      "kind": "frame",
      "children": [
        "vendor"
      ],
      "title": "External trust boundary",
      "containerType": "boundary",
      "style": "boundary"
    },
    {
      "id": "iddocs",
      "kind": "node",
      "nodeType": "database",
      "shape": "cylinder",
      "label": "ID Document Store\nS3 + KMS",
      "icon": "lucide:file-lock",
      "style": "internal"
    },
    {
      "id": "vault",
      "kind": "node",
      "nodeType": "database",
      "shape": "cylinder",
      "label": "Customer Vault\ntokenized PII",
      "icon": "lucide:key",
      "style": "safeStorage"
    },
    {
      "id": "audit",
      "kind": "node",
      "nodeType": "database",
      "shape": "cylinder",
      "label": "Audit Log\nhashed events (immutable)",
      "icon": "lucide:scroll-text",
      "style": "safeStorage"
    },
    {
      "id": "e1",
      "kind": "edge",
      "from": {
        "elementId": "customer"
      },
      "to": {
        "elementId": "webapp"
      },
      "router": "orthogonal",
      "label": "submit KYC",
      "style": "plainEdge"
    },
    {
      "id": "e2",
      "kind": "edge",
      "from": {
        "elementId": "webapp"
      },
      "to": {
        "elementId": "orch"
      },
      "router": "orthogonal",
      "label": "full PII",
      "style": "plainEdge"
    },
    {
      "id": "e3",
      "kind": "edge",
      "from": {
        "elementId": "orch"
      },
      "to": {
        "elementId": "vendor"
      },
      "router": "orthogonal",
      "label": "full PII (DPA)",
      "style": "unsafe"
    },
    {
      "id": "e4",
      "kind": "edge",
      "from": {
        "elementId": "orch"
      },
      "to": {
        "elementId": "iddocs"
      },
      "router": "orthogonal",
      "label": "ID photo",
      "style": "unsafe"
    },
    {
      "id": "e5",
      "kind": "edge",
      "from": {
        "elementId": "orch"
      },
      "to": {
        "elementId": "vault"
      },
      "router": "orthogonal",
      "label": "tokenized",
      "style": "safe"
    },
    {
      "id": "e6",
      "kind": "edge",
      "from": {
        "elementId": "orch"
      },
      "to": {
        "elementId": "audit"
      },
      "router": "orthogonal",
      "label": "hashed events",
      "style": "safe"
    }
  ],
  "constraints": [
    {
      "type": "order",
      "source": "vendor",
      "target": "iddocs",
      "relation": "above"
    },
    {
      "type": "order",
      "source": "iddocs",
      "target": "vault",
      "relation": "above"
    },
    {
      "type": "order",
      "source": "vault",
      "target": "audit",
      "relation": "above"
    }
  ]
}

Agent workflow

Maintain a regulator-friendly data-flow diagram showing where PII / KYC / financial data moves between systems, what each system stores, and how the topology has evolved over time, by combining a structured data-classification inventory with a Zindex scene that carries an immutable audit trail.

Inputs

  • Data-classification inventory (typically a YAML/JSON file in compliance/ describing which fields each system stores and their classification)
  • Trust-boundary configuration (which systems sit inside vs outside the company perimeter)
  • Existing Zindex scene id (one per audited workflow - e.g. one for KYC, one for billing)
  • Zindex API key with scene-write scope

Outputs

  • Updated persisted scene with one node per system, edges labelled by what data crosses (e.g. 'full PII (DPA)'), and a frame around any external trust boundary
  • Rendered SVG suitable for inclusion in a SOC 2 / ISO 27001 audit pack
  • Revision diff showing what changed between this audit cycle and the previous one (which is the auditor-facing answer to 'what changed since last quarter?')
  • Per-revision watermark trail proving the diagram pinned to a specific date
  1. 01

    Fetch the persisted compliance scene

    Compliance scenes are long-lived - one scene per audited workflow, with ten or twenty revisions stretching back over the audit history. Fetch the current revision before changing anything.

    dsp_get_scene GET /v1/scenes/${SCENE_ID}
  2. 02

    Read the data-classification inventory

    Parse compliance/data-classification.yaml. Each entry maps a system to the data it stores and its classification (`public`, `internal`, `pii`, `kyc`, `financial`). Edges between systems are derived from which classification crosses each boundary.

  3. 03

    Compute the diff against the persisted scene

    For each system in the classification, ensure a node exists with the expected nodeType (`actor`, `service`, `database`, `externalSystem`). For each data flow, ensure an edge exists with the expected label (e.g. 'full PII (DPA)' or 'tokenized'). Style PII-bearing edges with a red stroke; tokenized/hashed edges with green; internal-only edges in default. Frame any external-vendor systems inside a dashed trust-boundary frame so the auditor can see what data crosses the company perimeter.

  4. 04

    Apply the operation batch

    Send the diff as one applyOps batch. Use a `revisionMessage` that reads like an audit-log entry - 'Q3 2026: add tokenization layer between webapp and customer vault'. The revision message will surface in `dsp_diff_scene` output and ends up in the audit trail.

    dsp_apply_ops POST /v1/scenes/${SCENE_ID}/ops
  5. 05

    Validate the new revision

    Confirm the topology is valid. Pay attention to `LABEL_DUPLICATION_DETECTED` (two edges with the same label between different systems can confuse an auditor) and resolve before publishing.

    dsp_validate_scene POST /v1/scenes/validate
  6. 06

    Render the data-flow diagram

    Render to SVG. The watermark stamps scene-id + revision + date so the auditor always knows which version of the diagram they are looking at, with no separate metadata sidecar.

    dsp_render_scene POST /v1/scenes/${SCENE_ID}/render
  7. 07

    Generate the audit-cycle diff

    On audit cycles (typically quarterly), call `dsp_diff_scene` with `from=` the revision pinned at the start of the audit period and `to=` the current revision. The output is the canonical answer to 'what changed since last audit?' - far better evidence than a stack of dated screenshots.

    dsp_diff_scene GET /v1/scenes/${SCENE_ID}/diff?from=${AUDIT_START_REVISION}&to=${NEW_REVISION}
  8. 08

    Publish the SVG + diff to the audit pack

    Drop the rendered SVG and the diff JSON into the audit-pack directory. Both are traceable back to the persisted scene by id + revision; auditors who want to verify the diagram against current systems can replay the workflow at the recorded revision number.

MCP recipe

For agents using Model Context Protocol. The tool sequence below matches the workflow steps; copy the prompt as a system message.

Tool sequence

  1. 01 dsp_get_scene Fetch the persisted compliance scene
  2. 02 dsp_apply_ops Apply the operation batch
  3. 03 dsp_validate_scene Validate the new revision
  4. 04 dsp_render_scene Render the data-flow diagram
  5. 05 dsp_diff_scene Generate the audit-cycle diff

Unique tools used: dsp_get_scene, dsp_apply_ops, dsp_validate_scene, dsp_render_scene, dsp_diff_scene.

Copyable agent prompt

Drop this verbatim into a system prompt for an MCP-connected agent. The Zindex MCP server (@zindex-ai/mcp, configured with a ZINDEX_API_KEY environment variable - setup guide) exposes the tools the prompt references.

You are an automated compliance-documentation agent. Your job is to keep a regulator-friendly data-flow diagram in sync with the actual systems and data classifications, so that SOC 2 / ISO 27001 / GDPR / KYC audits can rely on the diagram as primary evidence rather than dated screenshots.

The persisted Zindex scene id is `${SCENE_ID}`; it already exists. Each audited workflow has its own scene - one for KYC, one for billing, one for any vendor-share boundary you need to document. Treat each scene as the canonical, mutable, immutable-revisioned record of how data flowed through the system at every point in time.

Workflow on every run (typically scheduled monthly, plus on every PR that touches `compliance/data-classification.yaml`):

1. Read `compliance/data-classification.yaml` (or the equivalent inventory). Each entry maps a system to (a) the data it stores, (b) the data classification (`public`, `internal`, `pii`, `kyc`, `financial`), and (c) whether the system is internal or an external vendor.

2. Call `dsp_get_scene({ sceneId: "${SCENE_ID}" })` to read the current revision and elements. Do not modify anything yet - this run might be a no-op if classifications haven't changed.

3. Diff the parsed classification against the persisted scene. For each system, ensure a node exists with the right nodeType (`actor`, `service`, `database`, `externalSystem`) and label. For each data-flow edge, ensure the edge exists with a label that names what data crosses (e.g. 'full PII (DPA)', 'tokenized', 'hashed events'). Apply edge styles consistently: red stroke for unprotected PII, green for tokenized/hashed, default for internal traffic - auditors and engineers should be able to read the protection posture at a glance. If any external vendors are touched, wrap them in a dashed trust-boundary frame so the perimeter is explicit.

4. Call `dsp_apply_ops` with one batch. `errorPolicy: "allOrNothing"`. The `revisionMessage` should read like an audit-log entry: 'Q3 2026: add tokenization layer between webapp and customer vault' or '2026-04-15: add SendGrid as PII recipient under DPA'. Revision messages surface in `dsp_diff_scene`; they ARE the audit trail.

5. Call `dsp_validate_scene`. Resolve any `LABEL_DUPLICATION_DETECTED` (two edges with the same label between different systems is genuinely confusing in an audit context - rename or anchor to a column to disambiguate). `EDGE_LABEL_SUPPRESSED_REDUNDANT` should not appear on this diagram family; if it does, an edge label is matching a column in an ER diagram and you've used the wrong scene.

6. Call `dsp_render_scene({ format: "svg", theme: "clean" })`. The rendered SVG carries a watermark with scene-id + revision + date - leave the watermark on; auditors rely on it.

7. On audit-cycle runs (quarterly is typical), call `dsp_diff_scene({ from: ${AUDIT_START_REVISION}, to: NEW_REVISION })`. The output lists added / removed / changed elements over the audit period - this is the canonical answer to 'what changed since last audit?' and is far stronger evidence than a stack of dated screenshots. Drop both the SVG and the diff JSON into the audit-pack directory.

Hard rules: never hand-edit the rendered SVG (the watermark and scene-id traceability are the whole point). Never delete a revision or rewrite history - the immutability is what makes the audit trail credible. Never commit a scene that fails validation; auditors who spot-check the diagram against live systems must always be able to re-validate it. Treat the data-classification inventory as the source of truth; if a system is not classified, it does not belong in the diagram until it is.

HTTP API recipe

For agents/devs not using MCP. Set $ZINDEX_API_KEY in the Authorization header on authenticated calls. Stateless endpoints (/v1/scenes/render, /v1/scenes/validate, /v1/scenes/normalize) need no key. Full reference: API endpoints, OpenAPI spec.

  1. 01 GET /v1/scenes/${SCENE_ID}

    Compliance scenes are long-lived - one scene per audited workflow, with ten or twenty revisions stretching back over the audit history. Fetch the current revision before changing anything.

  2. 02 POST /v1/scenes/${SCENE_ID}/ops

    Send the diff as one applyOps batch. Use a `revisionMessage` that reads like an audit-log entry - 'Q3 2026: add tokenization layer between webapp and customer vault'. The revision message will surface in `dsp_diff_scene` output and ends up in the audit trail.

  3. 03 POST /v1/scenes/validate

    Confirm the topology is valid. Pay attention to `LABEL_DUPLICATION_DETECTED` (two edges with the same label between different systems can confuse an auditor) and resolve before publishing.

  4. 04 POST /v1/scenes/${SCENE_ID}/render

    Render to SVG. The watermark stamps scene-id + revision + date so the auditor always knows which version of the diagram they are looking at, with no separate metadata sidecar.

    Example request body

    { "format": "svg", "theme": "clean" }
  5. 05 GET /v1/scenes/${SCENE_ID}/diff?from=${AUDIT_START_REVISION}&to=${NEW_REVISION}

    On audit cycles (typically quarterly), call `dsp_diff_scene` with `from=` the revision pinned at the start of the audit period and `to=` the current revision. The output is the canonical answer to 'what changed since last audit?' - far better evidence than a stack of dated screenshots.

Operations

Raw

The typed-operation envelope that builds this scene from empty. POST to /v1/scenes/:id/ops after creating a scene, or pass to dsp_apply_ops. Each op carries a stable id so subsequent runs can update the same elements instead of regenerating.

  • 14 operations
  • 6 createEdge, 1 createFrame, 7 createNode
{
  "schemaVersion": "0.1",
  "errorPolicy": "allOrNothing",
  "revisionMessage": "Initial PII data-flow diagram with vendor trust boundary",
  "ops": [
    {
      "op": "createFrame",
      "id": "trust-frame",
      "title": "External trust boundary",
      "containerType": "boundary",
      "children": [
        "vendor"
      ],
      "style": "boundary"
    },
    {
      "op": "createNode",
      "id": "customer",
      "nodeType": "actor",
      "shape": "roundedRect",
      "label": "Customer",
      "icon": "lucide:user",
      "style": "internal"
    },
    {
      "op": "createNode",
      "id": "webapp",
      "nodeType": "service",
      "shape": "roundedRect",
      "label": "Onboarding\nWeb App",
      "icon": "lucide:globe",
      "style": "internal"
    },
    {
      "op": "createNode",
      "id": "orch",
      "nodeType": "service",
      "shape": "roundedRect",
      "label": "KYC\nOrchestrator",
      "icon": "lucide:shield",
      "style": "internal"
    },
    {
      "op": "createNode",
      "id": "vendor",
      "nodeType": "service",
      "shape": "cloud",
      "label": "KYC Vendor\n3rd party (DPA)",
      "icon": "lucide:cloud",
      "style": "vendorNode"
    },
    {
      "op": "createNode",
      "id": "iddocs",
      "nodeType": "database",
      "shape": "cylinder",
      "label": "ID Document Store\nS3 + KMS",
      "icon": "lucide:file-lock",
      "style": "internal"
    },
    {
      "op": "createNode",
      "id": "vault",
      "nodeType": "database",
      "shape": "cylinder",
      "label": "Customer Vault\ntokenized PII",
      "icon": "lucide:key",
      "style": "safeStorage"
    },
    {
      "op": "createNode",
      "id": "audit",
      "nodeType": "database",
      "shape": "cylinder",
      "label": "Audit Log\nhashed events (immutable)",
      "icon": "lucide:scroll-text",
      "style": "safeStorage"
    },
    {
      "op": "createEdge",
      "id": "e1",
      "from": {
        "elementId": "customer"
      },
      "to": {
        "elementId": "webapp"
      },
      "label": "submit KYC",
      "router": "orthogonal",
      "style": "plainEdge"
    },
    {
      "op": "createEdge",
      "id": "e2",
      "from": {
        "elementId": "webapp"
      },
      "to": {
        "elementId": "orch"
      },
      "label": "full PII",
      "router": "orthogonal",
      "style": "plainEdge"
    },
    {
      "op": "createEdge",
      "id": "e3",
      "from": {
        "elementId": "orch"
      },
      "to": {
        "elementId": "vendor"
      },
      "label": "full PII (DPA)",
      "router": "orthogonal",
      "style": "unsafe"
    },
    {
      "op": "createEdge",
      "id": "e4",
      "from": {
        "elementId": "orch"
      },
      "to": {
        "elementId": "iddocs"
      },
      "label": "ID photo",
      "router": "orthogonal",
      "style": "unsafe"
    },
    {
      "op": "createEdge",
      "id": "e5",
      "from": {
        "elementId": "orch"
      },
      "to": {
        "elementId": "vault"
      },
      "label": "tokenized",
      "router": "orthogonal",
      "style": "safe"
    },
    {
      "op": "createEdge",
      "id": "e6",
      "from": {
        "elementId": "orch"
      },
      "to": {
        "elementId": "audit"
      },
      "label": "hashed events",
      "router": "orthogonal",
      "style": "safe"
    }
  ]
}

Validation

Valid

Captured response from POST /v1/scenes/validate. The platform runs 40+ semantic checks; see the full list in the validation rules reference.

  • 0 diagnostics

Scene validates with no diagnostics. Agents that produce scenes like this can ship the rendered SVG without a recovery loop.

Codes the platform can emit include TEXT_OVERFLOW, CANVAS_AUTO_EXTENDED, EDGE_LABEL_SUPPRESSED_REDUNDANT, EDGE_LABEL_SUPPRESSED_FANIN, EDGE_COLUMN_NOT_FOUND, LABEL_DUPLICATION_DETECTED, LAYOUT_ABSOLUTE_AT_ORIGIN, and MISSING_DIAGRAM_FAMILY. Each diagnostic carries a structured data field with element ids and context an agent can act on programmatically.

Revision diff

Raw

The structural diff between two revisions of the persisted scene — the response shape dsp_diff_scene returns. Stateful diagram evolution is Zindex's strongest differentiator: the same scene id evolves through immutable revisions, each diffable.

Evolution scenario

Q3 2026 audit cycle: a new tokenization service was inserted between the onboarding webapp and the customer vault. The PII edge that previously carried full data (`e2`) now carries only tokenised references (`e5`). Both edges' labels and stroke styles updated accordingly. This diff is the regulator-facing answer to 'what changed since last audit?'.

  • Revision 78
  • +1 added
  • -0 removed
  • ~2 modified

+ Added

  • tokenizer

~ Modified

  • e2
  • e5

Raw dsp_diff_scene response

{
  "schemaVersion": "1.0",
  "sceneId": "kyc-data-flow",
  "fromRevision": 7,
  "toRevision": 8,
  "summary": {
    "added": 1,
    "removed": 0,
    "modified": 2
  },
  "added": [
    "tokenizer"
  ],
  "removed": [],
  "modified": [
    "e2",
    "e5"
  ],
  "scenario": "Q3 2026 audit cycle: a new tokenization service was inserted between the onboarding webapp and the customer vault. The PII edge that previously carried full data (`e2`) now carries only tokenised references (`e5`). Both edges' labels and stroke styles updated accordingly. This diff is the regulator-facing answer to 'what changed since last audit?'."
}

Want to see this on your own scene? Run the CI recipe below — it calls dsp_diff_scene on every revision change and surfaces a real before / after on every PR.

CI/CD recipe

Pull request + scheduled Raw YAML

A complete, runnable GitHub Actions workflow for this example. Drop the YAML into .github/workflows/zindex-compliance-pii-flow.yml, add the listed secrets, and the agent runs unattended on every qualifying trigger. Re-comments idempotently using a hidden marker so the PR conversation stays clean across pushes.

Trigger

Runs on every PR that touches the data-classification inventory or the trust-boundary configuration, plus a monthly scheduled run on the 1st at midnight UTC. The monthly cadence aligns with most compliance audit cycles; the PR trigger catches classification changes the moment they're proposed.

  • Schedule: 0 0 1 * *
  • Path filters: compliance/data-classification.yaml, compliance/trust-boundaries.yaml

Required secrets

  • ZINDEX_API_KEY required Zindex API key with scene-write scope.
  • ZINDEX_SCENE_ID required Long-lived persisted scene id for this audited workflow (one per workflow - KYC, billing, vendor share). Never recreate.
  • AUDIT_BASELINE_REVISION required Revision number pinned at the start of the current audit cycle. Update each new audit period. The diff against this revision is the 'what changed since last audit?' evidence.

Inputs

  • compliance/data-classification.yaml - system → data classification map (public / internal / pii / kyc / financial)
  • scripts/parse-classification.mjs (you author this)
  • scripts/classification-to-ops.mjs (you author this) - maps classification deltas to typed ops, applies edge styling per classification (red for unprotected PII, green for tokenized/hashed)

Outputs

  • out/diagram.svg - rendered data-flow diagram with watermark stamping scene-id + revision + date
  • out/audit-diff.json - structural diff vs audit baseline (the regulator-facing evidence)
  • Workflow artifact 'compliance-audit-pack' with 90-day retention
  • Optional PR comment summarising classification changes

GitHub Actions workflow

# Zindex - Compliance / PII data-flow diagram. Maintains a regulator-friendly
# diagram of where sensitive data moves between systems. Runs monthly on the
# 1st (audit-cycle cadence) and on every PR that touches the data-classification
# inventory. Designed so the rendered SVG + revision diff become the canonical
# audit-pack evidence.
#
# Drop into .github/workflows/zindex-compliance.yml.

name: Zindex - Compliance PII data-flow

on:
  pull_request:
    paths:
      - "compliance/data-classification.yaml"
      - "compliance/trust-boundaries.yaml"
  schedule:
    - cron: "0 0 1 * *"   # Monthly: 1st of each month, 00:00 UTC
  workflow_dispatch:

permissions:
  contents: read
  pull-requests: write

concurrency:
  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
  cancel-in-progress: ${{ github.event_name == 'pull_request' }}

env:
  ZINDEX_API_BASE: https://api.zindex.ai

jobs:
  sync-data-flow:
    runs-on: ubuntu-latest
    environment: compliance   # gate: requires reviewer approval if configured
    steps:
      - uses: actions/checkout@v4

      # 1. Read the data-classification inventory + trust-boundary config.
      #    Author scripts/parse-classification.mjs against your compliance
      #    docs convention (typically one YAML file mapping system →
      #    classification + a list of external trust boundaries).
      - name: Parse classification inventory
        run: |
          mkdir -p out
          node scripts/parse-classification.mjs > out/classification.json

      - name: Capture current revision
        id: prev_rev
        env:
          ZINDEX_API_KEY: ${{ secrets.ZINDEX_API_KEY }}
          ZINDEX_SCENE_ID: ${{ secrets.ZINDEX_SCENE_ID }}
        run: |
          echo "rev=$(curl -fsSL -H "Authorization: Bearer $ZINDEX_API_KEY" \
            "$ZINDEX_API_BASE/v1/scenes/$ZINDEX_SCENE_ID" | jq -r '.revision')" >> "$GITHUB_OUTPUT"

      # 2. Compute applyOps batch. Audit-friendly tip: the revisionMessage
      #    becomes the audit log entry - make it descriptive
      #    ("Q3 2026: add tokenization layer between webapp and customer vault").
      - name: Compute applyOps batch
        run: node scripts/classification-to-ops.mjs out/classification.json > out/ops.json

      - name: Apply ops
        id: apply
        env:
          ZINDEX_API_KEY: ${{ secrets.ZINDEX_API_KEY }}
          ZINDEX_SCENE_ID: ${{ secrets.ZINDEX_SCENE_ID }}
        run: |
          RESP=$(curl -fsSL -X POST \
            -H "Authorization: Bearer $ZINDEX_API_KEY" \
            -H "Content-Type: application/json" \
            --data-binary @out/ops.json \
            "$ZINDEX_API_BASE/v1/scenes/$ZINDEX_SCENE_ID/ops")
          echo "rev=$(echo "$RESP" | jq -r '.revision')" >> "$GITHUB_OUTPUT"
          echo "applied=$(echo "$RESP" | jq -r '.applied')" >> "$GITHUB_OUTPUT"

      - name: Render
        env:
          ZINDEX_API_KEY: ${{ secrets.ZINDEX_API_KEY }}
          ZINDEX_SCENE_ID: ${{ secrets.ZINDEX_SCENE_ID }}
        run: |
          curl -fsSL -X POST \
            -H "Authorization: Bearer $ZINDEX_API_KEY" \
            -H "Content-Type: application/json" \
            -d '{"format":"svg","theme":"clean"}' \
            "$ZINDEX_API_BASE/v1/scenes/$ZINDEX_SCENE_ID/render" \
            | jq -r '.output.content' > out/diagram.svg

      # 3. Audit-cycle diff: compares current to the revision pinned at the
      #    start of the audit period (stored as the AUDIT_BASELINE_REVISION
      #    secret - update it each new audit cycle). This produces the
      #    "what changed since last audit?" answer.
      - name: Diff against audit baseline
        id: diff
        env:
          ZINDEX_API_KEY: ${{ secrets.ZINDEX_API_KEY }}
          ZINDEX_SCENE_ID: ${{ secrets.ZINDEX_SCENE_ID }}
          BASELINE: ${{ secrets.AUDIT_BASELINE_REVISION }}
          NEW: ${{ steps.apply.outputs.rev }}
        run: |
          curl -fsSL \
            -H "Authorization: Bearer $ZINDEX_API_KEY" \
            "$ZINDEX_API_BASE/v1/scenes/$ZINDEX_SCENE_ID/diff?from=$BASELINE&to=$NEW" \
            > out/audit-diff.json
          echo "added=$(jq -r '.summary.added' out/audit-diff.json)" >> "$GITHUB_OUTPUT"
          echo "removed=$(jq -r '.summary.removed' out/audit-diff.json)" >> "$GITHUB_OUTPUT"
          echo "modified=$(jq -r '.summary.modified' out/audit-diff.json)" >> "$GITHUB_OUTPUT"

      # 4. Both SVG and audit-diff get bundled into a single artifact - the
      #    audit-pack handoff. Long retention (90d) so the pack stays
      #    available for the full audit cycle.
      - uses: actions/upload-artifact@v4
        id: upload
        with:
          name: compliance-audit-pack
          path: |
            out/diagram.svg
            out/audit-diff.json
          retention-days: 90

      - uses: peter-evans/find-comment@v3
        if: github.event_name == 'pull_request'
        id: find_comment
        with:
          issue-number: ${{ github.event.pull_request.number }}
          comment-author: "github-actions[bot]"
          body-includes: "<!-- zindex-bot:compliance-pii-flow -->"

      - uses: peter-evans/create-or-update-comment@v4
        if: github.event_name == 'pull_request'
        with:
          comment-id: ${{ steps.find_comment.outputs.comment-id }}
          issue-number: ${{ github.event.pull_request.number }}
          edit-mode: replace
          body: |
            <!-- zindex-bot:compliance-pii-flow -->
            ### PII data-flow updated · revision ${{ steps.apply.outputs.rev }}

            | | |
            |---|---|
            | Changes since audit baseline | +${{ steps.diff.outputs.added }} / -${{ steps.diff.outputs.removed }} / ~${{ steps.diff.outputs.modified }} |
            | Revision | ${{ steps.prev_rev.outputs.rev }} → ${{ steps.apply.outputs.rev }} |
            | Baseline | revision `${{ secrets.AUDIT_BASELINE_REVISION }}` |

            <details><summary>Download audit pack (SVG + diff JSON)</summary>

            ${{ steps.upload.outputs.artifact-url }}

            </details>

            <sub>Rendered by Zindex · scene `${{ secrets.ZINDEX_SCENE_ID }}` · stamped with revision watermark</sub>

PR comment template

The bot posts this comment on every triggering PR. The hidden marker <!-- zindex-bot:compliance-pii-flow --> lets peter-evans/create-or-update-comment find and overwrite the previous comment instead of appending a new one.

<!-- zindex-bot:compliance-pii-flow -->
### PII data-flow updated · revision ${NEW_REVISION}

| | |
|---|---|
| Changes since audit baseline | +${ADDED} / -${REMOVED} / ~${MODIFIED} |
| Revision | ${PREV_REVISION} → ${NEW_REVISION} |
| Baseline | revision `${BASELINE_REVISION}` |

<details><summary>Download audit pack (SVG + diff JSON)</summary>

${ARTIFACT_URL}

</details>

<sub>Rendered by Zindex · scene `${SCENE_ID}` · stamped with revision watermark</sub>

Agent resources

Machine-readable versions of this example. Agents should fetch these rather than scrape the rendered HTML.

  • compliance-pii-flow.scene.json Canonical DSP scene Open
  • compliance-pii-flow.ops.json Typed-operation envelope that builds the scene Open
  • compliance-pii-flow.workflow.json Structured agent workflow (goal, inputs, outputs, steps) Open
  • compliance-pii-flow.diff.json Sample dsp_diff_scene response (revision evolution) Open
  • compliance-pii-flow.github-actions.yml Runnable GitHub Actions workflow for the CI/CD recipe Open
  • compliance-pii-flow.svg Build-time rendered diagram Open
  • compliance-pii-flow.md Agent-readable markdown summary Open
  • /examples/index.json Manifest of all examples (cross-linked) Open