11 KiB
DECNET — Technical Debt Register
Last updated: 2026-04-09 — All addressable debt cleared. Severity: 🔴 Critical · 🟠 High · 🟡 Medium · 🟢 Low
🔴 Critical
DEBT-001 — Hardcoded JWT fallback secret ✅ RESOLVED
File: decnet/env.py:15
Fixed in commit b6b046c. DECNET_JWT_SECRET is now required; startup raises ValueError if unset or set to a known-bad value.
DEBT-002 — Default admin credentials in code ✅ CLOSED (by design)
DECNET_ADMIN_PASSWORD defaults to "admin" intentionally — the web dashboard enforces a password change on first login (must_change_password=1). Startup enforcement removed as it broke tooling without adding meaningful security.
DEBT-003 — Hardcoded LDAP password placeholder ✅ CLOSED (false positive)
templates/ldap/server.py:73 — "<sasl_or_unknown>" is a log label for SASL auth attempts, not an operational credential. The LDAP template is a honeypot; it has no bind password of its own.
DEBT-004 — Wildcard CORS with no origin restriction ✅ RESOLVED
File: decnet/web/api.py:48-54
Fixed in commit b6b046c. allow_origins now uses DECNET_CORS_ORIGINS (env var, defaults to http://localhost:8080). allow_methods and allow_headers tightened to explicit allowlists.
🟠 High
DEBT-005 — Auth module has zero test coverage ✅ RESOLVED
File: decnet/web/auth.py
Comprehensive test suite added in tests/api/ covering login, password change, token validation, and JWT edge cases.
DEBT-006 — Database layer has zero test coverage ✅ RESOLVED
File: decnet/web/sqlite_repository.py
tests/api/test_repository.py added — covers log insertion, bounty CRUD, histogram queries, stats summary, and fuzz testing of all query paths. In-memory SQLite with StaticPool ensures full isolation.
DEBT-007 — Web API routes mostly untested ✅ RESOLVED
Files: decnet/web/router/ (all sub-modules)
Full coverage added across tests/api/ — fleet, logs, bounty, stream, auth all have dedicated test modules with both functional and fuzz test cases.
DEBT-008 — Auth token accepted via query string ✅ RESOLVED
File: decnet/web/dependencies.py:33-34
Query-string token fallback removed. get_current_user now accepts only Authorization: Bearer <token> header. Tokens no longer appear in access logs or browser history.
DEBT-009 — Inconsistent and unstructured logging across templates ✅ CLOSED (false positive)
All service templates already import from decnet_logging and use syslog_line() for structured output. The print(line, flush=True) present in some templates is the intentional Docker stdout channel for container log forwarding — not unstructured debug output.
DEBT-010 — decnet_logging.py duplicated across all 19 service templates ✅ RESOLVED
decnet_logging.py duplicated across all 19 service templatesFiles: templates/*/decnet_logging.py
All 22 per-directory copies deleted. Canonical source lives at templates/decnet_logging.py. deployer.py now calls _sync_logging_helper() before docker compose up — it copies the canonical file into each active template build context automatically.
🟡 Medium
DEBT-011 — No database migration system
File: decnet/web/db/sqlite/repository.py
Schema is created during startup via SQLModel.metadata.create_all. There is no Alembic or equivalent migration layer. Schema changes across deployments require manual intervention or silently break existing databases.
Status: Architectural. Deferred — requires Alembic integration and migration history bootstrapping.
DEBT-012 — No environment variable validation schema ✅ RESOLVED
File: decnet/env.py
DECNET_API_PORT and DECNET_WEB_PORT now validated via _port() — enforces integer type and 1–65535 range, raises ValueError with a clear message on bad input.
DEBT-013 — Unvalidated input on decky_name route parameter ✅ RESOLVED
decky_name route parameterFile: decnet/web/router/fleet/api_mutate_decky.py:10
decky_name now declared as Path(..., pattern=r"^[a-z0-9\-]{1,64}$") — FastAPI rejects non-matching values with 422 before any downstream processing.
DEBT-014 — Streaming endpoint has no error handling ✅ RESOLVED
File: decnet/web/router/stream/api_stream_events.py
event_generator() now wrapped in try/except. asyncio.CancelledError is handled silently (clean disconnect). All other exceptions log server-side via log.exception() and yield an event: error SSE frame to the client.
DEBT-015 — Broad exception detail leaked to API clients ✅ RESOLVED
File: decnet/web/router/fleet/api_deploy_deckies.py:78
Raw exception message no longer returned to client. Full exception now logged server-side via log.exception(). Client receives generic "Deployment failed. Check server logs for details.".
DEBT-016 — Unvalidated log query parameters ✅ RESOLVED
File: decnet/web/router/logs/api_get_logs.py:12-19
search capped at max_length=512. start_time and end_time validated against ^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}$ regex pattern. FastAPI rejects invalid input with 422.
DEBT-017 — Silent DB lock retry during startup ✅ RESOLVED
File: decnet/web/api.py:20-26
Each retry attempt now emits log.warning("DB init attempt %d/5 failed: %s", attempt, exc). After all retries exhausted, log.error() is emitted so degraded startup is always visible in logs.
DEBT-018 — No Docker HEALTHCHECK in any template ✅ RESOLVED
Files: All 20 templates/*/Dockerfile
All 24 Dockerfiles updated with:
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD kill -0 1 || exit 1
DEBT-019 — Most template containers run as root ✅ RESOLVED
Files: All templates/*/Dockerfile except Cowrie
All 24 Dockerfiles now create a decnet system user, use setcap cap_net_bind_service+eip on the Python binary (allows binding ports < 1024 without root), and drop to USER decnet before ENTRYPOINT.
DEBT-020 — Swagger/OpenAPI disabled in production ✅ RESOLVED
File: decnet/web/api.py:43-45
All route decorators now declare responses={401: {"description": "Not authenticated"}, 422: {"description": "Validation error"}}. OpenAPI schema is complete for all endpoints.
DEBT-021 — sqlite_repository.py is a god module ✅ RESOLVED
sqlite_repository.py is a god module~File: decnet/web/sqlite_repository.py (400 lines)
Fully refactored to decnet/web/db/ modular layout: models.py (SQLModel schema), repository.py (abstract base), sqlite/repository.py (SQLite implementation), sqlite/database.py (engine/session factory). Commit de84cc6.
DEBT-026 — IMAP/POP3 bait emails not configurable via service config
Files: templates/imap/server.py, templates/pop3/server.py, decnet/services/imap.py, decnet/services/pop3.py
Bait emails are hardcoded. A stub env var IMAP_EMAIL_SEED is read but currently ignored. Full implementation requires:
IMAP_EMAIL_SEEDpoints to a JSON file with a list of{from_, to, subject, date, body}dicts.templates/imap/server.pyloads and merges/replaces_BAIT_EMAILSfrom that file at startup.decnet/services/imap.pycompose_fragment()readsservice_cfg["email_seed"]and injectsIMAP_EMAIL_SEED+ a bind-mount for the seed file into the compose fragment.- Same pattern for POP3 (
POP3_EMAIL_SEED).
Status: Stub in place — full wiring deferred to next session.
DEBT-027 — Dynamic Bait Store
Files: templates/redis/server.py, templates/ftp/server.py
The bait store and honeypot files are hardcoded. A dynamic injection framework should be created to populate this payload across different honeypots.
Status: Deferred — out of current scope.
DEBT-028 — Test coverage for api_deploy_deckies.py
File: decnet/web/router/fleet/api_deploy_deckies.py (24% coverage)
The deploy endpoint exercises Docker Compose orchestration via decnet.engine.deploy, which creates MACVLAN/IPvlan networks and runs docker compose up. Meaningful tests require mocking the entire Docker SDK + subprocess layer, coupling tightly to implementation details.
Status: Deferred — test after Docker-in-Docker CI is available.
🟢 Low
DEBT-022 — Debug print() in correlation engine ✅ CLOSED (false positive)
print() in correlation enginedecnet/correlation/engine.py:20 — The print() call is inside the module docstring as a usage example, not in executable code. No production code path affected.
DEBT-023 — Unpinned base Docker images
Files: All templates/*/Dockerfile
debian:bookworm-slim and similar tags are used without digest pinning. Image contents can silently change on docker pull, breaking reproducibility and supply-chain integrity.
Status: Deferred — requires docker pull access to resolve current digests for each base image.
DEBT-024 — Stale service version hardcoded in Redis template ✅ RESOLVED
File: templates/redis/server.py:15
REDIS_VERSION updated from "7.0.12" to "7.2.7" (current stable).
DEBT-025 — No lock file for Python dependencies ✅ RESOLVED
Files: Project root
requirements.lock generated via pip freeze. Reproducible installs now available via pip install -r requirements.lock.
Summary
| ID | Severity | Area | Status |
|---|---|---|---|
| ✅ | Security / Auth | resolved b6b046c |
|
| ✅ | Security / Auth | closed (by design) | |
| ✅ | Security / Infra | closed (false positive) | |
| ✅ | Security / API | resolved b6b046c |
|
| ✅ | Testing | resolved | |
| ✅ | Testing | resolved | |
| ✅ | Testing | resolved | |
| ✅ | Security / Auth | resolved | |
| ✅ | Observability | closed (false positive) | |
| ✅ | Code Duplication | resolved | |
| DEBT-011 | 🟡 Medium | DB / Migrations | deferred (Alembic scope) |
| ✅ | Config | resolved | |
| ✅ | Security / Input | resolved | |
| ✅ | Reliability | resolved | |
| ✅ | Security / API | resolved | |
| ✅ | Security / API | resolved | |
| ✅ | Reliability | resolved | |
| ✅ | Infra | resolved | |
| ✅ | Security / Infra | resolved | |
| ✅ | Docs | resolved | |
| ✅ | Architecture | resolved de84cc6 |
|
| ✅ | Code Quality | closed (false positive) | |
| DEBT-023 | 🟢 Low | Infra | deferred (needs docker pull) |
| ✅ | Infra | resolved | |
| ✅ | Build | resolved | |
| DEBT-026 | 🟡 Medium | Features | deferred (out of scope) |
| DEBT-027 | 🟡 Medium | Features | deferred (out of scope) |
| DEBT-028 | 🟡 Medium | Testing | deferred (needs DinD CI) |
Remaining open: DEBT-011 (Alembic), DEBT-023 (image pinning), DEBT-026 (modular mailboxes), DEBT-027 (Dynamic bait store), DEBT-028 (deploy endpoint tests) Estimated remaining effort: ~12 hours