AI Inventory / Network discovery

Catch the AI calls that never touched your SDK.

Passive discovery surfaces every integration that emits a receipt. Active registration surfaces every integration that someone declared. Network discovery surfaces the third class: services talking to api.openai.com, api.anthropic.com, generativelanguage.googleapis.com, and friends from infrastructure that nobody wired up to Prova. The classic shadow-AI gap.

How it works

Point your Cloudflare Logpush job or your Datadog log forwarder at POST /api/v1/inventory/network. Prova matches each outbound request against a registry of known model-provider hostnames and aggregates one inventory row per distinct (app, environment, provider, model) tuple. Rows discovered this way are flagged network in the dashboard.

Supported providers

The hostname registry covers the providers most commonly seen in enterprise traffic. For Azure OpenAI, AWS Bedrock, and Vertex AI, the model is extracted from the URL path. For others the path carries the API verb (chat/completions, messages, etc) not the model, so the model field shows as unknown until that integration also starts emitting receipts.

openai            api.openai.com
anthropic         api.anthropic.com
azure_openai      *.openai.azure.com           (model from /deployments/<name>)
aws_bedrock       bedrock-runtime.*.amazonaws.com  (model from /model/<id>)
google            generativelanguage.googleapis.com  (model from /models/<name>)
google_vertex     *.aiplatform.googleapis.com  (model from /models/<name>)
cohere            api.cohere.ai, api.cohere.com
mistral           api.mistral.ai
together          api.together.xyz, api.together.ai
fireworks         api.fireworks.ai
groq              api.groq.com
openrouter        openrouter.ai
replicate         api.replicate.com
huggingface       api-inference.huggingface.co
perplexity        api.perplexity.ai

Cloudflare Logpush

Create a Logpush job that targets HTTP requests with WorkerSubrequest = true and ships the standard HTTP-request fields. Point the destination at this endpoint with your API key in the Authorization header.

POST https://prova.cobound.dev/api/v1/inventory/network
Authorization: Bearer prv_<your_key>
Content-Type: application/json

{
  "format": "cloudflare_logpush",
  "logs": [
    {
      "ClientRequestHost": "api.openai.com",
      "ClientRequestURI": "/v1/chat/completions",
      "ClientRequestMethod": "POST",
      "EdgeStartTimestamp": 1714521600000000000,
      "WorkerSubrequest": true,
      "WorkerScriptName": "marketing-bot-worker"
    }
  ]
}

The Worker name becomes the integration's app_id, so each Worker shows up as its own inventory row.

Datadog logs

Use a Datadog Forwarding Rule (or a Logs Pipeline export) to send outbound HTTP logs to this endpoint. Prova reads service and env for app + environment, and the URL host + path for provider + model.

{
  "format": "datadog",
  "logs": [
    {
      "timestamp": 1714521600000,
      "service": "checkout-service",
      "env": "production",
      "attributes": {
        "http": {
          "method": "POST",
          "url_details": {
            "host": "api.anthropic.com",
            "path": "/v1/messages"
          }
        }
      }
    }
  ]
}

NDJSON / generic

Anything that can stream NDJSON works. Send Content-Type: application/x-ndjson and include ?format=generic on the URL. Each line is { host, path?, method?, app_id?, environment?, occurred_at? }.

What you get back

{
  "format": "cloudflare_logpush",
  "received_rows": 4823,
  "invalid_lines": 0,
  "skipped_rows": 4811,
  "matched_observations": 12,
  "integrations_upserted": 3,
  "integrations": [
    { "id": "a1b2c3d4...", "samples": 7, "hostnames": ["api.openai.com"] },
    { "id": "e5f6g7h8...", "samples": 3, "hostnames": ["api.anthropic.com"] },
    { "id": "i9j0k1l2...", "samples": 2, "hostnames": ["api.cohere.com"] }
  ]
}

Most rows in a real Logpush stream are not AI traffic; they will show up in skipped_rows. The batch is idempotent on the integration tuple, so re-shipping the same logs is harmless.

What network discovery is not

A network-discovered row is not a signed receipt. It does not prove a model was called, only that an outbound HTTP request hit a provider hostname. The signed audit trail stays at /api/v1/audit/ingest. The right mental model: network discovery tells the CISO where AI is being called from; the receipt stream tells the auditor what was decided.

See also: Inventory methodology · Audit Vault ingest