# SPEC — DonjonSec Scanner: Audit, Reconciliation & Fix-to-100%

**Date:** 2026-05-28
**Author:** Cris (DonjonSec) + Claude
**Status:** Draft v2 — verified current state folded in; awaiting review
**Methodology:** SDD (Specify → Plan → Tasks → Implement)
**Canonical tree (post-reconciliation):** `C:\Donjonsec` (history adopted from `donjon-platform-clean`, Forgejo remote `192.0.2.116:3000`)
**Production goal:** something in production and *for sale* soon — so launch-blockers are prioritized over completeness.

---

## 1. Problem

Prospective buyers are asking about the DonjonSec scanner, which is sold on donjonsec.com with a free **Community Edition** and paid Pro/Enterprise/MSSP tiers. The product must (a) gate licenses correctly, (b) perform as advertised, (c) operate airgapped, and (d) ship up-to-date scanner definitions. Verification on 2026-05-28 shows the *architecture supports all four* but specific gaps block sale and contradict the storefront. This spec reconciles the live storefront against demonstrated capability and drives the product to a verified, sellable 100%.

## 1A. Verified Current State (2026-05-28)

Established by four parallel read-only audits (live site crawl, license-gating code, airgap code, definition freshness). **Caveat:** the Windows tree is a *frozen mirror*; live data/cron live on **CT100 `/opt/donjon-platform`** — anything data/automation-related must be re-verified there.

**License gating — VERDICT: PARTIAL.** Crypto core (Ed25519 + ML-DSA-65 dual-sig, fail-closed to Community) and API-layer gates (scan depth, 16-target cap, scheduled scans, export formats, AI quota, SSO/RBAC/tenant/MSSP routes) are **real and enforced**. But:
- **Embedded public keys are PLACEHOLDERS** (`lib/licensing.py:79-123`) → no real v2 paid license can verify → every install silently runs Community. **This is why nothing can be sold today.**
- **Trial system self-grants Pro, unsigned, infinitely renewable** (`lib/trial_license.py`; guard is a deletable `.trial-used` marker) → free unlimited Pro.
- **Compliance-framework cap (3) NOT enforced in scan path** → Community gets all 31 frameworks (revenue leak + contradicts pricing).
- **Scan throttle / history-retention / save-projects defined but never applied.**
- **`/api/license/*` router is dead** (calls non-existent methods → AttributeError; `web/api_license.py:39-87`).
- Scanner-set gating is absent, but the site *sells* "all 19 scanners free in CE", so that is **consistent** — not a bug.

**Airgap — VERDICT: PARTIAL, holds for core.** `DONJON_OFFLINE=1` flag exists and is tested; license validation, scanning against bundled DBs (673 MB vuln + 218 MB intel), compliance, reporting, and local AI (Ollama→template fallback) all run offline with no activation server or CDN. Gaps: **ASM scanner is not offline-guarded** (`scanners/asm_scanner.py:284,686,973,1079` → crt.sh/DoH calls, 30s timeouts), and bundled data is ~2 months stale. "Air-gap ready" is **defensible but should be qualified**: core runs fully offline; live threat-intel enrichment needs periodic bundle imports.

**Scanner definitions — VERDICT: STALE + false-green safety net.** All NVD/feed data is **66–68 days stale** (frozen 2026-03-22) in this tree; live freshness must be checked on CT100. The recent NVD-staleness fix (`469b475`) landed the *code* but: the **dashboard staleness indicator is a stub that always returns "ok"** (`web/api_intel.py:27-49`) — same silent-staleness class moved to the UI; the **watchdog/heartbeat the fix promised was never built**; `--check` has an **NVD age blind spot**; `weekly-intel-update.sh` has `exit 0` bypass paths and is not version-controlled in cron; compliance-framework auto-update is unwired; abuse.ch feed never populated.

**Live site claims — no deploy drift, but internal inconsistencies.** Live site == local Astro source on all headline numbers. Inconsistencies to reconcile: scanner *names* differ between /features/ (lists MCP + Orchestrator) and /docs/ (lists Adversary; table renders only **18** rows) though both claim 19; framework count **31 vs 30**; AI providers **16 claimed but only 7 shown** on /features/; trial endpoint port **9090 vs 5000** elsewhere. "168 tests passing" / "red team validated" do **not** appear on the live site.

**Community Edition (storefront, verbatim):** Free forever — 16 scan targets/session, 3 compliance frameworks, 10 AI queries/day, CSV+JSON export, 1 user, **all 19 scanners**, FAIR risk quantification. Paid tiers unlock unlimited targets, all frameworks, all export formats, scheduled scans, SSO/RBAC/multi-tenant/MSSP.

## 2. Goals

