{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "$id": "https://artifacts.apis.io/honeylabs-net/json-schema/honeylabs-net-ip-report-schema.json",
  "title": "IpReport",
  "x-generated": "2026-11-11",
  "x-method": "derived",
  "x-generator": "derive-json-schema.py",
  "x-source": "openapi/honeylabs-net-openapi.json#/components/schemas/IpReport",
  "type": "object",
  "properties": {
    "ip": {
      "type": "string"
    },
    "observed": {
      "type": "boolean",
      "description": "True when a first-packet event or an interactive TCP session reached a sensor."
    },
    "total_events": {
      "type": "integer",
      "description": "First-packet events only."
    },
    "verdict": {
      "$ref": "#/$defs/Verdict"
    },
    "first_seen": {
      "type": [
        "string",
        "null"
      ],
      "description": "Earliest of either source."
    },
    "last_seen": {
      "type": [
        "string",
        "null"
      ],
      "description": "Latest of either source."
    },
    "events_24h": {
      "type": "integer"
    },
    "events_7d": {
      "type": "integer"
    },
    "interactive_sessions": {
      "type": "integer",
      "description": "Interactive TCP sessions in the last 90 days. UDP sessions are left out of every overview field (a UDP source can be forged) and stay in interaction."
    },
    "interactive_sessions_24h": {
      "type": "integer"
    },
    "interactive_sessions_7d": {
      "type": "integer"
    },
    "interactive_protocols": {
      "type": "array",
      "description": "Protocols of those sessions, most used first.",
      "items": {
        "type": "object",
        "properties": {
          "protocol": {
            "type": "string"
          },
          "sessions": {
            "type": "integer"
          }
        }
      }
    },
    "distinct_ports_total": {
      "type": "integer",
      "description": "TCP ports across both sources, each port once."
    },
    "geo": {
      "type": "object",
      "description": "country_code, country_name, region, city, lat, lon, asn and org; they fall back to the interactive sessions' values where the first-packet data has none.",
      "properties": {
        "country_code": {
          "type": "string"
        },
        "country_name": {
          "type": "string"
        },
        "region": {
          "type": "string"
        },
        "city": {
          "type": "string"
        },
        "lat": {
          "type": [
            "number",
            "null"
          ]
        },
        "lon": {
          "type": [
            "number",
            "null"
          ]
        },
        "asn": {
          "type": [
            "integer",
            "null"
          ]
        },
        "org": {
          "type": "string"
        }
      }
    },
    "fingerprints": {
      "type": "object"
    },
    "recent_events": {
      "type": "array",
      "items": {
        "type": "object"
      }
    },
    "source_domain": {
      "type": [
        "string",
        "null"
      ],
      "description": "rDNS, falling back to the sessions' rDNS."
    },
    "top_ports": {
      "type": "array",
      "description": "At most 15, ranked by events and sessions together.",
      "items": {
        "type": "object",
        "properties": {
          "port": {
            "type": "integer"
          },
          "proto": {
            "type": "string"
          },
          "label": {
            "type": "string"
          },
          "count": {
            "type": "integer",
            "description": "First-packet events."
          },
          "sessions": {
            "type": "integer",
            "description": "Interactive sessions on that port."
          }
        }
      }
    },
    "timeline": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "date": {
            "type": "string"
          },
          "count": {
            "type": "integer",
            "description": "First-packet events."
          },
          "sessions": {
            "type": "integer",
            "description": "Interactive sessions."
          }
        }
      }
    },
    "cve_matches": {
      "type": "array",
      "description": "CVE signatures matched by this address's requests. A CVE whose payload an interactive session carried is a match with match_field \"interactive_session\" and confirmed_by \"interactive_session\". Each match carries actively_exploited (on the CISA KEV catalogue, or flagged by its signatures), kev_date_added (YYYY-MM-DD or null) and a title, taken the same way as on /api/cves.",
      "items": {
        "type": "object"
      }
    },
    "interaction": {
      "type": [
        "object",
        "null"
      ],
      "description": "The Interaction group: sessions, logins, commands, downloads and files from the interactive sensors over 90 days. null when the address held no session."
    }
  },
  "$defs": {
    "Engagement": {
      "type": "object",
      "description": "How much the actor sent, and whether it stayed.",
      "properties": {
        "level": {
          "type": "string",
          "enum": [
            "touch",
            "request",
            "payload"
          ],
          "description": "touch <64B, request 64B-4KB, payload >=4KB"
        },
        "label": {
          "type": "string"
        },
        "detail": {
          "type": "string"
        },
        "bytes_sent": {
          "type": "integer"
        },
        "session_seconds": {
          "type": "integer"
        },
        "persistent": {
          "type": "boolean",
          "description": "Session held open 60s or more"
        }
      }
    },
    "Verdict": {
      "type": "object",
      "properties": {
        "verdict": {
          "type": "string",
          "enum": [
            "scanner",
            "tor",
            "malicious",
            "scanning",
            "probing",
            "none"
          ],
          "description": "Stable set, safe to switch on. 'none' means not observed, which is about our coverage rather than the address being clean."
        },
        "label": {
          "type": "string"
        },
        "detail": {
          "type": [
            "string",
            "null"
          ]
        },
        "confidence": {
          "type": "string",
          "enum": [
            "none",
            "low",
            "medium",
            "high"
          ]
        },
        "network_type": {
          "type": [
            "string",
            "null"
          ]
        },
        "why": {
          "type": "array",
          "items": {
            "type": "string"
          },
          "description": "The signals that decided it. Show these; a label without reasoning is an assertion."
        },
        "engagement": {
          "$ref": "#/$defs/Engagement"
        },
        "basis": {
          "type": "string",
          "enum": [
            "first_contact",
            "interactive",
            "combined"
          ],
          "description": "What decided the verdict: first-packet signals alone, the interactive sessions, or the first-packet classifier with the sessions counted in."
        }
      }
    }
  }
}