1
Developer Guide
anti edited this page 2026-04-18 06:07:04 -04:00

Developer Guide

How to hack on DECNET. If you just want to deploy it, see Home and INI-Config-Format instead.

Environment setup

DECNET pins its runtime deps in requirements.lock. Always work inside the project virtualenv — do not install into the system interpreter.

cd /path/to/DECNET
python -m venv .venv
source .venv/bin/activate
pip install -e .

Every subsequent shell must source .venv/bin/activate before running pip, pytest, or decnet. The CLI entrypoint is registered in pyproject.toml and resolves to decnet.cli:app.

To confirm the dev install:

decnet services      # list registered service plugins
decnet distros       # list base-image archetypes
pytest -q            # run the suite

Repository layout

High-level tour. Only the directories you will touch often are listed.

Path What lives there
decnet/cli.py Typer app. Every decnet <verb> subcommand is defined here.
decnet/services/ Service plugins. One file per honeypot service. See Writing-a-Service-Plugin.
decnet/services/base.py BaseService contract.
decnet/services/registry.py Auto-discovery of BaseService subclasses.
decnet/composer.py Turns a fleet spec into a docker-compose file.
decnet/fleet.py Fleet planning: which decky runs which services on which IP.
decnet/archetypes.py, decnet/distros.py OS personas + base-image selection.
decnet/os_fingerprint.py TCP/IP stack tuning to bend nmap fingerprints toward a chosen persona.
decnet/env.py Central env-var parsing (DECNET_DB_TYPE, DECNET_EMBED_*, …).
decnet/collector/ Syslog / RFC 5424 ingest worker.
decnet/correlation/ Session and attacker correlation worker.
decnet/profiler/ Attacker profiler. Embeddable or standalone — see Design-Overview.
decnet/sniffer/ Passive PCAP sniffer worker. Same embed/standalone split.
decnet/mutator/ Runtime mutation of the decoy fleet.
decnet/prober/ Active probe / realism checker.
decnet/engine/ Deploy / teardown orchestration.
decnet/web/ FastAPI app + dashboard + repository layer.
decnet/web/db/ SQLModelRepository base and sqlite/, mysql/ subclasses. See Database-Drivers.
decnet/logging/ RFC 5424 emitters and the syslog bridge used by service containers.
templates/<slug>/ Dockerfile + service config bundle built into the service image.
tests/ Pytest suite. Mirrors the decnet/ tree loosely.
development/ Low-level design notes and generated graphs. Not shipped.

Coding conventions

Lint and static checks

  • ruff is the single source of truth for style. Config lives in ruff.toml. Run ruff check decnet tests before committing.
  • bandit is used for security linting of decnet/. Fix findings rather than silencing them; if a silence is unavoidable, scope the # nosec comment to one line and explain why.

Stealth in probes and banners

Never reveal DECNET identity in anything an attacker can see. That means:

  • No User-Agent: DECNET/... in the prober or in any service plugin.
  • No banners, MOTDs, /etc/issue contents, HTTP Server: headers, or SSH version strings that mention DECNET, honeypot, decoy, fake, or any internal codename.
  • No log filenames or env var names leaking into emitted service output.

This rule is load-bearing. A single leaked banner turns the whole fleet into a well-known signature.

Dependency injection for storage

Do not from decnet.web.db.sqlite.repository import SQLiteRepository in new code. Ever.

  • In workers / CLI / library code: call get_repository() from decnet/web/db/factory.py. It reads DECNET_DB_TYPE and returns the right backend, already wrapped with telemetry.
  • In FastAPI route handlers: take repo: BaseRepository = Depends(get_repo) — defined in decnet/web/dependencies.py. This keeps the test harness able to swap in an in-memory repo.

The direct-import rule is enforced by convention and by reviewer. If you find an old direct import while working on a file, fix it in the same commit.

See Database-Drivers for how SQLite and MySQL subclasses differ.

Tests

Layout

  • tests/ — fast unit tests. Run by default.
  • tests/api/ — FastAPI TestClient tests.
  • tests/docker/ — integration tests that spin real containers. Opt-in.
  • tests/live/ — full end-to-end against a live deploy. Opt-in.
  • tests/perf/, tests/stress/ — performance and soak. Opt-in.
  • tests/service_testing/ — per-service plugin smoke tests.
  • tests/conftest.py — shared fixtures, including repo factories.

Running

pytest -q                          # fast suite
pytest tests/api -q                # just the API
pytest tests/service_testing -q    # plugin smoke
pytest -k ssh                      # single topic

Rules

  • Every new feature ships with pytest coverage. No exceptions.
  • Never hand off code that is not running or not 100% green. If you cannot finish the tests, say so — do not push.
  • Do not use scapy's sniff() inside a TestClient lifespan test. The sniff thread hangs pytest teardown. Use static source inspection or a fake socket instead.

Commit style

  • Follow the existing log: short imperative subject, scope: prefix when obvious (feat(sniffer):, fix(web-ui):, test(ssh):, chore:).
  • Run the relevant pytest subset before committing. A broken main is worse than a late commit.
  • Never add Co-Authored-By: or any Claude / AI attribution trailer.
  • Prefer a new commit over --amend. Hooks that fail leave you in a half-state; amending there hides work.