- **G1** — One canonical tree at `C:\Donjonsec`, freshest history, Forgejo remote, clean working tree.
- **G2** — Verified **status matrix** for all 19 scanners (run against real targets).
- **G3** — Live-site claims reconciled to verified reality, incl. the named internal inconsistencies (scanner names, 30↔31, AI 7↔16, port 9090↔5000).
- **G4** — Standing **issue/fix pipeline** with error-replication protocol + failures-are-wins documentation.
- **G5** — **Real-target test harness** (CT107/108/VM200/CT102–104 on `vmbr1`) — no mocks — gating every fix.
- **G6** — **100% reached and proven:** test suite genuinely green, customer journey 16/16, claims reconciled.
- **G7 (NEW, launch-blocker)** — **License gating sellable:** real keys embedded, paid licenses validate, trial integrity restored, framework cap enforced, dead license router fixed.
- **G8 (NEW)** — **Airgap claim true:** `DONJON_OFFLINE` is comprehensive (ASM guarded), claim qualified accurately, verified with zero-egress proof.
- **G9 (NEW)** — **Definitions fresh + honestly monitored:** automation verified running on CT100, staleness indicator reports truth, watchdog exists.

## 3. Non-Goals

- New scanner features / net-new scanners (audit + fix-to-working only).
- A full architectural rewrite — **verification shows this is NOT warranted** (see §4 D8).
- Deleting any tree or data (archive only; deletion needs separate explicit approval).

## 4. Decisions

| # | Decision | Rationale |
|---|---|---|
| D1 | Canonical = `clean` history at `C:\Donjonsec` path + Forgejo remote | `clean` 2 mo newer, +`mcp`/`quantum`, matches Forgejo-canonical direction |
| D2 | Archive other two trees (`*-archive-20260528`), do not delete | Standing no-destructive-action rule |
| D3 | Done = test suite green AND journey 16/16 AND claims reconciled | Most rigorous, buyer-facing trust |
| D4 | Scope = all 19 scanners validated against real targets | No fake features |
| D5 | Issue ledger = in-repo markdown (truth) + Forgejo issues mirror | Air-gap-safe + stakeholder-visible |
| D6 | Blocked-infra scanners decided per-scanner at audit time | Judge stand-up vs defer on real cost |
| D7 | SP1 audit runs as multi-agent workflow (one auditor per scanner) | Parallel fan-out; fastest to matrix (explicit opt-in) |
| **D8 (NEW)** | **Targeted hardening, NOT full refactor** | Verified architecture is sound (real crypto, real API gates, real offline tooling); issues are specific gaps |
| **D9 (NEW)** | **License keys: LOCATE original private keys before any keygen** | Standing rule — never replace embedded keys / generate new pairs without finding originals first (see reference_license_keys) |
| **D10 (NEW)** | **Data/automation findings re-verified on CT100, not the Windows mirror** | This tree is a frozen mirror; live state differs |
| **D11 (NEW)** | **Sequence: clear P0 launch-blockers → sellable → then 19-scanner audit/journey** | "For sale soon" — fastest path to revenue without skipping rigor |
| **D12 (NEW)** | **Locate original signing keys via reference_license_keys memory + repo before keygen** | Honors never-replace-keys rule (D9); discovery is read-only first |

## 5. PRODUCTION LAUNCH-BLOCKERS (priority order — gate "for sale")

| P | Blocker | Why it blocks sale | First action |
|---|---|---|---|
| **P0** | Placeholder license public keys → no paid license validates | Cannot sell a paid tier that won't activate | Locate original keypair (D9); embed real public keys; sign + verify a Pro license end-to-end |
| **P0** | Trial self-grants infinite free Pro | Revenue integrity; "14-day" is meaningless | Sign trial licenses; durable one-time guard tied to real fingerprint |
| **P0** | Compliance-framework cap (3) unenforced | CE gives away all 31; contradicts pricing | Enforce cap in scan/compliance path |
| **P0** | Definitions 66–68 days stale + staleness stub lies "ok" | "327K CVEs / up-to-date" claim false; safety net fake | Verify/repair automation on CT100; make staleness indicator report truth; build watchdog |
| **P1** | Site internal inconsistencies (names, 30↔31, 7↔16, ports) | Prospect-facing credibility | Reconcile site to verified product reality |
| **P1** | Dead `/api/license/*` router | Broken endpoint a prospect/customer hits | Fix or remove router |
| **P1** | ASM scanner not offline-guarded | Undermines "air-gap ready" | Add `DONJON_OFFLINE` guard; qualify claim |
| **P2** | Customer journey 12/16 | End-to-end demo path | Pipeline (SP3) |
| **P2** | All-19-scanner functional validation | "Perform as advertised" | Workflow audit (SP1) + pipeline |

## 6. Sub-projects & sequencing

