INI Config Format
DECNET deployments are described by an INI file passed via --config. The file
is parsed by decnet/ini_loader.py (structural validation in
decnet/models.py::validate_ini_string) and converted into an IniConfig of
DeckySpec and CustomServiceSpec objects.
This page is an exhaustive reference. Every key documented here is accepted by the parser. Keys not listed are ignored (or cause a validation error for typed fields).
Related pages:
Section types
A DECNET INI file contains four kinds of sections:
[general]— fleet-wide network settings (optional, but recommended).[<decky-name>]— one decky (or a group of deckies ifamount=N).[<decky-name>.<service>]— persona/override config for one service on one decky (or group).[custom-<name>]— a bring-your-own service definition. See Custom services.
Section names must match ^[A-Za-z0-9\-_.]+$ (enforced on the decky name).
How the parser distinguishes a service subsection from a decky
A section with a dot in its name is treated as a per-service subsection
if and only if the last dotted segment is a known service name registered
in decnet.services.registry.all_services(). Otherwise the whole section
name is treated as a decky name.
Example: [decky-01.ssh] → subsection (because ssh is a registered service).
[decky.webmail] → decky section (because webmail is not a registered
service).
[general] section
Fleet-wide settings. All keys optional.
| Key | Type | Meaning |
|---|---|---|
net |
string | LAN CIDR for the decoy network, e.g. 192.168.1.0/24. Maps to IniConfig.subnet. |
gw |
string | LAN gateway IP. Maps to IniConfig.gateway. |
interface |
string | Host interface to bind MACVLAN/IPVLAN to, e.g. eth0, wlp6s0. |
Note:
log_targetis not an INI key. It is a CLI flag (--log-target HOST:PORT). You may leave a commented#log_target=reminder in your INI, but the parser will not read it.
Example:
[general]
net = 192.168.1.0/24
gw = 192.168.1.1
interface = eth0
Decky sections
Each non-general, non-custom-* section that is not a service subsection
defines a decky (or a group). The section name becomes the decky's name.
| Key | Type | Default | Meaning |
|---|---|---|---|
ip |
string | auto | Static IP inside the subnet. Forbidden when amount>1 — raises ValueError. |
services |
csv list | None |
Comma-separated service slugs. If omitted, falls back to the --randomize-services pool or the archetype's service list. |
archetype |
string | None |
Archetype slug (e.g. linux-server, windows-workstation). Archetypes pre-seed services, distro, and nmap_os. See Archetypes. |
amount |
int | 1 |
Number of deckies to spawn from this section. Must be 1..100. When >1, the section acts as a template and deckies are named <section>-01, <section>-02, …. |
nmap_os |
string | linux |
TCP/IP stack family for fingerprint spoofing. Accepted: linux, windows, embedded, bsd, cisco. Also accepted as nmap-os (alias). |
mutate_interval |
int | global | Auto-rotation interval in minutes (>=1). Also accepted as mutate-interval (alias). |
amount expansion rules
[corp-workstations]
archetype = windows-workstation
amount = 5
Expands to corp-workstations-01 … corp-workstations-05. Any
[corp-workstations.<service>] subsection propagates to all five
expanded deckies (see Resolution order below).
Setting ip= together with amount>1 is rejected — one IP cannot be shared.
Per-service subsections [<decky>.<service>]
Persona overrides for a single service on one decky (or a group). The last dotted segment must be a registered service slug — otherwise the subsection is ignored.
Every key inside such a subsection is passed through verbatim as a dict
into DeckySpec.service_config[<service>] and then consumed by the service
plugin's compose_fragment(..., service_cfg=...). The available keys are
therefore service-specific — see Service personas for
the full per-service key list.
Examples (excerpt from development/test-full.ini):
[decky-webmail.http]
server_header = Apache/2.4.54 (Debian)
response_code = 200
fake_app = wordpress
[decky-webmail.smtp]
smtp_banner = 220 mail.corp.local ESMTP Postfix (Debian/GNU)
smtp_mta = mail.corp.local
[decky-ldapdc.ssh]
ssh_version = OpenSSH_8.9p1 Ubuntu-3ubuntu0.6
kernel_version = 5.15.0-91-generic
users = root:toor,admin:admin123,svc_backup:backup2024
Resolution order
When the runtime configuration for a decky's service is built, values are layered in this order (later wins):
- Service defaults — hard-coded inside the service plugin.
- Archetype — if the decky's section had
archetype=…, the archetype's default services/distro/nmap_osapply. - Per-decky keys —
ip,services,nmap_os,mutate_intervalfrom the decky section override archetype defaults. - Per-service subsection — keys in
[<decky>.<service>]override the service's built-in defaults. For a group (amount>N), the subsection[<group>.<service>]is copied into every expanded decky.
Full realistic example
A 10-decky heterogeneous fleet, mixing an archetype pool with role-themed
boxes. This is a trimmed version of development/test-full.ini.
# full.ini — 10-decky heterogeneous fleet
#
# sudo decnet deploy --config full.ini --interface eth0 \
# --log-target 192.168.1.200:5140
[general]
net = 192.168.1.0/24
gw = 192.168.1.1
interface = eth0
# ── Archetype pool: 5 Windows workstations ───────────────────────────
[windows-workstation]
archetype = windows-workstation
amount = 5
# ── Web / Mail stack ─────────────────────────────────────────────────
[decky-webmail]
ip = 192.168.1.110
services = http, smtp, imap, pop3
nmap_os = linux
[decky-webmail.http]
server_header = Apache/2.4.54 (Debian)
fake_app = wordpress
[decky-webmail.smtp]
smtp_banner = 220 mail.corp.local ESMTP Postfix (Debian/GNU)
# ── Windows file server ──────────────────────────────────────────────
[decky-fileserv]
ip = 192.168.1.111
services = smb, ftp, tftp
nmap_os = windows
[decky-fileserv.smb]
workgroup = CORP
server_name = FILESERV01
os_version = Windows Server 2019
# ── Postgres / Mongo / Elastic ───────────────────────────────────────
[decky-dbsrv02]
ip = 192.168.1.113
services = postgres, mongodb, elasticsearch
nmap_os = linux
[decky-dbsrv02.postgres]
pg_version = 14.5
[decky-dbsrv02.elasticsearch]
es_version = 8.4.3
cluster_name = prod-search
# ── IoT / SCADA (embedded TCP stack) ─────────────────────────────────
[decky-iot]
ip = 192.168.1.117
services = mqtt, snmp, conpot
nmap_os = embedded
mutate_interval = 60
[decky-iot.snmp]
snmp_community = public
sys_descr = Linux router 5.4.0 #1 SMP x86_64
# ── Legacy admin box ─────────────────────────────────────────────────
[decky-legacy]
ip = 192.168.1.119
services = telnet, vnc, ssh
nmap_os = bsd
[decky-legacy.ssh]
ssh_version = OpenSSH_7.4p1 Debian-10+deb9u7
kernel_version = 4.9.0-19-amd64
users = root:root,admin:password,pi:raspberry
# ── Bring-your-own service (see Custom-Services) ─────────────────────
[custom-weirdapp]
binary = myorg/weirdapp:1.2.3
exec = /opt/weirdapp/run --listen 0.0.0.0:9000
ports = 9000
Deploy with:
sudo decnet deploy --config full.ini --interface eth0 \
--log-target 192.168.1.200:5140
Validation and errors
- Empty INI (no sections):
"The provided INI content must contain at least one section". - Total payload > 512 KB: rejected.
- At least one decky section is mandatory (
IniConfig.at_least_one_decky). amountmust be a positive integer<=100.ip=withamount>1is rejected.mutate_intervalmust be an integer.DeckySpecandCustomServiceSpecboth haveextra="forbid"— unknown typed fields on the parser's output raise Pydantic errors.
DECNET
User docs
- Quick-Start
- Installation
- Requirements-and-Python-Versions
- CLI-Reference
- INI-Config-Format
- Custom-Services
- Services-Catalog
- Service-Personas
- Archetypes
- Distro-Profiles
- OS-Fingerprint-Spoofing
- Networking-MACVLAN-IPVLAN
- Deployment-Modes
- SWARM-Mode
- MazeNET
- Remote-Updates
- Environment-Variables
- Teardown-and-State
- Database-Drivers
- Systemd-Setup
- Logging-and-Syslog
- Service-Bus
- Web-Dashboard
- REST-API-Reference
- Mutation-and-Randomization
- Troubleshooting
Developer docs
DECNET — honeypot deception-network framework. Pre-1.0, active development — use with caution. See Sponsors to support the project. Contact: samuel@securejump.cl