<?xml version="1.0" encoding="utf-8" ?>
<!--
bigFlows.pcap — Typical CI/CD Run Session Tracking

Tracks the protocols a CI/CD pipeline typically exercises:
  - dns-resolve       : DNS A/AAAA lookups (artifact hosts, registries)
  - tls-session       : HTTPS handshakes (registry, package, API endpoints)
  - http-transaction  : Per-request HTTP transactions
  - ssh-session       : SSH (git clone/push, deploy)
  - dhcp-transaction  : Agent lease acquisition

multi-match="true" so TCP lifecycle templates can close app-layer
sessions on FIN/RST without suppressing the app-layer messages
themselves.
-->
<FXT multi-match="true">

  <!-- ============================================================
       SESSION TYPE DEFINITIONS
       ============================================================ -->

  <!-- Thresholds derived from this capture's prior run:
       max-duration = p95 of observed session durations (late::… / timeout::…)
       idle-timeout = 60s uniformly (>1 min inactivity → timed-out) -->

  <session-type name="dns-resolve" default="true" max-duration="3" idle-timeout="60">
    <qualify logic="or">
      <field value="53">udp.srcport</field>
      <field value="53">udp.dstport</field>
    </qualify>
    <key>
      <protocol>
        <field>dns.id</field>
      </protocol>
    </key>
  </session-type>

  <session-type name="tls-session" max-duration="176" idle-timeout="60">
    <qualify logic="or">
      <field value="443">tcp.srcport</field>
      <field value="443">tcp.dstport</field>
      <field value="8443">tcp.srcport</field>
      <field value="8443">tcp.dstport</field>
    </qualify>
    <key>
      <protocol>
        <field>tcp.stream</field>
      </protocol>
    </key>
  </session-type>

  <!-- Long-poll HTTP endpoints (Dropbox change-notify, Disqus realtime).
       Declared BEFORE the generic http-transaction so the request packet
       (which carries http.host) qualifies into long-poll first; the
       tcp.stream key then binds the response leg to the same session.
       Wider thresholds reflect intentional 60–120s server holds.

       NOTE: with multi-match="true" (required so TCP RST can close
       app-layer sessions), each long-poll request also qualifies for
       http-transaction below and creates a parallel session there.
       http-transaction is therefore the SUPERSET (all HTTP, including
       long-poll); http-longpoll is a focused sub-view. Do not sum the
       two — they overlap by design. -->
  <session-type name="http-longpoll" max-duration="180" idle-timeout="120">
    <qualify logic="or">
      <field value-regex="notify\d*\.dropbox\.com">http.host</field>
      <field value-regex="realtime\.services\.disqus\.com">http.host</field>
    </qualify>
    <key>
      <protocol>
        <field>tcp.stream</field>
      </protocol>
    </key>
  </session-type>

  <session-type name="http-transaction" max-duration="1" idle-timeout="60">
    <qualify logic="or">
      <field value="80">tcp.srcport</field>
      <field value="80">tcp.dstport</field>
      <field value="8080">tcp.srcport</field>
      <field value="8080">tcp.dstport</field>
    </qualify>
    <key>
      <protocol>
        <field>tcp.stream</field>
      </protocol>
    </key>
  </session-type>

  <session-type name="ssh-session" max-duration="12" idle-timeout="60">
    <qualify logic="or">
      <field value="22">tcp.srcport</field>
      <field value="22">tcp.dstport</field>
    </qualify>
    <key>
      <protocol>
        <field>tcp.stream</field>
      </protocol>
    </key>
  </session-type>

  <session-type name="dhcp-transaction" max-duration="300" idle-timeout="60">
    <key>
      <protocol>
        <field>dhcp.id</field>
      </protocol>
    </key>
  </session-type>

  <!-- ============================================================
       DNS  (port 53)
       ============================================================ -->

  <udp-message
        session-type="dns-resolve"
        session-start="true"
        bookmark="true"
        style="forest-green"
    >
    <opcode match="^Domain Name System \(query\)" replace="❓ DNS Query">dns</opcode>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🆔 ID: $1">dns.id</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🔍 Query: $1">dns.qry.name</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="📋 Type: $1">dns.qry.type</param>
  </udp-message>

  <udp-message
        session-type="dns-resolve"
        session-stop="true"
        session-result="success::resolved"
        bookmark="true"
        style="forest-green dashed"
    >
    <opcode match=".*No error.*" replace="✅ DNS Response (OK)">dns.flags</opcode>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🆔 ID: $1">dns.id</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🔍 Query: $1">dns.qry.name</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🌐 Address: $1">dns.a</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🌐 AAAA: $1">dns.aaaa</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🔗 CNAME: $1">dns.cname</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="⏱️ Time: $1">dns.time</param>
  </udp-message>

  <udp-message
        session-type="dns-resolve"
        session-stop="true"
        session-result="failure::dns-error"
        bookmark="true"
        style="crimson wave"
    >
    <opcode match="^Domain Name System \(response\)" replace="⚠️ DNS Response (Error)">dns</opcode>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🆔 ID: $1">dns.id</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🔍 Query: $1">dns.qry.name</param>
    <param match=".*=\s+(.*)" replace="⚠️ Rcode: $1">dns.flags.rcode</param>
  </udp-message>

  <!-- ============================================================
       TLS / HTTPS  (ports 443, 8443)
       ============================================================ -->

  <tcp-message
        session-type="tls-session"
        session-start="true"
        bookmark="true"
        style="amethyst"
    >
    <opcode match=".*Client Hello.*" replace="🤝 TLS Client Hello">tls.record</opcode>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🔒 Version: $1">tls.handshake.version</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🌐 SNI: $1">tls.handshake.extensions_server_name</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🔍 JA3: $1">tls.handshake.ja3</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🔢 Stream: $1">tcp.stream</param>
  </tcp-message>

  <tcp-message session-type="tls-session" style="sea-green" bookmark="true">
    <opcode match=".*Server Hello($|[^ ]).*" replace="🤝 TLS Server Hello">tls.record</opcode>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🔒 Version: $1">tls.handshake.version</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🔐 Cipher: $1">tls.handshake.ciphersuite</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🔍 JA3S: $1">tls.handshake.ja3s</param>
  </tcp-message>

  <tcp-message session-type="tls-session" style="forest-green">
    <opcode match=".*Certificate($|[^ ]).*" replace="🔐 TLS Certificate">tls.record</opcode>
    <param match="(?:[^:]*:\s*)?(.*)" replace="📏 Cert Length: $1">tls.handshake.certificate_length</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="👤 Subject: $1">x509if.rdnSequence</param>
  </tcp-message>

  <tcp-message session-type="tls-session" style="sea-green">
    <opcode match=".*(Server Key Exchange|Client Key Exchange|Server Hello Done|Encrypted Handshake Message|New Session Ticket|Change Cipher Spec).*" replace="🔒 TLS $1">tls.record</opcode>
  </tcp-message>

  <tcp-message session-type="tls-session" style="slate dotted">
    <opcode match=".*Application Data.*" replace="🔒 TLS Application Data">tls.record</opcode>
    <param match="(?:[^:]*:\s*)?(.*)" replace="📏 Length: $1">tls.record.length</param>
  </tcp-message>

  <!-- TLS Alert: close_notify (graceful) -->
  <tcp-message
        session-type="tls-session"
        session-stop="true"
        session-result="success::tls-close-notify"
        style="forest-green dashed"
        bookmark="true"
    >
    <opcode match=".*Alert.*Close Notify.*" replace="🚨 TLS Close Notify">tls.record</opcode>
    <param match="Description:\s*(.*)" replace="📝 Description: $1">tls.alert_message.desc</param>
  </tcp-message>

  <!-- TLS Alert: fatal -->
  <tcp-message
        session-type="tls-session"
        session-stop="true"
        session-result="failure::tls-alert-fatal"
        style="rust"
        bookmark="true"
    >
    <opcode match=".*Alert.*(Fatal|Handshake Failure|Certificate|Decrypt|Protocol|Unexpected|Bad Record).*" replace="🚨 TLS Alert Fatal">tls.record</opcode>
    <param match="Level:\s*(.*)" replace="⚠️ Level: $1">tls.alert_message.level</param>
    <param match="Description:\s*(.*)" replace="📝 Description: $1">tls.alert_message.desc</param>
  </tcp-message>

  <!-- TLS Alert: encrypted/generic -->
  <tcp-message
        session-type="tls-session"
        session-stop="true"
        session-result="failure::tls-alert"
        style="rust"
        bookmark="true"
    >
    <opcode match=".*(Encrypted Alert|Alert).*" replace="🚨 TLS Alert">tls.record</opcode>
    <param match="Description:\s*(.*)" replace="📝 Description: $1">tls.alert_message.desc</param>
  </tcp-message>

  <!-- TCP FIN closes the TLS session if no alert seen -->
  <tcp-message
        session-type="tls-session"
        session-stop="true"
        session-result="success::tcp-fin"
        style="forest-green dashed"
    >
    <opcode match=".*Flags:.*\((.*FIN.*)\)" replace="🔗 TCP ($1)">tcp.flags</opcode>
  </tcp-message>

  <tcp-message
        session-type="tls-session"
        session-stop="true"
        session-result="failure::tcp-reset"
        style="crimson wave"
        bookmark="true"
    >
    <opcode match=".*Flags:.*\((.*RST.*)\)" replace="🔗 TCP ($1)">tcp.flags</opcode>
  </tcp-message>

  <!-- ============================================================
       HTTP  (ports 80, 8080)
       ============================================================ -->

  <tcp-message
        session-type="http-transaction"
        session-start="true"
        bookmark="true"
        style="teal"
    >
    <opcode match="(?:[^:]*:\s*)?(.*)" replace="🌐 HTTP $1">http.request.method</opcode>
    <param match="(?:[^:]*:\s*)?(.*)" replace="📍 URI: $1">http.request.uri</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🏠 Host: $1">http.host</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🧭 User-Agent: $1">http.user_agent</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="📄 Content-Type: $1">http.content_type</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="📏 Content-Length: $1">http.content_length</param>
  </tcp-message>

  <tcp-message
        session-type="http-transaction"
        session-stop="true"
        session-result="success::http-2xx"
        style="teal dashed"
        bookmark="true"
    >
    <opcode match="^2[0-9][0-9]" replace="HTTP $0 OK">http.response.code</opcode>
    <param match="(?:[^:]*:\s*)?(.*)" replace="💬 Phrase: $1">http.response.phrase</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="📄 Content-Type: $1">http.content_type</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🖥️ Server: $1">http.server</param>
  </tcp-message>

  <tcp-message
        session-type="http-transaction"
        session-stop="true"
        session-result="success::http-redirect"
        style="teal dashed"
        bookmark="true"
    >
    <opcode match="^3[0-9][0-9]" replace="HTTP $0 Redirect">http.response.code</opcode>
    <param match="(?:[^:]*:\s*)?(.*)" replace="💬 Phrase: $1">http.response.phrase</param>
  </tcp-message>

  <tcp-message
        session-type="http-transaction"
        session-stop="true"
        session-result="failure::http-client-error"
        style="rust"
        bookmark="true"
    >
    <opcode match="^4[0-9][0-9]" replace="HTTP $0 Client Error">http.response.code</opcode>
    <param match="(?:[^:]*:\s*)?(.*)" replace="💬 Phrase: $1">http.response.phrase</param>
  </tcp-message>

  <tcp-message
        session-type="http-transaction"
        session-stop="true"
        session-result="failure::http-server-error"
        style="rust"
        bookmark="true"
    >
    <opcode match="^5[0-9][0-9]" replace="HTTP $0 Server Error">http.response.code</opcode>
    <param match="(?:[^:]*:\s*)?(.*)" replace="💬 Phrase: $1">http.response.phrase</param>
  </tcp-message>

  <tcp-message
        session-type="http-transaction"
        session-stop="true"
        session-result="failure::tcp-reset"
        style="crimson wave"
    >
    <opcode match=".*Flags:.*\((.*RST.*)\)" replace="🔗 TCP ($1)">tcp.flags</opcode>
  </tcp-message>

  <!-- ============================================================
       HTTP long-poll  (Dropbox notify, Disqus realtime)
       Same template shapes as http-transaction; only session-type
       differs so longer durations classify as success, not late.
       ============================================================ -->

  <template-group session-type="http-longpoll">

    <tcp-message session-start="true" bookmark="true" style="cerulean">
      <opcode match="(?:[^:]*:\s*)?(.*)" replace="🔁 HTTP LP $1">http.request.method</opcode>
      <param match="(?:[^:]*:\s*)?(.*)" replace="📍 URI: $1">http.request.uri</param>
      <param match="(?:[^:]*:\s*)?(.*)" replace="🏠 Host: $1">http.host</param>
    </tcp-message>

    <tcp-message
          session-stop="true"
          session-result="success::longpoll-2xx"
          style="cerulean dashed"
          bookmark="true"
      >
      <opcode match="^2[0-9][0-9]" replace="HTTP LP $0 OK">http.response.code</opcode>
      <param match="(?:[^:]*:\s*)?(.*)" replace="💬 Phrase: $1">http.response.phrase</param>
    </tcp-message>

    <tcp-message
          session-stop="true"
          session-result="success::longpoll-redirect"
          style="cerulean dashed"
      >
      <opcode match="^3[0-9][0-9]" replace="HTTP LP $0 Redirect">http.response.code</opcode>
    </tcp-message>

    <tcp-message
          session-stop="true"
          session-result="failure::longpoll-client-error"
          style="rust"
          bookmark="true"
      >
      <opcode match="^4[0-9][0-9]" replace="HTTP LP $0 Client Error">http.response.code</opcode>
    </tcp-message>

    <tcp-message
          session-stop="true"
          session-result="failure::longpoll-server-error"
          style="rust"
          bookmark="true"
      >
      <opcode match="^5[0-9][0-9]" replace="HTTP LP $0 Server Error">http.response.code</opcode>
    </tcp-message>

    <tcp-message
          session-stop="true"
          session-result="failure::tcp-reset"
          style="crimson wave"
      >
      <opcode match=".*Flags:.*\((.*RST.*)\)" replace="🔗 TCP ($1)">tcp.flags</opcode>
    </tcp-message>

  </template-group>

  <!-- ============================================================
       SSH  (port 22)
       ============================================================ -->

  <tcp-message
        session-type="ssh-session"
        session-start="true"
        bookmark="true"
        style="amethyst"
    >
    <opcode match="Protocol: (.*)" replace="🔖 SSH $1">ssh.protocol</opcode>
    <param>ssh.direction</param>
  </tcp-message>

  <tcp-message session-type="ssh-session" bookmark="true" style="royal-blue">
    <opcode match=".*Key Exchange Init.*" replace="🤝 SSH Key Exchange Init">ssh.message_code</opcode>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🔑 KEX: $1">ssh.kex_algorithms</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🔒 Cipher: $1">ssh.encryption_algorithms_client_to_server</param>
  </tcp-message>

  <tcp-message session-type="ssh-session" bookmark="true" style="sea-green">
    <opcode match=".*New Keys.*" replace="🔒 SSH New Keys">ssh.message_code</opcode>
  </tcp-message>

  <tcp-message session-type="ssh-session" style="amethyst">
    <opcode match="Message Code: (.*)" replace="📡 SSH $1">ssh.message_code</opcode>
  </tcp-message>

  <tcp-message session-type="ssh-session" style="amethyst dotted">
    <opcode match="Packet Length: (.*)" replace="🔒 SSH Encrypted (len=$1)">ssh.packet_length</opcode>
    <param>ssh.direction</param>
  </tcp-message>

  <tcp-message
        session-type="ssh-session"
        session-stop="true"
        session-result="success::tcp-fin"
        style="forest-green dashed"
        bookmark="true"
    >
    <opcode match=".*Flags:.*\((.*FIN.*)\)" replace="🔗 TCP ($1)">tcp.flags</opcode>
  </tcp-message>

  <tcp-message
        session-type="ssh-session"
        session-stop="true"
        session-result="failure::tcp-reset"
        style="crimson wave"
        bookmark="true"
    >
    <opcode match=".*Flags:.*\((.*RST.*)\)" replace="🔗 TCP ($1)">tcp.flags</opcode>
  </tcp-message>

  <!-- ============================================================
       DHCP  (CI agent lease lifecycle)
       ============================================================ -->

  <udp-message
        session-type="dhcp-transaction"
        session-start="true"
        bookmark="true"
        style="amethyst"
    >
    <opcode display="brief" match="^1$" replace="🔍 DHCP Discover">dhcp.option.dhcp</opcode>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🆔 XID: $1">dhcp.id</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="📡 MAC: $1">dhcp.hw.mac_addr</param>
    <param match="(?:[^:]*:\s*)?(.*)" replace="🏷️ Hostname: $1">dhcp.option.hostname</param>
  </udp-message>

  <udp-message session-type="dhcp-transaction" bookmark="true" style="forest-green">
    <opcode display="brief" match="^2$" replace="📬 DHCP Offer">dhcp.option.dhcp</opcode>
    <param>dhcp.id</param>
    <param>dhcp.ip.your</param>
    <param>dhcp.option.dhcp_server_id</param>
    <param>dhcp.option.ip_address_lease_time</param>
  </udp-message>

  <udp-message
        session-type="dhcp-transaction"
        session-start="true"
        bookmark="true"
        style="amethyst"
    >
    <opcode display="brief" match="^3$" replace="📝 DHCP Request">dhcp.option.dhcp</opcode>
    <param>dhcp.id</param>
    <param>dhcp.option.requested_ip_address</param>
    <param>dhcp.option.dhcp_server_id</param>
  </udp-message>

  <udp-message
        session-type="dhcp-transaction"
        session-stop="true"
        session-result="success::ack"
        style="forest-green"
        bookmark="true"
    >
    <opcode display="brief" match="^5$" replace="✅ DHCP ACK">dhcp.option.dhcp</opcode>
    <param>dhcp.id</param>
    <param>dhcp.ip.your</param>
    <param>dhcp.option.dhcp_server_id</param>
    <param>dhcp.option.ip_address_lease_time</param>
    <param>dhcp.option.router</param>
    <param>dhcp.option.domain_name_server</param>
  </udp-message>

  <udp-message
        session-type="dhcp-transaction"
        session-stop="true"
        session-result="failure::nak"
        style="magenta"
        bookmark="true"
    >
    <opcode display="brief" match="^6$" replace="❌ DHCP NAK">dhcp.option.dhcp</opcode>
    <param>dhcp.id</param>
    <param>dhcp.option.dhcp_server_id</param>
  </udp-message>

  <udp-message
        session-type="dhcp-transaction"
        session-stop="true"
        session-result="success::released"
        style="sea-green"
        bookmark="true"
    >
    <opcode display="brief" match="^7$" replace="🛑 DHCP Release">dhcp.option.dhcp</opcode>
    <param>dhcp.id</param>
    <param>dhcp.ip.client</param>
  </udp-message>

  <udp-message
        session-type="dhcp-transaction"
        session-start="true"
        bookmark="true"
        style="amethyst"
    >
    <opcode display="brief" match="^8$" replace="ℹ️ DHCP Inform">dhcp.option.dhcp</opcode>
    <param>dhcp.id</param>
    <param>dhcp.ip.client</param>
  </udp-message>

  <!-- ============================================================
       Cross-layer TCP analysis (retransmits, zero-window) — applies
       to every TCP-based session type for CI/CD perf visibility.
       ============================================================ -->

  <tcp-message style="amber" bookmark="true">
    <opcode match=".*" replace="⚠️ TCP Retransmission">tcp.analysis.retransmission</opcode>
    <param>tcp.seq_raw</param>
  </tcp-message>
  <tcp-message style="amber" bookmark="true">
    <opcode match=".*" replace="⚡ TCP Fast Retransmission">tcp.analysis.fast_retransmission</opcode>
    <param>tcp.seq_raw</param>
  </tcp-message>
  <tcp-message style="amber">
    <opcode match=".*" replace="⚠️ TCP Duplicate ACK">tcp.analysis.duplicate_ack</opcode>
    <param>tcp.ack_raw</param>
  </tcp-message>
  <tcp-message style="rust" bookmark="true">
    <opcode match=".*" replace="🛑 TCP Zero Window">tcp.analysis.zero_window</opcode>
    <param match="Bytes in flight:\s*(.*)" replace="📦 Bytes in flight: $1">tcp.analysis.bytes_in_flight</param>
  </tcp-message>

</FXT>