```
SP0 Reconciliation ─┬─> SP1 Audit/Triage (19 scanners, workflow) ─┐
 (prerequisite)     ├─> SP2 Harness + issue/fix pipeline          ├─> SP3 Drive-to-green
                    └─> SP-LIC License/trial/framework-cap (P0)    │   (test suite green,
                        SP-FRESH Definition automation on CT100 (P0)│    journey 16/16,
                        SP-AIR Airgap hardening (P1)                │    claims reconciled,
                        SP-CLAIM Site reconciliation (P1) ──────────┘    blockers cleared)
```

P0 work (SP-LIC, SP-FRESH) runs in parallel with SP1/SP2 since it touches different subsystems.

## 7. Scanner → real-target mapping (draft, finalized in SP1)

| Scanner(s) | Real target |
|---|---|
| network, vulnerability, web, ssl, quantum | CT108 (DVWA / TLS) + CT102–104 |
| linux, compliance (CIS) | CT102 Debian / CT103 Ubuntu / CT104 Rocky |
| windows, ad | VM200 windows-test |
| container, sbom | Docker on CT101 / CT108 |
| malware | EICAR test string |
| credential, shadow_ai, asm, adversary, mcp | scoped fixtures on `vmbr1` |
| cloud, openvas | ⚠️ may need infra not yet present — flag BLOCKED, decide per D6 |

## 8. Acceptance Criteria (Given/When/Then — all require real execution)

**AC1 — Canonical tree.** *Given* three diverged trees, *when* SP0 completes, *then* `C:\Donjonsec` is on `main`, Forgejo remote, clean tree, all 19 scanners, backup bundle + archives on disk.

**AC2 — Status matrix.** *Given* canonical tree, *when* SP1 completes, *then* every one of 19 scanners has a status set by **running it**, not reading code.

**AC3 — Claims reconciled.** *Given* the verified matrix, *when* site/README updated, *then* no claim (scanner count/names, framework count, AI count, ports, capability) exists that can't be demonstrated; the named inconsistencies are resolved.

**AC4 — Pipeline rigor.** *Given* a failure, *when* it enters the pipeline, *then* reproduced **three ways** before any fix, closed **only** with real-target evidence + documented lesson.

**AC5 — Real-target gate.** *Given* a proposed fix, *when* validated, *then* the scanner runs against its mapped `vmbr1` target and produces correct output; mock-only does not satisfy.

**AC6 — 100% reached.** *Given* full pipeline, *when* SP3 completes, *then* test suite genuinely green, journey 16/16 against real targets, AC3 holds.

**AC7 — License sellable.** *Given* real embedded keys, *when* a signed Pro license is installed, *then* `get_tier()=="pro"`, paid features unlock, and an unsigned/forged license fails closed to Community. Trial issues a signed time-boxed license that cannot be renewed past its one-time grant on the same fingerprint.

**AC8 — CE caps enforced.** *Given* a Community install, *when* a scan/compliance run exceeds 3 frameworks or 16 targets, *then* it is blocked exactly as the pricing page states.

**AC9 — Airgap true.** *Given* `DONJON_OFFLINE=1` on an egress-denied host, *when* a full scan (incl. ASM) + AI analysis + report export run, *then* zero outbound connections to public IPs occur (tcpdump-proven) and the product functions.

**AC10 — Definitions honest.** *Given* the live CT100 install, *when* the staleness indicator is read, *then* it reflects true data age; *and* a watchdog flags a missed update within its window; *and* a forced incremental update fetches genuinely fresh CVEs (new `success` row, current window).

## 9. Risks / what would make this fail (inverse thinking)

- **Selling a license that can't activate** — mitigated by P0 key embedding + AC7 end-to-end signing test.
- **Silent staleness reopening at the UI layer** — mitigated by AC10 (truthful indicator + watchdog), not just the code fix.
- **Replacing keys and orphaning the signing pipeline** — mitigated by D9 (locate originals first).
- **Blocked-infra scanners faking pass** — explicit 🚫 BLOCKED, never counted green.
- **Reconciliation data loss** — bundle backups + archive-not-delete before any reset.
- **Shipping before claims reconciled** — D3 makes claim accuracy part of done.

## 10. Artifacts (under `docs/plans/2026-05-28-scanner-audit/`)

| File | Purpose |
|---|---|
| `SPEC.md` | this document |
| `PLAN.md` | technical plan (writing-plans output) |
| `TASKS.md` | atomic, one-session tasks |
| `STATUS_MATRIX.md` | per-scanner verified status |
| `ISSUE_LOG.md` | issue ledger (truth; mirrored to Forgejo) |
| `decision-log.md` | numbered decisions w/ rationale |
| `VERIFICATION-2026-05-28.md` | full findings from the four audits (license/airgap/freshness/claims) |
