Thank you for your interest in contributing! We welcome bug reports, feature requests, and pull requests.
- Rust: Stable toolchain — install via rustup
- Python: 3.13 or higher
- uv: Fast Python package manager — install via astral.sh/uv
- cog: Conventional commit tool — install via
cargo install cocogitto
# 1. Clone the repository
git clone https://github.com/LaBackDoor/stackforge.git
cd stackforge
# 2. Sync Python dependencies
uv sync
# 3. Compile the Rust extension and install in editable mode
uv run maturin developRe-run
uv run maturin developany time you change Rust code.
stackforge/
├── crates/
│ ├── stackforge-core/ # Core Rust library (published to crates.io)
│ │ └── src/
│ │ ├── layer/ # Protocol implementations
│ │ │ ├── mod.rs # LayerKind enum, Layer trait, LayerEnum dispatch
│ │ │ ├── bindings.rs # Protocol detection rules (ethertype, port → layer)
│ │ │ ├── field.rs # Field abstractions (MacAddress, BytesField, etc.)
│ │ │ ├── ethernet.rs
│ │ │ ├── arp.rs
│ │ │ ├── ipv4/
│ │ │ ├── ipv6/
│ │ │ ├── tcp/
│ │ │ ├── udp/
│ │ │ ├── icmp/
│ │ │ ├── icmpv6/
│ │ │ ├── dns/
│ │ │ ├── http/
│ │ │ ├── http2/
│ │ │ ├── quic/
│ │ │ ├── l2tp/
│ │ │ ├── ssh/
│ │ │ ├── tls/
│ │ │ ├── dot11/ # 802.11 Wi-Fi
│ │ │ ├── dot15d4/ # 802.15.4 Zigbee
│ │ │ └── generic/ # Runtime custom protocols
│ │ ├── packet.rs
│ │ └── lib.rs
│ └── stackforge-automata/ # Async state machine framework (planned)
├── src/
│ └── lib.rs # PyO3 bindings
├── python/
│ └── stackforge/
│ ├── __init__.py # Python re-exports
│ └── custom.py # CustomLayer + typed fields
└── tests/
├── integration/ # Rust integration tests
├── python/ # Python tests (pytest)
│ └── scapy_compat/ # Scapy behavioural compatibility tests
├── uts/ # UTS regression test suites (Scapy-format)
└── sample_pcap/ # PCAP fixtures
# All unit + integration tests
cargo test
# Single test by name
cargo test test_name
# Single integration file
cargo test --test integration dns# All Python tests
uv run pytest tests/python
# Single file
uv run pytest tests/python/test_dns.py
# Single test
uv run pytest tests/python/test_dns.py::test_dns_query_build
# Scapy compatibility tests only
uv run pytest tests/python/scapy_compat
# Verbose output
uv run pytest -v tests/pythonUTS files in tests/uts/ are Scapy-format regression suites run via the Python test suite:
uv run pytest tests/python/scapy_compat/test_uts_integration.pyAll of the following must pass before submitting a PR:
# Rust
cargo fmt
cargo clippy
# Python
uv run ruff check .Follow the existing pattern used by layers like dns, http, or quic.
crates/stackforge-core/src/layer/<proto>/
├── mod.rs # Layer struct, parse(), fields, Layer trait impl
└── builder.rs # (optional) Builder for constructing packets
The layer struct holds start/end offsets into a shared Bytes buffer — it does not copy field values on parse. Fields are read on demand:
pub struct MyProtoLayer {
start: usize,
end: usize,
}
impl MyProtoLayer {
pub fn my_field<'a>(&self, buf: &'a [u8]) -> u16 {
u16::from_be_bytes(buf[self.start..self.start + 2].try_into().unwrap())
}
}In crates/stackforge-core/src/layer/mod.rs, add a variant to the LayerKind enum and handle it in LayerEnum.
In crates/stackforge-core/src/layer/bindings.rs, register how the new layer is detected (e.g., from an ethertype, IP protocol number, or TCP/UDP port).
If the layer needs Python-accessible fields, add them to the PyO3 bindings in src/lib.rs.
- Rust: Add
tests/integration/<proto>.rswith parse and build tests. - Python: Add
tests/python/test_<proto>.pycovering field access, builders, and edge cases. - UTS (optional): Add
tests/uts/<proto>.utsfor Scapy-format regression tests.
- Lazy Zero-Copy View —
Packet::parse()only records layer boundaries (offsets). Field values are read directly from the buffer when accessed, with no upfront copying. - Copy-on-Write Mutation — The underlying
Bytesbuffer is only cloned when a mutation occurs on a shared packet. Packet(raw)is unparsed — always call.parse()before accessing layers or fields.Packet::parse()assumes Ethernet first — Dot11 and Dot15d4 frames require a dedicated parse entry point.
This project uses Conventional Commits enforced by cocogitto (cog). All commits must follow the format:
<type>(<scope>): <description>
[optional body]
Common types:
| Type | When to use |
|---|---|
feat |
New feature or protocol |
fix |
Bug fix |
chore |
Maintenance, dependency updates, CI |
docs |
Documentation only |
test |
Adding or fixing tests |
refactor |
Code restructuring with no behaviour change |
perf |
Performance improvement |
Use cog commit instead of git commit to get an interactive prompt that validates the format:
cog commit feat "add OSPF layer"
cog commit fix "correct DNS name compression offset"Breaking changes must include a ! after the type or a BREAKING CHANGE: footer:
cog commit feat! "redesign Layer trait interface"Versions and the changelog are managed by cog. Maintainers cut releases with:
# Bump version automatically based on commit history (patch / minor / major)
cog bump --auto
# Or bump explicitly
cog bump --patch
cog bump --minor
cog bump --majorcog bump runs pre_bump_hooks (updates Cargo.toml version), tags the commit, and regenerates CHANGELOG.md from the commit history. Contributors do not need to edit CHANGELOG.md manually.
- Fork the repo and create your branch from
main. - Add tests for any new behaviour — both Rust and Python.
- Ensure all tests pass:
cargo testanduv run pytest tests/python. - Ensure linting passes:
cargo fmt && cargo clippy && uv run ruff check . - Open a pull request with a clear description of what changed and why.