Skip to content
Please update to the latest release 0.77.2 to address Multiple CVEs.
Generic.Applications.ZeroTier

Generic.Applications.ZeroTier

Collects ZeroTier installation artefacts from the client file system without requiring the ZeroTier service to be running.

ZeroTier creates encrypted peer-to-peer overlay networks over any Internet connection. It is used legitimately for remote access and VPN replacement, but like any VPN service, its tunnels traverse NAT and firewalls without inbound ports, which makes it worth checking when investigating unauthorised remote access or exfiltration.

Key concepts

Worlds, planets and moons

ZeroTier nodes locate each other through root servers listed in a signed binary structure called a world. ZeroTier ships with a built-in planet (Earth, ID 149604618) maintained by ZeroTier Inc. Organisations may replace it with a custom planet or add supplementary moons. A non-default planet or any moon means the node is using private root infrastructure.

Networks

A network is a ZeroTier virtual LAN with a 16-hex-digit ID. Networks can be private (members require controller authorisation) or public. When a node joins a private network, the controller issues a Certificate of Membership (COM) — a signed, time-stamped credential recording when this node’s membership was last authorised and by whom.

Peers

ZeroTier caches peer identities and last-known direct paths in peers.d/. Files are written on clean shutdown and when an inactive peer is dropped from memory, not per-packet, and ZeroTier removes any it has not updated in 30 days. A peer reached only through a root-server relay never establishes a direct path, so its file records no endpoints.

Identity

Each node has a globally unique 10-hex-digit address derived from an elliptic-curve key pair generated on first run. The address appears permanently in any network controller’s member list once the node has joined. By default this artifact redacts private key material (see RedactPrivateKeys).

Sources

  • Identities: the node’s public identity and, if present, private key
  • Networks: configuration for each joined network — assigned addresses, pushed routes, DNS, and the certificate of membership
  • LocalNetworks: per-network policy overrides controlling which routes and DNS the host accepts from the controller
  • Peers: cached peer identities and last-known direct paths
  • Worlds: the planet and any moons the node is configured to use

#vpn #network #zerotier #forensics


name: Generic.Applications.ZeroTier
author: "Andreas Misje – @misje"
description: |
  Collects ZeroTier installation artefacts from the client file system without
  requiring the ZeroTier service to be running.

  ZeroTier creates encrypted peer-to-peer overlay networks over any Internet
  connection. It is used legitimately for remote access and VPN replacement,
  but like any VPN service, its tunnels traverse NAT and firewalls
  without inbound ports, which makes it worth checking when
  investigating unauthorised remote access or exfiltration.

  ## Key concepts

  ### Worlds, planets and moons

  ZeroTier nodes locate each other through *root servers* listed in a signed
  binary structure called a *world*. ZeroTier ships with a built-in *planet*
  (Earth, ID `149604618`) maintained by ZeroTier Inc. Organisations may
  replace it with a custom planet or add supplementary *moons*. A non-default
  planet or any moon means the node is using private root infrastructure.

  ### Networks

  A *network* is a ZeroTier virtual LAN with a 16-hex-digit ID. Networks can
  be private (members require controller authorisation) or public. When a node
  joins a private network, the controller issues a *Certificate of Membership*
  (COM) — a signed, time-stamped credential recording when this node's
  membership was last authorised and by whom.

  ### Peers

  ZeroTier caches peer identities and last-known direct paths in `peers.d/`.
  Files are written on clean shutdown and when an inactive peer is dropped from
  memory, not per-packet, and ZeroTier removes any it has not updated in 30
  days. A peer reached only through a root-server relay never establishes a
  direct path, so its file records no endpoints.

  ### Identity

  Each node has a globally unique 10-hex-digit address derived from an
  elliptic-curve key pair generated on first run. The address appears
  permanently in any network controller's member list once the node has
  joined. By default this artifact redacts private key material (see
  `RedactPrivateKeys`).

  ## Sources

  - **Identities**: the node's public identity and, if present, private key
  - **Networks**: configuration for each joined network — assigned addresses,
    pushed routes, DNS, and the certificate of membership
  - **LocalNetworks**: per-network policy overrides controlling which routes
    and DNS the host accepts from the controller
  - **Peers**: cached peer identities and last-known direct paths
  - **Worlds**: the planet and any moons the node is configured to use

  #vpn #network #zerotier #forensics

references:
  - https://github.com/zerotier/ZeroTierOne/tree/main/node

type: CLIENT

