File Upload & Scanning Pipeline  ·  View 08 of 21  ·  Structure

Platform Components

Three subscriptions, because the scan plane's blast radius is a subscription boundary rather than a network rule.

Editable source SVG draw.io All views
Platform subscription — control Edge & API Front Door WAF API gateway APIM Control services — Container Apps Session service Credential minting user-delegation SAS Lifecycle state machine Policy resolver Reconciler Metadata & evidence Object metadata Cosmos DB Verdict store Cosmos DB Dedup index Cosmos DB Transition log Blob, immutable Scan subscription — isolated, no inbound Work intake Finalise events Event Grid Priority queues Service Bus ×3 Dispatcher size profile Ephemeral sandboxed workers Signature engine Container Apps job Deep engine Container Apps job Archive expander bounded depth 12 Verdict composer Poison dead-letter indeterminate Storage subscription Untrusted plane — deny by default Unscanned Quarantine Evidence immutable, 180 d Serving plane Available objects Entra ID Key Vault / HSM Signature feed Azure Monitor finalised verdict promote Platform Components — Three Subscriptions, Two Planes Interface / broker Application we own Security / platform Data store Queue / topic Risk / gap External / third party event / async synchronous The scan subscription has no inbound reachability and no credential that can change an object's state: it reads bytes and writes verdicts, and nothing else. v 1.0 · isolation scan plane in its own subscription

Decisions

  • Scan workers run in their own subscription with no inbound reachability, an egress allow-list of two destinations, and no credential able to change an object's lifecycle state
  • Every scan runs in an ephemeral job destroyed after one object, whatever the outcome — there is no shared mutable filesystem between two tenants' files
  • Poison objects dead-letter as indeterminate naming the bound that was hit; a decompression bomb is a policy outcome, never a crashed worker that blocks a lane

Realisation

  • Control services on Container Apps; metadata, verdicts and the dedup index on Cosmos DB; the transition log on immutable Blob with legal hold
  • Event Grid carries finalisation into the scan subscription; three Service Bus queues carry the priority lanes; Container Apps jobs are the workers
  • Two Blob storage accounts, not two containers: untrusted and serving are separate accounts with separate identity and network controls

Cost accepted

  • A separate untrusted account means promotion is a copy, priced at p99 object size — the trade argued in Question 3
  • Ephemeral per-object jobs cost more per scan than a warm pool, and remove the shared-state class of escape entirely
  • Scan compute is budgeted at ≤ 35% of total pipeline cost, with deduplication expected to avoid ≥ 25% of it