-
Notifications
You must be signed in to change notification settings - Fork 0
docs(adr): accepted architecture decisions with verified APA 7th citations #111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
seonghobae
wants to merge
5
commits into
main
Choose a base branch
from
cursor/docs-adr-accepted-decisions-289d
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
2eabad4
docs(adr): record accepted architecture decisions with APA 7th citations
cursoragent 5dea790
Merge remote-tracking branch 'origin/main' into HEAD
seonghobae 86277d5
docs(adr): reconcile accepted SOC orchestration
seonghobae 95f2719
docs(adr): align fuzzing preprint citation
seonghobae 43369a8
docs(adr): align accepted runtime boundaries
seonghobae File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,59 @@ | ||
| # ADR 0001: Standalone Rust gateway with in-workspace `waf-ids-core` | ||
|
|
||
| - Status: Accepted | ||
| - Date: 2026-08-25 | ||
| - Recorded from: current `main` (`README.md` workspace notes; | ||
| `docs/architecture.md` components and security boundaries; root | ||
| `Cargo.toml` workspace members) | ||
|
|
||
| ## Context | ||
|
|
||
| wardnet (crate name `waf-ids-ai-soc`) is the WAF / IDS / AI SOC gateway | ||
| and control-plane leaf for ContextualWisdomLab. Operators need a binary | ||
| that starts, serves management and gateway HTTP, and scores requests | ||
| without checking out sibling products. | ||
|
|
||
| Domain logic (models, validation, upserts, scoring, DNSBL zone text, | ||
| event retention, feed freshness, KPI snapshots) is reusable. Splitting | ||
| that logic into a git submodule before an independently versioned | ||
| engine or SDK exists would add release and review overhead without an | ||
| external consumer. | ||
|
|
||
| Cargo workspaces keep multiple packages on one lockfile and one | ||
| `cargo test --workspace` surface (The Cargo Book, n.d.). | ||
|
|
||
| ## Decision | ||
|
|
||
| 1. Ship a **standalone Rust gateway**. The process runs by itself with | ||
| optional operator configuration. No sibling checkout is required. | ||
| 2. Keep reusable domain code in **`crates/waf-ids-core`**, a member of | ||
| the same Cargo workspace (`path` dependency), not a git submodule, | ||
| until an independently versioned engine, SDK, or adapter needs its | ||
| own release lifecycle. | ||
| 3. Treat sibling ContextualWisdomLab products as **optional composition | ||
| callers** over HTTP or documented contracts: | ||
| - **naruon** and **gyeot** may call or be called when an operator | ||
| wires them; they are not required tree members. | ||
| - **contextual-orchestrator** may front the optional SOC LLM | ||
| (`SOC_LLM_BASE_URL`); absent configuration, SOC assist is off. | ||
| - **Clearfolio** may receive optional document-viewer relays | ||
| (`CLEARFOLIO_BASE_URL`); absent configuration, that surface stays | ||
| disabled. | ||
| Existing optional caller links stay in place. Do not require those | ||
| services to start the gateway. | ||
|
|
||
| ## Consequences | ||
|
|
||
| - Operators can `cargo run` and use `/admin`, `/gateway/{path}`, and | ||
| `/dnsbl/zone` on a single binary. | ||
| - `waf-ids-core` stays free of async/HTTP dependencies so domain tests | ||
| and fuzz mirrors do not pull the Axum crate graph. | ||
| - A later submodule or crates.io publish is deferred until a real | ||
| second consumer and release cadence exist. | ||
| - Optional Clearfolio and orchestrator hooks must remain inert when | ||
| unconfigured so the leaf stays independently runnable. | ||
|
|
||
| ## References | ||
|
|
||
| The Cargo Book. (n.d.). *Workspaces*. | ||
| https://doc.rust-lang.org/cargo/reference/workspaces.html |
55 changes: 55 additions & 0 deletions
55
docs/adr/0002-optional-json-state-standalone-durability.md
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| # ADR 0002: Optional JSON state for standalone durability | ||
|
|
||
| - Status: Accepted | ||
| - Date: 2026-08-25 | ||
| - Recorded from: current `main` (`README.md` run notes; | ||
| `docs/architecture.md` security boundaries; `docs/runbooks/operations.md` | ||
| persistence behavior) | ||
|
|
||
| ## Context | ||
|
|
||
| The gateway must keep operator-managed routes, threat indicators, DNSBL | ||
| entries, events, and license metadata across a local restart when an | ||
| operator asks for durability. Many lab and smoke runs do not need a | ||
| file at all. | ||
|
|
||
| JSON is the Internet Standard data interchange format for this class of | ||
| text documents (Bray, 2017, RFC 8259 / STD 90). A single pretty-printed | ||
| object is enough for a standalone process. It is not a multi-operator | ||
| database, a backup system, or an audited change workflow. | ||
|
|
||
| ## Decision | ||
|
|
||
| 1. `WAF_IDS_STATE_PATH` is **optional**. When unset, the process uses | ||
| seeded in-memory state. Health reports `persistence: memory`. | ||
| 2. When the path is set, load JSON from that file (or seed and create | ||
| it). Persist with a **temporary sibling file** and **atomic rename** | ||
| onto the configured path. Health reports `persistence: file`. | ||
| 3. If a management write cannot replace the state file, **roll back** | ||
| the in-memory mutation and return an operator-visible error. | ||
| 4. Treat this JSON file as **baseline standalone durability only**. It | ||
| is not a production control-plane database, not a backup plan, and | ||
| not an audited change-management system. | ||
|
|
||
| A production database is **not** an accepted architecture decision on | ||
| current `main`. | ||
|
|
||
| ## Consequences | ||
|
|
||
| - `scripts/smoke.sh` can prove restart persistence with a temporary | ||
| JSON file and no external datastore. | ||
| - Parse failures on a configured path fail startup rather than silently | ||
| ignoring a corrupt file (`docs/security/threat-model.md`). | ||
| - Concurrent writers and disaster recovery remain out of scope until a | ||
| later durable store is accepted. | ||
| - Schema evolution is the application's JSON shape, not a migration | ||
| framework. | ||
|
|
||
| ## References | ||
|
|
||
| Bray, T. (Ed.). (2017). *The JavaScript Object Notation (JSON) data | ||
| interchange format* (RFC 8259). RFC Editor. | ||
| https://doi.org/10.17487/RFC8259 | ||
|
|
||
| *(Internet Standard, STD 90. Live-checked 2026-08-25 via | ||
| https://www.rfc-editor.org/info/rfc8259 and the DOI above.)* |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| # ADR 0003: OWASP CRS / Coraza as WAF authority | ||
|
|
||
| - Status: Accepted | ||
| - Date: 2026-08-25 | ||
| - Recorded from: current `main` (`README.md` production-coverage note; | ||
| `docs/architecture.md` near-term WAF integration) | ||
|
|
||
| ## Context | ||
|
|
||
| The gateway scores requests from local threat indicators and DNSBL | ||
| entries. That baseline is not a replacement for a maintained WAF rule | ||
| set. Inventing an in-house rule language would duplicate work the | ||
| OWASP Core Rule Set already does for generic attack detection | ||
| (OWASP Foundation / CRS Project, n.d.). | ||
|
|
||
| OWASP Coraza is an open-source WAF engine documented as compatible | ||
| with OWASP CRS (OWASP Coraza, n.d.). Current `main` already accepts | ||
| Coraza/CRS **audit** documents; it does not embed Coraza in-process. | ||
|
|
||
| ## Decision | ||
|
|
||
| 1. **OWASP CRS remains the WAF rule authority.** Do not replace CRS | ||
| with hand-rolled gateway rules. | ||
| 2. Accept admin-authenticated Coraza / OWASP CRS **audit JSON/NDJSON** | ||
| at `POST /api/waf/coraza/audit`. Interrupted transactions and CRS | ||
| rule messages become `SecurityEvent` rows. Block-grade hits may seed | ||
| DNSBL and `client_ip` / path threat indicators so later gateway | ||
| decisions can enforce matching clients. | ||
| 3. Run Coraza **outside** this process for now. **In-process Coraza | ||
| embedding is a follow-up**, not an accepted replacement of CRS and | ||
| not an accepted replacement of the audit ingest path. | ||
|
|
||
| Related accepted ingest on the same `main` (IDS, not WAF authority): | ||
| admin-authenticated Suricata EVE JSON/NDJSON at | ||
| `POST /api/ids/suricata/eve` (Eve JSON output, n.d.). Full route | ||
| correlation and live EVE tailing remain follow-ups. | ||
|
|
||
| ## Consequences | ||
|
|
||
| - Operators can attach an external Coraza/CRS deployment and still use | ||
| this gateway for scoring, events, and route-scoped block mode. | ||
| - CRS versioning and rule quality stay with the CRS project (latest | ||
| line observed 2026-08-25: 4.29.0 on https://coreruleset.org/). | ||
| - Embedding Coraza later must still consume CRS; it must not become a | ||
| pretext for a parallel hand-written rule pack. | ||
|
|
||
| ## References | ||
|
|
||
| OWASP Coraza. (n.d.). *Documentation*. https://coraza.io/docs/ | ||
|
|
||
| OWASP Coraza. (n.d.). *OWASP Coraza WAF*. https://coraza.io/ | ||
|
|
||
| OWASP Foundation / CRS Project. (n.d.). *OWASP Core Rule Set*. | ||
| https://coreruleset.org/ | ||
|
|
||
| Eve JSON output. (n.d.). In *Suricata documentation*. | ||
| https://docs.suricata.io/en/latest/output/eve/eve-json-output.html |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,69 @@ | ||
| # ADR 0004: RFC 5782-style DNSBL zone export | ||
|
|
||
| - Status: Accepted | ||
| - Date: 2026-08-25 | ||
| - Recorded from: current `main` (`README.md` DNSBL notes; | ||
| `docs/architecture.md` `/dnsbl/zone` and DNSBL serving follow-up; | ||
| `docs/fuzzing.md` zone-export invariants) | ||
|
|
||
| ## Context | ||
|
|
||
| Operators publish listed addresses so mail and gateway scorers can | ||
| query a DNS blacklist. Levine (2010) describes DNS blacklists and | ||
| whitelists as **IRTF Informational** practice: this is **not** an | ||
| Internet Standards Track specification. The conventional listing | ||
| record is an A resource record whose address is a **response code**, | ||
| not a destination to connect to. Those A values SHOULD lie in | ||
| `127.0.0.0/8` so a mistaken use as an IP address stays on loopback | ||
| (Levine, 2010, RFC 5782). | ||
|
|
||
| Zone and resource-record structure follows DNS concepts and the DNS | ||
| implementation specification (Mockapetris, 1987a, RFC 1034 / STD 13; | ||
| Mockapetris, 1987b, RFC 1035 / STD 13). | ||
|
|
||
| ## Decision | ||
|
|
||
| 1. Export an **RFC 5782-style DNSBL zone** at `GET /dnsbl/zone` using | ||
| the configured `DNSBL_ORIGIN`. The `dnsbl.local` default is for local | ||
| development only; authoritative deployments must explicitly configure a | ||
| non-`.local` origin because `.local.` is reserved for mDNS. | ||
| 2. Require every published DNSBL **response code** to be an IPv4 | ||
| loopback-style address in **`127.0.0.0/8`**. Reject codes outside | ||
| that range at the management API. | ||
| 3. Treat the export as **zone text suitable for an authoritative DNS | ||
| server**. This process does **not** serve DNS on port 53. | ||
| 4. **Hickory DNS authoritative serving is a follow-up**, to be | ||
| considered after zone-export semantics stabilize. It is not | ||
| accepted on current `main`. | ||
|
|
||
| ## Consequences | ||
|
|
||
| - Management upserts stay keyed by listed `address`; the A-record | ||
| payload is the validated `127.0.0.0/8` code. | ||
| - Fuzz and property tests require every published A-record code to | ||
| remain a loopback literal and every TXT payload to stay escaped | ||
| (`docs/fuzzing.md`). | ||
| - RFC 5782 remains Informational. Local validation is stricter | ||
| (`MUST` in this gateway) than the RFC `SHOULD` on A values. | ||
| - IPv6 DNSxL layout in RFC 5782 is not an accepted serving mode here. | ||
|
|
||
| ## References | ||
|
|
||
| Cheshire, S., & Krochmal, M. (2013). *Multicast DNS* (RFC 6762). | ||
| RFC Editor. https://doi.org/10.17487/RFC6762 | ||
|
|
||
| Levine, J. (2010). *DNS blacklists and whitelists* (RFC 5782). RFC | ||
| Editor. https://doi.org/10.17487/RFC5782 | ||
|
|
||
| *(IRTF Informational; not Standards Track. Also | ||
| https://www.rfc-editor.org/info/rfc5782. Live-checked 2026-08-25.)* | ||
|
|
||
| Mockapetris, P. (1987a). *Domain names—concepts and facilities* | ||
| (RFC 1034). RFC Editor. https://doi.org/10.17487/RFC1034 | ||
|
|
||
| *(Internet Standard, STD 13.)* | ||
|
|
||
| Mockapetris, P. (1987b). *Domain names—implementation and | ||
| specification* (RFC 1035). RFC Editor. https://doi.org/10.17487/RFC1035 | ||
|
|
||
| *(Internet Standard, STD 13.)* |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| # ADR 0005: Coverage-guided fuzzing of untrusted-input surfaces | ||
|
|
||
| - Status: Accepted | ||
| - Date: 2026-08-25 | ||
| - Recorded from: current `main` (`docs/fuzzing.md`; README verification | ||
| note; in-repo preprint PDF | ||
| `docs/papers/fuzzing-art-science-engineering-survey-arxiv-1812.00140.pdf`) | ||
|
|
||
| ## Context | ||
|
|
||
| The gateway parses attacker-controlled path, query, body, and client | ||
| IP on every request. Startup also deserializes untrusted JSON state | ||
| and admin-token configuration. DNSBL zone generation emits text from | ||
| operator-supplied reasons and codes. | ||
|
|
||
| Manès et al. (2021) survey fuzzing as repeated execution with | ||
| generated, often malformed inputs, and treat coverage-guided fuzzing | ||
| as a primary engineering method for finding crashes and invariant | ||
| violations on those surfaces. This ADR cites that **published** IEEE | ||
| Transactions on Software Engineering article as primary. The 2018 | ||
| arXiv posting is the **preprint** of the same work (Manès et al., | ||
| 2018) and is already vendored in-repo. No other fuzzing paper is | ||
| cited. | ||
|
|
||
| ## Decision | ||
|
|
||
| 1. Exercise the untrusted-input surfaces with **coverage-guided | ||
| fuzzing** (cargo-fuzz / libFuzzer) on nightly: | ||
| - `fuzz_score_request` — `waf_ids_core::score_request` | ||
| - `fuzz_appdata_json` — `AppData` state-file JSON | ||
| - `fuzz_parse_admin_tokens` — admin-token configuration parser | ||
| - `fuzz_dnsbl_zone` — DNSBL zone export / validation | ||
| 2. Keep a **stable property-test mirror** (`proptest`) in | ||
| `crates/waf-ids-core/tests/fuzz_invariants.rs` and | ||
| `tests/fuzz_invariants.rs` so the same invariants run on stable in | ||
| `cargo test --workspace`. | ||
| 3. Isolate fuzz targets in the `fuzz/` Cargo workspace so root | ||
| `cargo test --workspace` never builds libFuzzer targets. | ||
| 4. When an untrusted-input surface changes, keep the libFuzzer target | ||
| and the property-test mirror in sync (`docs/fuzzing.md`). | ||
|
|
||
| ## Consequences | ||
|
|
||
| - Invariants include: no panic on arbitrary valid UTF-8 JSON input for the | ||
| state parser; non-empty score | ||
| reasons; deterministic scoring; serde round-trip of parsed state; | ||
| no empty token key or empty actor; TXT payloads fully escaped; | ||
| every published A-record code in `127.0.0.0/8`. | ||
| - Pull-request CI smoke-fuzzes each target for a bounded budget; | ||
| nightly runs a longer budget. Those workflows are operational, not | ||
| additional papers. | ||
| - Coverage-gate stubs and cancelled scanner runs are not evidence for | ||
| this decision. | ||
|
|
||
| ## References | ||
|
|
||
| Manès, V. J. M., Han, H., Han, C., Cha, S. K., Egele, M., Schwartz, | ||
| E. J., & Woo, M. (2021). The art, science, and engineering of | ||
| fuzzing: A survey. *IEEE Transactions on Software Engineering, | ||
| 47*(11), 2312–2331. https://doi.org/10.1109/TSE.2019.2946563 | ||
|
|
||
| *(Primary published version. Crossref record confirmed 2026-08-25: | ||
| title, volume 47 issue 11, pages 2312–2331, date 2021-11-01. DOI | ||
| resolver reached `https://ieeexplore.ieee.org/document/8863940/`.)* | ||
|
|
||
| Manès, V. J. M., Han, H., Han, C., Cha, S. K., Egele, M., Schwartz, | ||
| E. J., & Woo, M. (2018). The art, science, and engineering of | ||
| fuzzing: A survey. *arXiv*. | ||
| https://doi.org/10.48550/arXiv.1812.00140 | ||
|
|
||
| *(Preprint; arXiv:1812.00140. Submitted 2018-12-01, revised 2019-04-08 | ||
| as v4. Local copy: | ||
| `docs/papers/fuzzing-art-science-engineering-survey-arxiv-1812.00140.pdf`.)* |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,76 @@ | ||
| # ADR 0006: Admin-token threat-intel document ingest | ||
|
|
||
| - Status: Accepted | ||
| - Date: 2026-08-25 | ||
| - Recorded from: current `main` (`docs/architecture.md` threat | ||
| intelligence paragraph; admin console copy for the ingest routes) | ||
|
|
||
| ## Context | ||
|
|
||
| Gateway scoring needs threat indicators and DNSBL entries. Operators | ||
| already hold documents from STIX/TAXII, MISP, and OpenCTI. Those | ||
| documents are untrusted until validated. Live pull jobs against a | ||
| MISP REST API or an OpenCTI GraphQL endpoint are a different | ||
| operational surface (credentials, scheduling, pagination) and are | ||
| **not** accepted on current `main`. | ||
|
|
||
| STIX 2.1 is an OASIS Standard for exchanging cyber threat | ||
| intelligence objects (Jordan et al., 2021a). TAXII 2.1 is the OASIS | ||
| Standard for transporting STIX over HTTP collections (Jordan & | ||
| Varner, 2021). MISP is an open threat-intelligence sharing platform | ||
| and associated open standards (MISP Project, n.d.; MISP Standard, | ||
| n.d.). OpenCTI documents an open CTI platform with official | ||
| operator documentation (OpenCTI, n.d.). | ||
|
|
||
| ## Decision | ||
|
|
||
| 1. Ingest **operator-posted documents** on admin-authenticated routes: | ||
| - `POST /api/threat-intel/stix` — STIX 2.x indicator or bundle JSON | ||
| - `POST /api/threat-intel/misp` — MISP Event / attribute JSON | ||
| (`to_ids=false` attributes skipped) | ||
| - `POST /api/threat-intel/opencti` — OpenCTI observable / indicator | ||
| export JSON | ||
| 2. Support operator-initiated remote TAXII ingestion at | ||
| `POST /api/threat-intel/taxii/poll`: receive a TAXII 2.1 objects URL | ||
| (or API root plus collection id) and optional credentials, fetch the | ||
| external endpoint, normalize its response to STIX, then upsert it. | ||
| 3. Map supported IP, domain, URL, and hash material into | ||
| `ThreatIndicator` and `DnsblEntry` rows and update feed freshness. | ||
| 4. **Live MISP REST pull** and **live OpenCTI GraphQL pull** remain | ||
| follow-ups. They are not accepted replacements for document ingest. | ||
| 5. Never write TAXII or admin credentials into audit-log payloads. | ||
|
|
||
| ## Consequences | ||
|
|
||
| - An operator (or an external poller they control) can push reviewed | ||
| intelligence without this process holding a standing MISP or | ||
| OpenCTI session. | ||
| - STIX/TAXII citations are the OASIS Standard HTML editions fetched | ||
| 2026-08-25, not drafts. | ||
| - MISP Internet-Draft HTML for a “core format” exists on | ||
| misp-standard.org; this ADR does **not** treat that draft as a | ||
| published RFC or Standards Track document. The accepted references | ||
| are the official project and standard landings. | ||
| - TAXII poll still performs an outbound HTTP GET of objects the | ||
| operator named; that is document transport, not a live MISP/OpenCTI | ||
| product puller. | ||
|
|
||
| ## References | ||
|
|
||
| Jordan, B., Piazza, R., & Darley, T. (Eds.). (2021, June 10). *STIX | ||
| Version 2.1* (OASIS Standard). OASIS Open. | ||
| https://docs.oasis-open.org/cti/stix/v2.1/os/stix-v2.1-os.html | ||
|
|
||
| Jordan, B., & Varner, D. (Eds.). (2021, June 10). *TAXII Version 2.1* | ||
| (OASIS Standard). OASIS Open. | ||
| https://docs.oasis-open.org/cti/taxii/v2.1/os/taxii-v2.1-os.html | ||
|
|
||
| MISP Project. (n.d.). *MISP open source threat intelligence platform | ||
| & open standards for threat intelligence sharing*. | ||
| https://www.misp-project.org/ | ||
|
|
||
| MISP Standard. (n.d.). *MISP standard*. | ||
| https://www.misp-standard.org/ | ||
|
|
||
| OpenCTI. (n.d.). *OpenCTI documentation*. | ||
| https://docs.opencti.io/latest/ | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.