parameters:
  - name: NetworkFilesGlob
    description: |
      Glob patterns for ZeroTier per-network configuration files; `*.local.conf` files are collected by `LocalNetworkFilesGlob` (see description).
    type: json_array
    default: >-
      [
      "/var/lib/zerotier-one/networks.d/*.conf",
      "C:/ProgramData/ZeroTier/One/networks.d/*.conf",
      "/Library/Application Support/ZeroTier/One/networks.d/*.conf"
      ]

  - name: LocalNetworkFilesGlob
    description: |
      Glob patterns for per-network local policy override files (`*.local.conf`; see description).
    type: json_array
    default: >-
      [
      "/var/lib/zerotier-one/networks.d/*.local.conf",
      "C:/ProgramData/ZeroTier/One/networks.d/*.local.conf",
      "/Library/Application Support/ZeroTier/One/networks.d/*.local.conf"
      ]

  - name: IdentityFilesGlob
    description: |
      Glob patterns for the node's `identity.public`/`identity.secret` files; private keys are redacted by default (see `RedactPrivateKeys`).
    type: json_array
    default: >-
      [
      "/var/lib/zerotier-one/identity.{public,secret}",
      "C:/ProgramData/ZeroTier/One/identity.{public,secret}",
      "/Library/Application Support/ZeroTier/One/identity.{public,secret}"
      ]

  - name: PeerFilesGlob
    description: |
      Glob patterns for cached `.peer` files, one per known peer (see description).
    type: json_array
    default: >-
      [
      "/var/lib/zerotier-one/peers.d/*.peer",
      "C:/ProgramData/ZeroTier/One/peers.d/*.peer",
      "/Library/Application Support/ZeroTier/One/peers.d/*.peer"
      ]

  - name: WorldFilesGlob
    description: |
      Glob patterns for the `planet` file and any `moons.d/*.moon` files (see description).
    type: json_array
    default: >-
      [
      "/var/lib/zerotier-one/planet",
      "/var/lib/zerotier-one/moons.d/*.moon",
      "C:/ProgramData/ZeroTier/One/planet",
      "C:/ProgramData/ZeroTier/One/moons.d/*.moon",
      "/Library/Application Support/ZeroTier/One/planet",
      "/Library/Application Support/ZeroTier/One/moons.d/*.moon"
      ]

  - name: RedactPrivateKeys
    description: |
      String substituted for the private key in `identity.secret`; set empty to collect it in full (see description).
    default: "<REDACTED>"

implied_permissions:
  - READ_RESULTS

