Multi-Modal State Observability¶
Enable pcap_obs=True to extend each agent's observation dict with two additional arrays that expose the network state as synthesized packet traffic and a per-host feature matrix.
env = NetForgeRLEnv({
'scenario_type': 'ransomware',
'pcap_obs': True,
})
obs, _ = env.reset(seed=0)
Observation keys (when pcap_obs=True)¶
| Key | Shape | dtype | Description |
|---|---|---|---|
pcap |
(32, 20) | float32 | Synthesized packet snapshot for this tick |
node_features |
(100, 8) | float32 | Per-host attribute matrix for GNN models |
adj_matrix |
(10000,) | float32 | 100×100 routing adjacency (always present) |
PCAP packet features (20 dims)¶
| Idx | Name | Range | Description |
|---|---|---|---|
| 0 | src_idx |
0–1 | Source host index / 100 |
| 1 | dst_idx |
0–1 | Destination host index / 100 |
| 2 | protocol |
0–1 | TCP=0.2 UDP=0.4 ICMP=0.6 Modbus=0.8 S7Comm=1.0 |
| 3 | port_norm |
0–1 | Port / 65535 |
| 4 | payload_kb |
0–1 | Payload size / 100 KB |
| 5 | flag_syn |
0/1 | TCP SYN |
| 6 | flag_rst |
0/1 | TCP RST |
| 7 | flag_ack |
0/1 | TCP ACK (C2 keepalive) |
| 8 | flag_psh |
0/1 | Data payload present |
| 9 | is_lateral |
0/1 | Source and destination in different subnets |
| 10 | is_c2 |
0/1 | Beacon from compromised host |
| 11 | is_recon |
0/1 | ICMP/scan pattern |
| 12 | is_exfil |
0/1 | Large payload from compromised host |
| 13 | is_exploit |
0/1 | Toward known vulnerable port |
| 14 | dst_sensitive |
0/1 | Destination in Secure or OT subnet |
| 15 | src_privilege |
0/0.5/1 | None / User / Root |
| 16 | dst_compromised |
0/1 | Destination already owned |
| 17 | tick_norm |
0–1 | tick / max_ticks |
| 18 | is_encrypted |
0/1 | TLS/SSH (port 443 or 22) |
| 19 | severity |
0/0.5/1 | Benign / suspicious / critical |
Node feature matrix (8 dims per host)¶
| Idx | Name | Description |
|---|---|---|
| 0 | privilege |
0=none 0.5=user 1.0=root |
| 1 | is_online |
host.status == 'online' |
| 2 | is_compromised |
compromised_by != 'None' |
| 3 | is_decoy |
decoy node |
| 4 | is_dc |
domain controller |
| 5 | subnet_type |
DMZ=0.2 Corp=0.4 Secure=0.6 OT=0.8 |
| 6 | cvss_norm |
cvss_score / 10 |
| 7 | edr_active |
endpoint detection running |
GNN usage (PyTorch Geometric)¶
adj_matrix reshaped to (100, 100) is the adjacency matrix. Combined with node_features this is a complete graph:
import torch
from torch_geometric.data import Data
x = torch.tensor(obs['node_features']) # (100, 8)
adj = torch.tensor(obs['adj_matrix'].reshape(100, 100))
edge_index = adj.nonzero().t().contiguous() # (2, E)
data = Data(x=x, edge_index=edge_index)
Packet generation logic¶
Each tick the synthesizer derives packets from live simulation state — not random noise:
- C2 beacons — every compromised host generates a TCP/443 beacon toward the DMZ gateway
- Lateral movement — hosts with elevated privilege generate scan/exploit packets toward live targets
- Background — remaining slots fill with benign TCP/UDP traffic between online hosts
All values are in [0, 1] so no normalization is needed before passing to a neural network.