Crafty sends packets through a real network interface, reacts to responses and lets programs declare limits on their traffic. Introduced on October 28, 2026, the statically typed language supports tasks such as checking ports, measuring round trips and investigating packet loss or TCP behavior. Its manual in the Robinet repository documents built-in protocol stacks alongside bit-level packet construction.
Project author rixed describes it on Hacker News as a “purpose-built packet language with guardrails.” The useful question is what those guardrails actually constrain. Crafty provides duration, packet, byte, rate and SYN budgets, plus allowed TCP and UDP source-port ranges. Those controls make experiments easier to bound, but their scope matters as much as their presence.
Sending Packets and Matching Their Responses
Crafty represents protocol stacks as layers separated by slashes. A stack such as /IP(dst=192.0.2.10)/UDP(dst=7) selects an IPv4 destination and UDP destination port. Ethernet is implicit, and a block establishes that stack as the context for subsequent instructions.
This shortened version of the manual’s introductory example sends three payloads and waits for replies:
/IP(dst=192.0.2.10)/UDP(dst=7) {
repeat var=I count=3 every=100ms {
Raw("ping ${I}")
expect timeout=1s Raw {
print(MATCH[Raw].data)
}
}
}
Raw is a terminal layer: adding it completes the stack and sends the payload. The loop requests three repetitions at a 100-millisecond interval. Each expect waits up to one second for matching response data, which becomes available through MATCH.
This example is explanatory, not an executed test. Unlike the manual’s full example, it omits the catch block that tolerates a missing reply. Here, an unhandled timeout ends the program. The manual explains both timeout handling and stack semantics.
The important detail is that a stack represents streams, not merely a packet template. UDP chooses its source port once from the permitted range, and subsequent messages and relative response patterns remain associated with that stream.
Relative stacks reuse the current stream; absolute stacks start anew. A relative ../ICMP pattern can match an ICMP error associated with the current IP/UDP stream. An absolute pattern has broader scope.
I’d treat this distinction as one of Crafty’s most useful design choices. Keeping a probe and its response matcher in the same stream reduces the bookkeeping an agent would otherwise need to generate. It also makes a program’s intent easier to inspect than a sequence of loosely connected send-and-capture commands.
Mixing Automatic Stacks With Manual Packets
Crafty includes stacks for ARP, IPv4, TCP, UDP, ICMP and DNS. Its self-driven layers handle protocol work such as address resolution, connection establishment, retransmission, fragmentation and reassembly.
Adding ! makes a layer manual. For example, TCP!(flags=SYN, ack_num=0) specifies a TCP segment directly, independently of the connection’s state. On reception, IP! exposes individual fragments and TCP! exposes individual segments, including segments without payload data.
The manual also documents mixed stacks, allowing automatic behavior where it is useful and manual control where an experiment needs it. Capturing a sent message into a packet value requires a fully manual message.
That division serves two different jobs. Automatic stacks support ordinary service checks without requiring an agent to implement TCP. Manual layers let a program examine specific flags, fragments and unusual protocol behavior. The latter capability makes traffic budgets especially consequential: fine-grained packet control still needs limits on how much traffic a program can produce.
What the Traffic Budgets Actually Cover
Crafty’s documented controls cover several independent dimensions of an experiment:
| Control | What it constrains | Practical purpose |
|---|---|---|
| Duration budget | How long an experiment can run | Bounds experiments that keep waiting or repeating |
| Packet budget | Packet count | Limits the number of packets generated |
| Byte budget | Traffic volume | Adds a volume constraint alongside packet count |
| Rate budget | Traffic generation rate | Restricts how quickly traffic is produced |
| SYN budget | SYN count | Separately bounds TCP connection-initiation traffic |
| TCP/UDP source-port ranges | Permitted source ports | Keeps streams within an explicitly selected local range |
These controls address different failure modes. A packet-count ceiling alone cannot express both a total-volume limit and a sending-rate limit. A duration limit gives an experiment an endpoint, while a SYN budget provides a separate constraint relevant to TCP connection attempts.
The source-port restriction deserves particular attention. The manual demonstrates --udp 40000-40010, which supplies the range from which UDP streams choose their source ports. The program separately specifies destination port 7 through UDP(dst=7).
That means a source-port range is not a destination-port allowlist. It organizes and constrains the local side of the experiment; it does not, by itself, express which remote services an agent may contact.
My view is that this is the most consequential detail for deployment planning. A narrow-looking port range can be mistaken for a narrow target policy. Crafty’s documented source-port control and an operator’s destination-access policy solve different problems.
Compile Checks and Exit Codes Support Agent Wrappers
Crafty’s CLI includes compile-only checking, packet-printing formats, statistics and examples. It also distinguishes compile errors from budget stops through its exit codes, according to the manual.
Frequently Asked Questions
3 questions
1Does Crafty Send Real Network Traffic?
Yes. Its manual describes sending through a real interface and capturing responses. Compile-only checking is available before a live run.