export: |
  // Redact Field in Obj with RedactPrivateKeys if the field is non-empty:
  LET RedactField(Obj, Field) = if(
      condition=RedactPrivateKeys
       AND get(item=Obj, field=Field),
      then=set(item=Obj, field=Field, value=RedactPrivateKeys),
      else=Obj)

  // Normalise a dict parsed from text: strip empty string values, cast keys
  // matching Booleans (regex) to bool, and keys matching Ints (regex) to int:
  LET ConvertDict(Obj, Booleans=NULL, Ints=NULL) = to_dict(
      item={
      SELECT _key,
             if(condition=_value != "", then=_value) AS _value
      FROM items(item=Obj)
    }) + to_dict(item={
      SELECT _key,
             NOT NOT _value AS _value
      FROM items(item=Obj)
      WHERE _key =~ Booleans
    }) + to_dict(item={
      SELECT _key,
             atoi(string=_value) AS _value
      FROM items(item=Obj)
      WHERE _key =~ Ints
    })

  // Decode ZeroTier Dictionary escape sequences in a string value:
  // \n → newline, \r → CR, \e → "=", \0 → NUL, \\ → backslash.
  // ZeroTier uses a key–value format for most of its files, including
  // binary data. Binary data is escaped.
  LET Unescape(Data) = if(
      condition=Data,
      then=regex_transform(source=Data,
                           map=dict(`\\\\n`='\x0A',
                                    `\\\\r`='\x0D',
                                    `\\\\e`='=',
                                    `\\\\0`='\x00',
                                    `\\\\{2}`='''\''')))

  // Format a 16-byte binary string as a colon-grouped IPv6 address:
  LET ToIPv6(Data) = ip(
      parse=regex_replace(source=format(format="%x", args=Data),
                          re="(.{4})",
                          replace="$1:")[:-1])

  // Binary structure profile covering all ZeroTier on-disk formats.
  // Used by every Parse* helper below via parse_binary():
  LET ZTProfile = '''[
    ["AddrList", 0, [
      ["_Addrs", 0, "Array", {
        type: "_Addr",
        count: 1000,
        max_count: 1000,
        sentinel: "x=>x.Type = 'Empty'"
      }]
    ]],
    ["RouteList", 0, [
      ["_Routes", 0, "Array", {
        type: "Route",
        count: 1000,
        max_count: 1000,
        sentinel: "x=>x.Target.Type = 'Empty'"
      }]
    ]],
    ["Route", "x=>(x.Via.EndOf - x.StartOf) + 4", [
      ["Target", 0, "_Addr"],
      ["Via", "x=>x.Target.EndOf - x.StartOf", "_Addr"],
      ["Flags", "x=>x.Via.EndOf - x.StartOf", "uint16be"],
      ["Metric", "x=>(x.Via.EndOf - x.StartOf) + 2", "uint16be"]
    ]],
    ["DNS", 0, [
      ["FQDN", 0, "String", {
        length: 128
      }],
      ["Addrs", 128, "AddrList"]
    ]],

    ["Peer", 0, [
      ["Version", 0, "uint8"],
      ["Identity", 1, "Identity"],
      ["VProto", "x=>x.Identity.EndOf - x.StartOf", "uint16be"],
      ["VMajor", "x=>(x.Identity.EndOf - x.StartOf) + 2", "uint16be"],
      ["VMinor", "x=>(x.Identity.EndOf - x.StartOf) + 4", "uint16be"],
      ["VRevision", "x=>(x.Identity.EndOf - x.StartOf) + 6", "uint16be"],
      ["PathCount", "x=>(x.Identity.EndOf - x.StartOf) + 8", "uint16be"],
      ["Paths", "x=>(x.Identity.EndOf - x.StartOf) + 10", "Array", {
        type: "_Addr",
        count: "x=>x.PathCount"
      }]
    ]],

    ["World", 0, [
      ["Type", 0, "Enumeration", {
        type: "uint8",
        map: {
          "Null": 0,
          "Planet": 1,
          "Moon": 127
        }
      }],
      ["ID", 1, "uint64be"],
      ["Timestamp", 9, "uint64be"],
      ["_UpdatesSignedBy", 17, "String", {
        term: "",
        length: 64
      }],
      ["_Signature", 81, "String", {
        term: "",
        length: 96
      }],
      ["RootCount", 177, "uint8"],
      ["Roots", 178, "Array", {
        type: "Root",
        count: "x=>x.RootCount"
      }]
    ]],
    ["Root", "x=>x.Endpoints.EndOf - x.StartOf", [
      ["Identity", 0, "Identity"],
      ["EndpointCount", "x=>x.Identity.EndOf - x.StartOf", "uint8"],
      ["Endpoints", "x=>(x.Identity.EndOf - x.StartOf) + 1", "Array", {
        type: "_Addr",
        count: "x=>x.EndpointCount"
      }]
    ]],

    ["Identity", "x=>71 + x.PrivLen", [
      ["_Address", 0, "String", {
        term: "",
        length: 5
      }],
      ["Address", 0, "Value", {
        value: "x=>format(format='%02x', args=x._Address)"
      }],
      ["Type", 5, "uint8"],
      ["_PubKey", 6, "String", {
        term: "",
        length: 64
      }],
      ["PubKey", 0, "Value", {
        value: "x=>format(format='%02x', args=x._PubKey)"
      }],
      ["PrivLen", 70, "uint8"]
    ]],

    ["_Addr", "x=>x.Addr.EndOf - x.StartOf", [
      ["Type", 0, "Enumeration", {
        type: "uint8",
        map: {
          "IPv4": 0x04,
          "IPv6": 0x06,
          "Empty": 0x00
        }
      }],
      ["Addr", 1, "Union", {
        selector: "x=>x.Type",
        choices: {
          "IPv4": "IPv4Addr",
          "IPv6": "IPv6Addr",
          "Empty": "Empty"
        }
      }]
    ]],
    ["IPv4Addr", 6, [
      ["_Value", 0, "uint32"],
      ["Value", 0, "Value", {
        value: "x=>ip(netaddr4_le=x._Value)"
      }],
      ["Prefix", 4, "uint16be"]
    ]],
    ["IPv6Addr", 18, [
      ["_Value", 0, "String", {
        term: "",
        length: 16
      }],
      ["Value", 0, "Value", {
        value: "x=>ToIPv6(Data=x._Value)"
      }],
      ["Prefix", 16, "uint16be"]
    ]],
    ["Empty", 0, []],

    ["COM", 0, [
      ["Version", 0, "uint8"],
      ["QualifierCount", 1, "uint16be"],
      ["Qualifiers", 3, "Array", {
        type: "Qualifier",
        count: "x=>x.QualifierCount"
      }],
      ["_SignedByBytes", "x=>3 + (x.QualifierCount * 24)", "String", {
        term: "",
        length: 5
      }],
      ["SignedBy", 0, "Value", {
        value: "x=>format(format='%02x', args=x._SignedByBytes)"
      }]
    ]],
    ["Qualifier", 24, [
      ["_ID", 0, "uint64be"],
      ["ID", 0, "Enumeration", {
        type: "uint64be",
        map: {
          "Timestamp":  0,
          "NetworkID":  1,
          "IssuedTo":   2,
          "IdHash0":    3,
          "IdHash1":    4,
          "IdHash2":    5,
          "IdHash3":    6
        }
      }],
      ["_Value", 8, "uint64be"],
      ["MaxDelta", 16, "uint64be"],
      ["Value", 0, "Value", {
        value: "x=>if(condition=x._ID = 0, then=timestamp(epoch=x._Value), else=if(condition=x._ID IN (1, 2), then=format(format='%x', args=x._Value), else=x._Value))"
      }]
    ]]
    ]'''

  // Parse binary AddrList blob: returns list of _Addr structs:
  LET ParseIPs(Data) = if(
      condition=Data,
      then=parse_binary(accessor='data',
                        filename=Data,
                        profile=ZTProfile,
                        struct='AddrList')._Addrs)

  // Parse binary RouteList blob: returns list of Route structs:
  LET ParseRoutes(Data) = if(
      condition=Data,
      then=parse_binary(accessor='data',
                        filename=Data,
                        profile=ZTProfile,
                        struct='RouteList')._Routes)

  // Parse binary DNS blob:
  LET ParseDNS(Data) = if(
      condition=Data,
      then=parse_binary(accessor='data',
                        filename=Data,
                        profile=ZTProfile,
                        struct='DNS'))

  // Format a parsed _Addrs list into human-readable "address/prefix" strings:
  LET FormatIPs(Addrs) = array(_={
      SELECT if(condition=Addr.Prefix,
                then=format(format="%v/%v", args=(Addr.Value, Addr.Prefix)),
                else=Addr.Value) AS Addr
      FROM foreach(row=Addrs)
    }).Addr

  // Format a parsed _Routes list into dicts with From (CIDR) and Via keys:
  LET FormatRoutes(Routes) = array(_={
      SELECT dict(`From`=if(condition=Target.Addr.Prefix,
                            then=format(format="%v/%v",
                                        args=(Target.Addr.Value,
                                            Target.Addr.Prefix)),
                            else=Target.Addr.Value),
                  Via=Via.Addr.Value) AS Route
      FROM foreach(row=Routes)
    }).Route

  // Format a parsed DNS struct into a dict with FQDN and Addrs; NULL if no FQDN:
  LET FormatDNS(DNS) = if(
      condition=DNS.FQDN,
      then=dict(FQDN=DNS.FQDN || NULL, Addrs=FormatIPs(Addrs=DNS.Addrs._Addrs)))

  // Parse a binary COM blob. The JSON conversion is done to convert the parsed
  // typose to true dicts, necessary for further processing:
  LET ParseCOM(Data) = if(
      condition=Data,
      then=parse_json(
        data=serialize(format='json',
                       item=parse_binary(accessor='data',
                                         filename=Data,
                                         profile=ZTProfile,
                                         struct='COM'))))

  // Extract IssuedTo node ID, signing authority and issuance timestamp from a
  // parsed COM. Returns NULL for version 0 (unsigned/empty) certificates:
  LET FormatCOM(COM) = if(
      condition=COM.Version >= 1,
      then=dict(IssuedTo=filter(list=COM.Qualifiers,
                                condition='x=>x.ID = "IssuedTo"')[0].Value,
                SignedBy=COM.SignedBy,
                Timestamp=filter(list=COM.Qualifiers,
                                 condition='x=>x.ID = "Timestamp"')[0].Value))

  // Parse a binary .peer cache file:
  LET ParsePeer(Data) = if(
      condition=Data,
      then=parse_binary(accessor='data',
                        filename=Data,
                        profile=ZTProfile,
                        struct='Peer'))

  // Format a parsed Peer into a dict with Address, Version string and Endpoints list:
  LET FormatPeer(Peer) = if(
      condition=Peer.Version,
      then=dict(
        Address=Peer.Identity.Address,
        Version=format(
          format="%v.%v.%v",
          args=(Peer.VMajor, Peer.VMinor, Peer.VRevision)),
        Endpoints=array(
          _={
      SELECT
      format(
        format="%v:%v",
        args=(Addr.Value, Addr.Prefix)) AS EP
      FROM foreach(
        row=Peer.Paths)
    }).EP))

  // Parse a binary planet or moon world file:
  LET ParseWorld(Data) = if(
      condition=Data,
      then=parse_binary(accessor='data',
                        filename=Data,
                        profile=ZTProfile,
                        struct='World'))

  // Format a parsed World into a dict. IsDefaultPlanet is true for the
  // built-in ZeroTier Earth planet (ID 149604618):
  LET FormatWorld(World) = if(
      condition=World.Type,
      then=dict(Type=World.Type,
                ID=World.ID,
                Timestamp=timestamp(epoch=World.Timestamp / 1000),
                Roots=array(_={
      SELECT dict(Address=Identity.Address,
                  Endpoints=array(_={
      SELECT format(format="%v:%v", args=(Addr.Value, Addr.Prefix)) AS EP
      FROM foreach(row=Endpoints)
    }).EP) AS R
      FROM foreach(row=World.Roots)
    }).R,
                IsDefaultPlanet=(World.ID = 149604618
                   AND World.Type = "Planet")))

  // NULL-guard atoi — returns NULL for empty input rather than 0:
  LET StrToInt(String) = if(condition=String, then=atoi(string=String))

  // NULL-guard hex-string-to-int (prepends "0x"); returns NULL for empty input:
  LET HEXToInt(String) = if(condition=String, then=atoi(string='0x' + String))

  // Convert a hex epoch string to a timestamp; returns NULL for empty input:
  LET HEXToTimestamp(Data) = if(condition=Data,
                                then=timestamp(epoch=atoi(string='0x' + Data)))

  // Helper to produce `Fqdn` that would normally be present in the notebook
  // (which we override):
  LET _FQDN = scope().Fqdn || client_info(client_id=ClientId).os_info.fqdn

sources:
  - name: Identities
    precondition: SELECT true FROM scope()
    query: |
      LET Results = SELECT
          OSPath,
          Mtime || NULL AS Mtime,
          Atime || NULL AS Atime,
          Ctime || NULL AS Ctime,
          Btime || NULL AS Btime,
          parse_string_with_regex(
            string=read_file(filename=OSPath),
            regex='''^(?P<ID>[^:]+):(?P<Type>\d+):(?P<PublicKey>[^:]+)(?::(?P<PrivateKey>.+))?$''') AS Identity
        FROM glob(
          globs=IdentityFilesGlob)

      SELECT *, RedactField(Obj=ConvertDict(Obj=Identity, Ints='^Type$'),
                            Field='PrivateKey') AS Identity
      FROM Results

    notebook:
      - type: vql
        template: |
          /*
          ## Node identity

          The ZeroTier node address is a globally unique 10-hex-digit identifier
          derived from the key pair in `identity.public`/`identity.secret`. It
          is the same address that appears in every network controller's member
          list for networks this host has joined.

          The presence of `identity.secret` (containing the private key) is
          expected on a functioning ZeroTier node. Its file timestamps record
          when ZeroTier was first installed on this host (`Btime`) and last
          active (`Mtime`).

          `KeyType` is always `0` (C25519/Ed25519 key pair); no other type
          is defined in the currently known versions of ZeroTier.
          */
          LET ColumnTypes<=dict(`_ClientID`='client')

          SELECT ClientId AS _ClientID,
                 _FQDN,
                 OSPath,
                 Mtime,
                 Btime,
                 Identity.ID AS NodeID,
                 Identity.Type AS KeyType,
                 if(condition=Identity.PrivateKey,
                    then="present") AS PrivateKey
          FROM source(source="Identities")

  - name: Networks
    query: |
      LET S = scope()

      LET Networks = SELECT
          OSPath,
          MACB,
          Unescape(Data=S.NetworkID) AS NetworkID,
          Unescape(Data=S.Name) AS Name,
          Unescape(Data=S.IssuedTo) AS NodeID,
          HEXToInt(String=S.`Version`) AS `Version`,
          HEXToInt(String=S.Revision) AS Revision,
          if(condition=S.NetType
              AND atoi(string='0x' + NetType),
             then='public',
             else='private') AS NetType,
          ParseCOM(Data=Unescape(Data=S.COM)) AS COM,
          HEXToTimestamp(Data=Unescape(Data=S.Timestamp)) AS Timestamp,
          HEXToInt(String=Unescape(Data=S.MTU)) AS MTU,
          ParseIPs(Data=Unescape(Data=S.IPs)) AS Addrs,
          ParseRoutes(Data=Unescape(Data=S.Routes)) AS Routes,
          ParseDNS(Data=Unescape(Data=S.DNS)) AS DNS,
          if(condition=S.SSOIssuer,
             then=dict(Issuer=S.SSOIssuer, Provider=S.SSOProvider)) AS SSO
        FROM foreach(
          row={
          SELECT
          dict(OSPath=OSPath,
               MACB=dict(Mtime=Mtime || NULL,
                         Atime=Atime || NULL,
                         Ctime=Ctime || NULL,
                         Btime=Btime || NULL)) + parse_string_with_regex(
            string=read_file(filename=OSPath),
            regex=('(?m)^nwid=(?P<NetworkID>.+)$', '(?m)^n=(?P<Name>.+)$', '(?m)^mtu=(?P<MTU>.+)$', '''(?m)^I=(?P<IPs>[^\n]+)$''', '(?m)^RT=(?P<Routes>.+)$', '(?m)^DNS=(?P<DNS>.+)$', '(?m)^v=(?P<Version>.+)$', '(?m)^ts=(?P<Timestamp>.+)$', '(?m)^r=(?P<Revision>.+)$', '(?m)^id=(?P<IssuedTo>.+)$', '(?m)^t=(?P<NetType>.+)$', '(?m)^C=(?P<COM>[^\n]+)$', '(?m)^iurl=(?P<SSOIssuer>.+)$', '(?m)^ssop=(?P<SSOProvider>.+)$', )) AS Info
          FROM glob(
            globs=NetworkFilesGlob)
          WHERE NOT OSPath =~ '''\.local.conf$'''
        },
          column='Info')

      SELECT OSPath,
             MACB,
             NetworkID,
             Name,
             NodeID AS _NodeID,
             `Version` AS _Version,
             Revision AS _Revision,
             NetType,
             Timestamp,
             COM AS _COM,
             FormatCOM(COM=COM) AS CertOfMembership,
             SSO AS _SSO,
             MTU AS _MTU,
             FormatIPs(Addrs=Addrs) AS Addrs,
             FormatRoutes(Routes=Routes) AS Routes,
             FormatDNS(DNS=DNS) AS DNS
      FROM Networks

    notebook:
      - type: vql
        template: |
          /*
          ## Network memberships

          The Certificate of Membership (COM) is issued by the network controller
          each time it approves this node's membership. `LastAuthenticated` is the
          most recent time the controller validated this node on each network.
          A timestamp within the incident window indicates the node was an active,
          authenticated member at that time.

          `SignedByNodeID` is the ZeroTier address of the controller that issued
          the certificate. On hosted (zerotier.com) networks this will be
          ZeroTier Inc.'s controller. On self-hosted networks it is the
          organisation's own.
          */
          LET ColumnTypes<=dict(`_ClientID`='client')

          SELECT ClientId AS _ClientID,
                 _FQDN,
                 NetworkID,
                 Name,
                 NetType,
                 CertOfMembership.Timestamp AS LastAuthenticated,
                 CertOfMembership.IssuedTo AS IssuedToNodeID,
                 CertOfMembership.SignedBy AS SignedByNodeID,
                 MACB.Mtime AS FileModified,
                 Addrs,
                 Routes,
                 DNS
          FROM source(source="Networks")
          ORDER BY LastAuthenticated DESC

      - type: vql
        template: |
          /*
          ## Gateway routes

          Routes with a `Via` address are pushed by the controller to forward
          traffic for a given subnet through a specific ZeroTier peer acting as
          a gateway. These are distinct from directly-connected routes (no `Via`),
          which simply define the overlay's own address space.

          `0.0.0.0/0` with a `Via` routes all host traffic through that peer.
          RFC-1918 entries give the host access to internal subnets via the
          overlay. The `Via` address is the ZeroTier IP of the peer acting as
          the gateway.
          */
          LET ColumnTypes<=dict(`_ClientID`='client')

          SELECT ClientId AS _ClientID,
                 _FQDN,
                 NetworkID,
                 Name,
                 _NodeID AS NodeID,
                 NetType,
                 Addrs,
                 filter(list=Routes,
                        condition='x => x.Via') AS GatewayRoutes
          FROM source(source="Networks")
          WHERE filter(list=Routes,
                       condition='x => x.Via')

      - type: vql
        template: |
          /*
          ## All routes (flat)

          One row per route entry across all joined networks.
          */
          LET ColumnTypes<=dict(`_ClientID`='client')

          SELECT ClientId AS _ClientID,
                 _FQDN,
                 NetworkID,
                 Name,
                 NodeID,
                 Route.`From` AS Target,
                 Route.Via AS Via
          FROM flatten(query={
              SELECT ClientId,
                     NetworkID,
                     Name,
                     _NodeID AS NodeID,
                     Routes AS Route
              FROM source(source="Networks")
              WHERE Routes
            })

      - type: vql
        template: |
          /*
          ## DNS configuration

          Networks with DNS pushed by the controller. `Domain` is the
          search domain. `Resolvers` are the DNS server addresses.
          */
          LET ColumnTypes<=dict(`_ClientID`='client')

          SELECT ClientId AS _ClientID,
                 _FQDN,
                 NetworkID,
                 Name,
                 _NodeID AS NodeID,
                 DNS.FQDN AS Domain,
                 DNS.Addrs AS Resolvers
          FROM source(source="Networks")
          WHERE DNS

      - type: vql
        template: |
          /*
          ## SSO-configured networks

          Networks where a single sign-on issuer has been configured.
          */
          LET ColumnTypes<=dict(`_ClientID`='client')

          SELECT ClientId AS _ClientID,
                 _FQDN,
                 NetworkID,
                 Name,
                 _NodeID AS NodeID,
                 _SSO.Issuer AS SSOIssuer,
                 _SSO.Provider AS SSOProvider
          FROM source(source="Networks")
          WHERE _SSO

      - type: vql
        template: |
          /*
          ## Assigned addresses (flat)

          One row per IP address assigned to this host on each network.
          */
          LET ColumnTypes<=dict(`_ClientID`='client')

          SELECT *
          FROM flatten(query={
              SELECT ClientId AS _ClientID,
                     _FQDN,
                     NetworkID,
                     Name,
                     _NodeID AS NodeID,
                     Addrs AS Addr
              FROM source(source="Networks")
              WHERE Addrs
            })
          GROUP BY NetworkID, Addr

  - name: LocalNetworks
    query: |
      SELECT OSPath,
             regex_replace(source=basename(path=OSPath),
                           re='''\.local\.conf$''',
                           replace='') AS NetworkID,
             Mtime || NULL AS Mtime,
             Atime || NULL AS Atime,
             Ctime || NULL AS Ctime,
             Btime || NULL AS Btime,
             ConvertDict(Obj=to_dict(item={
          SELECT Key AS _key,
                 Value AS _value
          FROM parse_records_with_regex(
            file=OSPath,
            regex='''(?P<Key>[^\n=]+)=(?P<Value>[^\n]+)''')
        }),
                         Booleans='^allow') AS Conf
      FROM glob(globs=LocalNetworkFilesGlob)

    notebook:
      - type: vql
        template: |
          /*
          ## Local network policy

          These per-network files control which routes the host actually accepts
          from the network controller, regardless of what the controller pushes:

          - `AllowManaged`: accept routes to private/internal subnets.
          - `AllowGlobal`: accept routes to public (non-RFC-1918) IP space.
          - `AllowDefault`: accept a default gateway route (`0.0.0.0/0`),
            redirecting **all** host traffic through the ZeroTier overlay.
          - `AllowDNS`: apply DNS configuration pushed by the controller.

          Either means the host accepted routes beyond its own overlay subnet,
          which is worth checking in any investigation.
          */
          LET ColumnTypes<=dict(`_ClientID`='client')

          SELECT ClientId AS _ClientID,
                 _FQDN,
                 OSPath,
                 NetworkID,
                 Mtime,
                 Conf.allowManaged AS AllowManaged,
                 Conf.allowGlobal AS AllowGlobal,
                 Conf.allowDefault AS AllowDefault,
                 Conf.allowDNS AS AllowDNS
          FROM source(source="LocalNetworks")

      - type: vql
        template: |
          /*
          ## Accepted routes along with policy

          Routes pushed by the controller joined with the local policy
          that controls whether the host actually accepts them.
          */
          LET ColumnTypes<=dict(`_ClientID`='client')

          LET PolicyByNetwork <= to_dict(item={
              SELECT NetworkID AS _key,
                     Conf AS _value
              FROM source(source="LocalNetworks")
            })

          SELECT ClientId AS _ClientID,
                 _FQDN,
                 NetworkID,
                 Name,
                 _NodeID AS NodeID,
                 Routes,
                 DNS,
                 get(item=PolicyByNetwork,
                     field=NetworkID).allowManaged AS AllowManaged,
                 get(item=PolicyByNetwork,
                     field=NetworkID).allowGlobal AS AllowGlobal,
                 get(item=PolicyByNetwork,
                     field=NetworkID).allowDefault AS AllowDefault,
                 get(item=PolicyByNetwork,
                     field=NetworkID).allowDNS AS AllowDNS
          FROM source(source="Networks")

  - name: Peers
    query: |
      LET Results = SELECT
          OSPath,
          Mtime || NULL AS Mtime,
          Atime || NULL AS Atime,
          Ctime || NULL AS Ctime,
          Btime || NULL AS Btime,
          FormatPeer(Peer=ParsePeer(Data=read_file(filename=OSPath))) AS Peer
        FROM glob(globs=PeerFilesGlob)

      SELECT OSPath,
             Mtime,
             Atime,
             Ctime,
             Btime,
             Peer.Address AS PeerID,
             Peer.Version AS Version,
             Peer.Endpoints AS Endpoints
      FROM Results

    notebook:
      - type: vql
        template: |
          /*
          ## Peer cache

          Peer cache files are written on clean ZeroTier shutdown and
          periodically when a peer expires from the in-memory peer table after
          becoming inactive. If the service was killed or crashed, files may be
          absent or not reflect recent activity.

          `LastSeen` is the file modification time, written at shutdown or peer
          eviction, not when the peer was last contacted. The gap between last
          contact and last write is unknown.
          `FirstSeen` (birth/creation time, where the filesystem supports it)
          approximates when this peer's entry was first persisted to disk.
          Treat both as rough indicators, not precise timestamps.

          `Endpoints` are the direct IP:port paths known at the time of the
          last file write, and may not cover every address the peer has used.
          Each is a real Internet address: the remote node's own, or that of a
          NAT or relay in front of it. They can help locate a peer or identify
          which host it connects through.
          */
          LET ColumnTypes<=dict(`_ClientID`='client')

          SELECT ClientId AS _ClientID,
                 _FQDN,
                 Mtime AS LastSeen,
                 Btime AS FirstSeen,
                 PeerID,
                 Version AS ZTVersion,
                 Endpoints
          FROM source(source="Peers")
          ORDER BY LastSeen DESC

      - type: vql
        template: |
          /*
          ## Peer endpoints (flat)

          One row per endpoint across all cached peers, deduplicated by
          address. Useful for searching a specific IP across all known
          peers.
          */
          LET ColumnTypes <= dict(
              `_ClientID`='client')

          LET SplitEndpoint(Endpoint) = parse_string_with_regex(
              string=Endpoint,
              regex='''(?P<IP>.+):(?P<Port>\d+)''')

          LET Endpoints = SELECT *, SplitEndpoint(Endpoint=Endpoint) AS Endpoint
            FROM flatten(query={
              SELECT ClientId AS _ClientID,
                    _FQDN,
                    Mtime AS LastSeen,
                    Btime AS FirstSeen,
                    PeerID,
                    Version AS ZTVersion,
                    Endpoints AS Endpoint
              FROM source(source="Peers")
              WHERE Endpoint
              ORDER BY LastSeen DESC
            })
            GROUP BY Endpoint

          SELECT _ClientID,
                _FQDN,
                LastSeen,
                FirstSeen,
                PeerID,
                ZTVersion,
                Endpoint.IP AS IP,
                Endpoint.Port AS Port
          FROM Endpoints

  - name: Worlds
    query: |
      SELECT OSPath,
             Mtime || NULL AS Mtime,
             Atime || NULL AS Atime,
             Ctime || NULL AS Ctime,
             Btime || NULL AS Btime,
             FormatWorld(World=ParseWorld(Data=read_file(filename=OSPath))) AS World
      FROM glob(globs=WorldFilesGlob)

    notebook:
      - type: vql
        template: |
          /*
          ## Worlds (planet and moons)

          ZeroTier ships with a single built-in planet (Earth, ID `149604618`)
          maintained by ZeroTier Inc. Its root servers handle initial peer
          discovery for the global ZeroTier network.

          **Non-default planet** (`IsDefaultPlanet = false`): the node uses a
          custom planet, routing all peer discovery through privately controlled
          root servers. Normal in enterprise deployments, and also a way to run
          an overlay outside ZeroTier's public infrastructure.

          **Moons** (`WorldType = "Moon"`): supplementary root servers orbited in
          addition to the planet. The presence of moons always indicates custom
          infrastructure. Check `Roots` for the root server addresses.

          `WorldTimestamp` is the creation/update timestamp embedded in the world
          definition itself, not when it was installed. Use the file `Mtime` for
          the latter.
          */
          LET ColumnTypes<=dict(`_ClientID`='client')

          SELECT ClientId AS _ClientID,
                 _FQDN,
                 OSPath,
                 Mtime,
                 World.Type AS WorldType,
                 World.ID AS WorldID,
                 World.Timestamp AS WorldTimestamp,
                 World.IsDefaultPlanet AS IsDefaultPlanet,
                 World.Roots AS Roots
          FROM source(source="Worlds")

      - type: vql
        template: |
          /*
          ## Custom root server endpoints

          One row per endpoint for any non-default planet or moon. The
          built-in Earth planet (ID `149604618`) is excluded. These are
          the actual network addresses of the root infrastructure this
          node is configured to use.
          */
          LET ColumnTypes<=dict(`_ClientID`='client')

          SELECT ClientId AS _ClientID,
                 _FQDN,
                 *
          FROM flatten(query={
              SELECT ClientId,
                     WorldID,
                     WorldType,
                     WorldTimestamp,
                     Roots.Address AS Address,
                     Roots.Endpoints AS Endpoint
              FROM flatten(query={
                  SELECT ClientId,
                         World.ID AS WorldID,
                         World.Type AS WorldType,
                         World.Timestamp AS WorldTimestamp,
                         World.Roots AS Roots
                  FROM source(source="Worlds")
                  WHERE NOT World.IsDefaultPlanet
                })
            })