Skip to main content
View Markdown ↗

Copy this page

Select and copy the Markdown below, then paste it into your LLM.

sinkhole-flows attestor

Attaches HTTP(S) flow data captured by a mitmproxy sidecar — request/response pairs and TLS ClientHello events — to a signed attestation collection, so policy can reason about exactly which hosts a scan or build talked to.

FieldValue
Namesinkhole-flows
Predicate typehttps://aflock.ai/attestations/sinkhole-flows/v0.1
Lifecyclepostproduct
Default binary?No — builder opt-in only

What it captures

The attestor reads JSON Lines from /flows/out.jsonl (a bind mount from the host sinkhole/flows directory written by a mitmproxy addon sidecar) and emits a predicate with these fields:

  • scanId — value of the PIPW_SCAN_ID env var; flows are filtered to this id so concurrent scans sharing one sidecar stay attributable.
  • packageName, packageVersion — from PIPW_PACKAGE_NAME / PIPW_PACKAGE_VERSION; used to emit a pip://NAME@VERSION subject byte-identical to pip-install's for subject-digest joins in Archivista.
  • summary — aggregate counters: totalFlows, uniqueHosts, uniqueSnis, schemeCounts, statusCounts, totalBytesOut, totalBytesIn, flowsPath, flowsFileSha256 (sha256 of the raw out.jsonl).
  • flows[] — each entry has scanId, timestamp, event ("http" or "tls_clienthello"), sni, alpnProtocols, method, scheme, host, port, path, httpVersion, requestHeaders, requestBody, responseStatus, responseReason, responseHeaders, responseBody.
  • Bodies use a FlowBody shape: encoding, length, truncated, text, b64, empty — text or base64 as the mitmproxy addon recorded it.

Subjects emitted: pip://NAME@VERSION (digest of NAME==VERSION), pipw-sinkhole-scan://<scanId>, and pipw-sinkhole-flows-file://<scanId> (digest = the recorded flowsFileSha256).

A missing /flows/out.jsonl is not an error — the attestor returns an empty-but-valid statement so it can run in both sinkhole-enabled and sinkhole-disabled container configs.

When to use

Designed for the pipw_sinkhole workflow: a pip-witness scan container with all egress routed through a mitmproxy sidecar. Use it whenever you want a signed, queryable record of every HTTP(S) host a scan or build contacted, paired with a no-egress-except-via-proxy network policy for the strongest "this build only talked to expected hosts" signal.

Flags

None. Behavior is driven by environment variables: PIPW_SCAN_ID, PIPW_PACKAGE_NAME, PIPW_PACKAGE_VERSION.

Output shape

{
  "scanId": "scan-abc123",
  "packageName": "requests",
  "packageVersion": "2.31.0",
  "summary": {
    "scanId": "scan-abc123",
    "totalFlows": 4,
    "uniqueHosts": ["pypi.org", "files.pythonhosted.org"],
    "uniqueSnis": ["pypi.org", "files.pythonhosted.org"],
    "schemeCounts": {"https": 4},
    "statusCounts": {"200": 4},
    "totalBytesOut": 1820,
    "totalBytesIn": 248113,
    "flowsPath": "/flows/out.jsonl",
    "flowsFileSha256": "9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08"
  },
  "flows": [
    {
      "scanId": "scan-abc123",
      "timestamp": "2026-05-21T17:04:11Z",
      "event": "tls_clienthello",
      "sni": "pypi.org",
      "alpnProtocols": ["h2", "http/1.1"]
    },
    {
      "scanId": "scan-abc123",
      "timestamp": "2026-05-21T17:04:11Z",
      "event": "http",
      "method": "GET",
      "scheme": "https",
      "host": "pypi.org",
      "port": 443,
      "path": "/simple/requests/",
      "httpVersion": "HTTP/2.0",
      "responseStatus": 200
    }
  ]
}

Gotchas

  • Requires a cooperating mitmproxy sidecar addon writing JSONL to /flows/out.jsonl. Without that sidecar (and the pipw_sinkhole Docker network routing traffic through it), the attestation will simply be empty.
  • The scanner buffer is sized to 32 MB per line so large captured bodies (the addon caps body size at 4 MB but base64 + JSON envelope can push a single entry past 8 MB on disk) don't trip a bufio.Scanner "token too long" error.
  • Lines that fail to parse, or whose scan_id does not match the current PIPW_SCAN_ID, are silently skipped — concurrent scans sharing one sidecar do not cross-contaminate.
  • The pip://NAME@VERSION subject is intentionally byte-identical to the one pip-install emits (digest of "NAME==VERSION"). Changing either side breaks the Archivista subject-digest join.

CLI example

See the constraint summary + reproduction recipe at https://github.com/aflock-ai/attestor-compliance-examples/tree/main/40-sinkhole-flows. This attestor is currently blocked or doc-only — the linked example explains why and shows the recipe to validate once the constraint is removed.

See also

Reference generated from the product documentation. Match commands and support details to your installed release.