From 2679c032fd522b771cb0b0bf766ffc0327171330 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 26 Aug 2026 21:08:53 +0900 Subject: [PATCH 01/13] chore: complete Wardnet product rename --- .dockerignore | 2 +- .gitignore | 2 +- AGENTS.md | 4 +- CLAUDE.md | 16 ++-- Cargo.lock | 42 +++++----- Cargo.toml | 6 +- Dockerfile | 14 ++-- README.md | 16 ++-- .../{waf-ids-core => wardnet-core}/Cargo.toml | 2 +- .../{waf-ids-core => wardnet-core}/src/lib.rs | 33 ++++---- .../tests/fuzz_invariants.rs | 2 +- deploy/docker-compose.yml | 10 +-- .../{waf-ids-ai-soc.yaml => wardnet.yaml} | 38 ++++----- docs/architecture.md | 8 +- docs/commercial/20b-krw-sale-readiness.md | 4 +- docs/commercial/buyer-due-diligence.md | 2 +- docs/deployment/production.md | 10 +-- docs/design-system.md | 2 +- docs/figma/enterprise-product-architecture.md | 6 +- docs/fuzzing.md | 8 +- docs/goals/2026-07-02-initial-mvp-goal.md | 2 +- docs/migrations/wardnet-rename.md | 18 +++++ docs/ponytail/2026-07-02-complexity-audit.md | 6 +- docs/runbooks/operations.md | 10 +-- docs/security/threat-model.md | 2 +- .../2026-07-02-enterprise-product-package.md | 4 +- ...2026-07-02-feed-freshness-siem-evidence.md | 2 +- .../2026-07-02-program-completion-baseline.md | 6 +- ...i-soc-mvp.md => 2026-07-02-wardnet-mvp.md} | 4 +- .../plans/2026-07-03-audit-log-surface.md | 10 +-- .../2026-07-03-buyer-evidence-manifest.md | 2 +- ...07-02-enterprise-product-package-design.md | 6 +- ...-02-feed-freshness-siem-evidence-design.md | 2 +- ...7-02-program-completion-baseline-design.md | 4 +- ...design.md => 2026-07-02-wardnet-design.md} | 2 +- ...26-07-03-buyer-evidence-manifest-design.md | 2 +- fuzz/Cargo.toml | 8 +- fuzz/fuzz_targets/fuzz_appdata_json.rs | 2 +- fuzz/fuzz_targets/fuzz_dnsbl_zone.rs | 2 +- fuzz/fuzz_targets/fuzz_parse_admin_tokens.rs | 4 +- fuzz/fuzz_targets/fuzz_score_request.rs | 4 +- scripts/smoke.sh | 4 +- src/credentials.rs | 2 +- src/lib.rs | 79 ++++++++++++------- src/main.rs | 4 +- src/misp_import.rs | 2 +- src/opencti_import.rs | 2 +- src/stix_import.rs | 2 +- tests/binary.rs | 4 +- tests/fuzz_invariants.rs | 2 +- 50 files changed, 238 insertions(+), 192 deletions(-) rename crates/{waf-ids-core => wardnet-core}/Cargo.toml (96%) rename crates/{waf-ids-core => wardnet-core}/src/lib.rs (98%) rename crates/{waf-ids-core => wardnet-core}/tests/fuzz_invariants.rs (99%) rename deploy/kubernetes/{waf-ids-ai-soc.yaml => wardnet.yaml} (73%) create mode 100644 docs/migrations/wardnet-rename.md rename docs/superpowers/plans/{2026-07-02-waf-ids-ai-soc-mvp.md => 2026-07-02-wardnet-mvp.md} (96%) rename docs/superpowers/specs/{2026-07-02-waf-ids-ai-soc-design.md => 2026-07-02-wardnet-design.md} (98%) diff --git a/.dockerignore b/.dockerignore index 06b9dea0..1900c263 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,6 +2,6 @@ target .git .github .DS_Store -waf-ids-state.local.json +wardnet-state.local.json *.profraw *.profdata diff --git a/.gitignore b/.gitignore index 408e06d1..dca39553 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ /target -/waf-ids-state*.json +/wardnet-state*.json /runtime-state*.json diff --git a/AGENTS.md b/AGENTS.md index 2a32694b..d0060487 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -17,7 +17,7 @@ Cross-agent conventions for any agent (Claude, Codex, Cursor, opencode, …) wor - A failing **`trivy-fs` is a REAL finding, not a flake.** Read the job log — it prints each finding's rule id / severity / file — or the run's SARIF results, then **remediate**: - Rust dependency CVE → bump the crate (`cargo update -p `, adjust `Cargo.toml`) and commit the updated `Cargo.lock`. - Container/OS finding → fix the base image or package in the `Dockerfile`. - - k8s/IaC misconfig → fix `deploy/kubernetes/waf-ids-ai-soc.yaml` or `deploy/docker-compose.yml`. + - k8s/IaC misconfig → fix `deploy/kubernetes/wardnet.yaml` or `deploy/docker-compose.yml`. - Genuine false positive only → add a narrow, commented entry to `.trivyignore` (see the existing `AVD-KSV-0125` note for the expected style). Never broaden it to silence a real vuln. - Do **not** weaken or disable the gate. A local scan with a stale DB misses findings: run `trivy --download-db-only` first, then scan the **merge ref**, not just the PR head (e.g. `trivy fs --scanners vuln,misconfig --severity CRITICAL,HIGH --ignore-unfixed .`). - Gating is by the Security Scan **job result**, not the `code_scanning` rule. That org ruleset is intentionally **CodeQL-only** (multiple code-scanning tools can't converge on one PR ref) — do **not** add tools to it. @@ -30,7 +30,7 @@ Cross-agent conventions for any agent (Claude, Codex, Cursor, opencode, …) wor - Org rule: do **not** read config/secrets from raw environment variables (`std::env::var` / `os.getenv`) at runtime. Read them from a KV / credential registry. Org Actions secrets (e.g. `OPENAI_API_KEY`) flow **into** the KV via a bootstrap/CI step; runtime reads from the KV — env is only transport into the KV, never the runtime source. - Reference implementation: xtrmLLMBatchPython's pgcrypto-encrypted Postgres credential registry (`get_credential(name)`). Reuse that pattern (a DB-backed KV is fine) unless a dedicated KV is adopted. -- **Secrets path:** Admin tokens (`ADMIN_TOKEN` / `ADMIN_TOKENS`, optional `WAF_IDS_CREDENTIALS_PATH` JSON) bootstrap a process-local `CredentialRegistry` (`get_credential`) at startup; runtime auth reads the registry, not env. **Remaining deviation:** non-secret operational config (`BIND_ADDR`, `WAF_IDS_STATE_PATH`, `DNSBL_ORIGIN`, `EVENT_LIMIT`, `RATE_LIMIT`, …) still reads env directly — migrate those behind the same registry/KV when a durable credential store is adopted. +- **Secrets path:** Admin tokens (`ADMIN_TOKEN` / `ADMIN_TOKENS`, optional `WARDNET_CREDENTIALS_PATH` JSON) bootstrap a process-local `CredentialRegistry` (`get_credential`) at startup; runtime auth reads the registry, not env. **Remaining deviation:** non-secret operational config (`BIND_ADDR`, `WARDNET_STATE_PATH`, `DNSBL_ORIGIN`, `EVENT_LIMIT`, `RATE_LIMIT`, …) still reads env directly — migrate those behind the same registry/KV when a durable credential store is adopted. ### This repo's role in the ecosystem diff --git a/CLAUDE.md b/CLAUDE.md index f6a0a676..a46f5dea 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,7 +6,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## What This Is -wardnet (crate name `waf-ids-ai-soc`) is a Rust-first WAF/IDS/AI SOC gateway and control-plane baseline for ContextualWisdomLab: web-managed API gateway routes, request scoring from threat indicators and DNSBL entries, monitor/block enforcement, RFC 5782-style DNSBL zone export, SOC event/KPI APIs, commercial readiness evidence APIs, and an embedded admin console at `/admin`. It deliberately does not reimplement a full WAF/IDS/SIEM — production coverage is meant to come from adapters to proven engines (OWASP CRS/Coraza, Suricata, STIX/TAXII, MISP/OpenCTI). +wardnet (crate name `wardnet`) is a Rust-first WAF/IDS/AI SOC gateway and control-plane baseline for ContextualWisdomLab: web-managed API gateway routes, request scoring from threat indicators and DNSBL entries, monitor/block enforcement, RFC 5782-style DNSBL zone export, SOC event/KPI APIs, commercial readiness evidence APIs, and an embedded admin console at `/admin`. It deliberately does not reimplement a full WAF/IDS/SIEM — production coverage is meant to come from adapters to proven engines (OWASP CRS/Coraza, Suricata, STIX/TAXII, MISP/OpenCTI). ## Commands @@ -23,7 +23,7 @@ Other common commands: ```bash cargo run # serve on 127.0.0.1:8080; open /admin cargo test --workspace # run a single test by name -cargo test -p waf-ids-core # test only the core crate +cargo test -p wardnet-core # test only the core crate scripts/smoke.sh # end-to-end smoke: boots the binary, exercises the API, verifies restart persistence ``` @@ -46,9 +46,9 @@ cargo +nightly fuzz run fuzz_score_request -- -max_total_time=60 Root Cargo workspace with two members (resolver 3): -- `crates/waf-ids-core` — pure domain crate, no async/HTTP deps (only `serde` + `percent-encoding`): models, validation, upserts, request scoring, DNSBL zone formatting, event retention, threat-feed freshness, KPI snapshots, commercial readiness, buyer evidence manifests. -- Root crate `waf-ids-ai-soc` (`src/lib.rs`) — Axum management API, embedded admin console, optional JSON state persistence, upstream proxying, NDJSON event export, support bundle assembly, plus the in-crate HTTP tests. Depends on `waf-ids-core`. -- `src/main.rs` — deliberately thin shim over `waf_ids_ai_soc::run_from_env` so all config/serve logic is unit-testable; covered end-to-end by `tests/binary.rs` (SIGTERM graceful shutdown). +- `crates/wardnet-core` — pure domain crate, no async/HTTP deps (only `serde` + `percent-encoding`): models, validation, upserts, request scoring, DNSBL zone formatting, event retention, threat-feed freshness, KPI snapshots, commercial readiness, buyer evidence manifests. +- Root crate `wardnet` (`src/lib.rs`) — Axum management API, embedded admin console, optional JSON state persistence, upstream proxying, NDJSON event export, support bundle assembly, plus the in-crate HTTP tests. Depends on `wardnet-core`. +- `src/main.rs` — deliberately thin shim over `wardnet::run_from_env` so all config/serve logic is unit-testable; covered end-to-end by `tests/binary.rs` (SIGTERM graceful shutdown). - `fuzz/` — a **separate** cargo workspace (empty `[workspace]` table in `fuzz/Cargo.toml` — do not remove) so root `cargo test --workspace` never builds fuzz targets. Seed corpora live in `fuzz/corpus//`. The core stays an in-repo workspace crate on purpose (no git submodule) until it has an independent release cadence. @@ -57,12 +57,12 @@ The core stays an in-repo workspace crate on purpose (no git submodule) until it - In-crate HTTP tests: `#[cfg(test)]` module in `src/lib.rs` (uses `tower::ServiceExt` to drive the Axum app). Tests that mutate env vars serialize on `ENV_GUARD`. - E2E binary test: `tests/binary.rs`. -- Property-test mirrors of the fuzz invariants (run on stable in normal CI): `tests/fuzz_invariants.rs` and `crates/waf-ids-core/tests/fuzz_invariants.rs` (proptest). +- Property-test mirrors of the fuzz invariants (run on stable in normal CI): `tests/fuzz_invariants.rs` and `crates/wardnet-core/tests/fuzz_invariants.rs` (proptest). - External smoke: `scripts/smoke.sh`. ## Runtime Configuration -Read in `run_from_env` (`src/lib.rs`): `BIND_ADDR` (default `127.0.0.1:8080`), `ADMIN_TOKEN` (write token for `X-Admin-Token`), `ADMIN_TOKENS` (comma-separated `token:actor` pairs for multi-token RBAC with per-token audit actors), `WAF_IDS_STATE_PATH` (optional JSON state file; omitted = seeded in-memory state), `DNSBL_ORIGIN` (default `dnsbl.local`), `EVENT_LIMIT` (default 1000, must be > 0), `RATE_LIMIT` / `RATE_LIMIT_WINDOW`. +Read in `run_from_env` (`src/lib.rs`): `BIND_ADDR` (default `127.0.0.1:8080`), `ADMIN_TOKEN` (write token for `X-Admin-Token`), `ADMIN_TOKENS` (comma-separated `token:actor` pairs for multi-token RBAC with per-token audit actors), `WARDNET_STATE_PATH` (optional JSON state file; omitted = seeded in-memory state), `DNSBL_ORIGIN` (default `dnsbl.local`), `EVENT_LIMIT` (default 1000, must be > 0), `RATE_LIMIT` / `RATE_LIMIT_WINDOW`. ## Key Conventions @@ -71,7 +71,7 @@ Read in `run_from_env` (`src/lib.rs`): `BIND_ADDR` (default `127.0.0.1:8080`), ` - Audit logs must never leak admin tokens (`scripts/smoke.sh` asserts this). - Untrusted-input surfaces (request scorer, state deserializer, admin-token parser, DNSBL zone export) are fuzzed; if you change one, keep its libFuzzer target and proptest mirror in sync (`docs/fuzzing.md` lists the invariants per target). - Block mode is route-scoped; default bind is localhost. See `docs/architecture.md` for security boundaries and the near-term adapter roadmap. -- Deployment assets: `Dockerfile` (two-stage build, pinned base images, runs as non-root `wafids`), `deploy/docker-compose.yml`, `deploy/kubernetes/waf-ids-ai-soc.yaml`. +- Deployment assets: `Dockerfile` (two-stage build, pinned base images, runs as non-root `wardnet`), `deploy/docker-compose.yml`, `deploy/kubernetes/wardnet.yaml`. ## Further Docs diff --git a/Cargo.lock b/Cargo.lock index c696190f..938d9bbd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1335,7 +1335,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] -name = "waf-ids-ai-soc" +name = "wait-timeout" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" +dependencies = [ + "libc", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wardnet" version = "0.1.0" dependencies = [ "axum", @@ -1346,11 +1364,11 @@ dependencies = [ "serde_json", "tokio", "tower", - "waf-ids-core", + "wardnet-core", ] [[package]] -name = "waf-ids-core" +name = "wardnet-core" version = "0.1.0" dependencies = [ "percent-encoding", @@ -1359,24 +1377,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "wait-timeout" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11" -dependencies = [ - "libc", -] - -[[package]] -name = "want" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] - [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" diff --git a/Cargo.toml b/Cargo.toml index b2ec231f..f5cc9b60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,12 +1,12 @@ [package] -name = "waf-ids-ai-soc" +name = "wardnet" version = "0.1.0" edition = "2024" description = "Rust-first WAF/IDS/AI SOC gateway with DNSBL and commercial readiness surfaces" license = "MIT" [workspace] -members = [".", "crates/waf-ids-core"] +members = [".", "crates/wardnet-core"] resolver = "3" [dependencies] @@ -16,7 +16,7 @@ futures-util = { version = "0.3", default-features = false, features = ["std"] } serde = { version = "1", features = ["derive"] } serde_json = "1" tokio = { version = "1", features = ["fs", "macros", "net", "rt-multi-thread", "signal", "sync"] } -waf-ids-core = { path = "crates/waf-ids-core" } +wardnet-core = { path = "crates/wardnet-core" } [dev-dependencies] tower = { version = "0.5", features = ["util"] } diff --git a/Dockerfile b/Dockerfile index f92c557f..aa2a627e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,21 +13,21 @@ RUN apt-get update \ ca-certificates=20230311+deb12u1 \ curl=7.88.1-10+deb12u14 \ && rm -rf /var/lib/apt/lists/* \ - && groupadd --gid 10001 wafids \ - && useradd --uid 10001 --gid 10001 --create-home --home-dir /var/lib/waf-ids-ai-soc wafids + && groupadd --gid 10001 wardnet \ + && useradd --uid 10001 --gid 10001 --create-home --home-dir /var/lib/wardnet wardnet -COPY --from=build /app/target/release/waf-ids-ai-soc /usr/local/bin/waf-ids-ai-soc +COPY --from=build /app/target/release/wardnet /usr/local/bin/wardnet ENV BIND_ADDR=127.0.0.1:8080 \ DNSBL_ORIGIN=dnsbl.local \ EVENT_LIMIT=1000 \ - WAF_IDS_STATE_PATH=/var/lib/waf-ids-ai-soc/state.json + WARDNET_STATE_PATH=/var/lib/wardnet/state.json EXPOSE 8080 -VOLUME ["/var/lib/waf-ids-ai-soc"] -USER wafids +VOLUME ["/var/lib/wardnet"] +USER wardnet HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ CMD curl -fsS "http://${BIND_ADDR}/healthz" || exit 1 -ENTRYPOINT ["/usr/local/bin/waf-ids-ai-soc"] +ENTRYPOINT ["/usr/local/bin/wardnet"] diff --git a/README.md b/README.md index d1587583..8f7109cb 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# WAF IDS AI SOC +# Wardnet Rust-first gateway and SOC control-plane baseline for ContextualWisdomLab. The project starts small on purpose: - web-managed API gateway routes -- reusable `waf-ids-core` domain crate inside the same Cargo workspace +- reusable `wardnet-core` domain crate inside the same Cargo workspace - request scoring from threat indicators and DNSBL entries - monitor/block enforcement modes - RFC 5782-style DNSBL zone export @@ -21,7 +21,7 @@ It does not pretend to be a full WAF, IDS, SIEM, or SOAR yet. Production WAF and ## Completion Baseline -The program-complete baseline means the binary can run by itself, keep operator-managed routes/threats/DNSBL entries/events across restart when `WAF_IDS_STATE_PATH` is configured, enforce monitor/block decisions, export DNSBL records, and prove that loop through `scripts/smoke.sh`. +The program-complete baseline means the binary can run by itself, keep operator-managed routes/threats/DNSBL entries/events across restart when `WARDNET_STATE_PATH` is configured, enforce monitor/block decisions, export DNSBL records, and prove that loop through `scripts/smoke.sh`. It is still not a hardened internet-facing deployment. Use TLS, identity-aware access, upstream allowlists, and route rollback procedures before production traffic. @@ -66,15 +66,17 @@ Useful environment variables: - `BIND_ADDR`: listen address, default `127.0.0.1:8080` - `ADMIN_TOKEN`: optional write token for management writes via `X-Admin-Token` -- `WAF_IDS_STATE_PATH`: optional JSON state path. When omitted, the service runs with seeded in-memory state. +- `WARDNET_STATE_PATH`: optional JSON state path. When omitted, the service runs with seeded in-memory state. - `DNSBL_ORIGIN`: DNSBL zone origin, default `dnsbl.local` - `EVENT_LIMIT`: retained event count, default `1000`; must be greater than zero +Existing deployments can follow the [Wardnet rename migration](docs/migrations/wardnet-rename.md). + Example with persistent local state: ```bash ADMIN_TOKEN=dev-secret \ -WAF_IDS_STATE_PATH=./waf-ids-state.local.json \ +WARDNET_STATE_PATH=./wardnet-state.local.json \ DNSBL_ORIGIN=dnsbl.example \ cargo run ``` @@ -170,11 +172,11 @@ Deployment assets: - `Dockerfile` - `deploy/docker-compose.yml` -- `deploy/kubernetes/waf-ids-ai-soc.yaml` +- `deploy/kubernetes/wardnet.yaml` ## Workspace -- `crates/waf-ids-core`: pure domain models, validation, upserts, scoring, DNSBL zone formatting, event retention, threat-feed freshness classification, KPI snapshots, commercial readiness snapshots, and buyer evidence manifests. +- `crates/wardnet-core`: pure domain models, validation, upserts, scoring, DNSBL zone formatting, event retention, threat-feed freshness classification, KPI snapshots, commercial readiness snapshots, and buyer evidence manifests. - `src/lib.rs`: Axum management API, admin console, optional state persistence, upstream proxying, NDJSON event export, evidence manifest/support bundle assembly, and in-crate HTTP tests. - `src/main.rs`: process configuration and server startup. diff --git a/crates/waf-ids-core/Cargo.toml b/crates/wardnet-core/Cargo.toml similarity index 96% rename from crates/waf-ids-core/Cargo.toml rename to crates/wardnet-core/Cargo.toml index d229947a..d643f649 100644 --- a/crates/waf-ids-core/Cargo.toml +++ b/crates/wardnet-core/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "waf-ids-core" +name = "wardnet-core" version = "0.1.0" edition = "2024" description = "Core WAF/IDS/AI SOC domain models, scoring, DNSBL, and commercial readiness logic" diff --git a/crates/waf-ids-core/src/lib.rs b/crates/wardnet-core/src/lib.rs similarity index 98% rename from crates/waf-ids-core/src/lib.rs rename to crates/wardnet-core/src/lib.rs index e3788698..4c5e3689 100644 --- a/crates/waf-ids-core/src/lib.rs +++ b/crates/wardnet-core/src/lib.rs @@ -981,62 +981,63 @@ pub fn kpi_snapshot_at(data: &AppData, now_unix: u64) -> SocKpiSnapshot { /// dependency — the format is a few lines of text. pub fn prometheus_exposition(kpi: &SocKpiSnapshot) -> String { let metrics: [(&str, &str, usize); 10] = [ + ("routes", "Configured gateway routes.", kpi.route_count), ( - "waf_ids_routes", - "Configured gateway routes.", - kpi.route_count, - ), - ( - "waf_ids_threat_indicators", + "threat_indicators", "Operator threat indicators loaded.", kpi.threat_indicator_count, ), ( - "waf_ids_dnsbl_entries", + "dnsbl_entries", "DNSBL reputation entries.", kpi.dnsbl_entry_count, ), ( - "waf_ids_threat_feeds", + "threat_feeds", "Imported threat feeds.", kpi.threat_feed_count, ), ( - "waf_ids_threat_feeds_fresh", + "threat_feeds_fresh", "Threat feeds within their TTL.", kpi.fresh_threat_feed_count, ), ( - "waf_ids_threat_feeds_stale", + "threat_feeds_stale", "Threat feeds past their TTL.", kpi.stale_threat_feed_count, ), ( - "waf_ids_security_events", + "security_events", "Total recorded security events.", kpi.event_count, ), ( - "waf_ids_security_events_blocked", + "security_events_blocked", "Security events with a blocked action.", kpi.blocked_event_count, ), ( - "waf_ids_security_events_monitored", + "security_events_monitored", "Security events with a monitored action.", kpi.monitor_event_count, ), ( - "waf_ids_audit_log_entries", + "audit_log_entries", "Recorded management audit-log entries.", kpi.audit_log_count, ), ]; let mut out = String::new(); - for (name, help, value) in metrics { + for (suffix, help, value) in metrics { + let name = format!("wardnet_{suffix}"); out.push_str(&format!( "# HELP {name} {help}\n# TYPE {name} gauge\n{name} {value}\n" )); + let legacy = format!("waf_ids_{suffix}"); + out.push_str(&format!( + "# HELP {legacy} Deprecated compatibility alias for {name}.\n# TYPE {legacy} gauge\n{legacy} {value}\n" + )); } out } @@ -1122,7 +1123,7 @@ pub fn commercial_readiness_snapshot_at(data: &AppData, now_unix: u64) -> Commer deployment_assets: vec![ "Dockerfile".to_string(), "deploy/docker-compose.yml".to_string(), - "deploy/kubernetes/waf-ids-ai-soc.yaml".to_string(), + "deploy/kubernetes/wardnet.yaml".to_string(), ], buyer_evidence: vec![ "docs/commercial/20b-krw-sale-readiness.md".to_string(), diff --git a/crates/waf-ids-core/tests/fuzz_invariants.rs b/crates/wardnet-core/tests/fuzz_invariants.rs similarity index 99% rename from crates/waf-ids-core/tests/fuzz_invariants.rs rename to crates/wardnet-core/tests/fuzz_invariants.rs index d2d75320..452f2513 100644 --- a/crates/waf-ids-core/tests/fuzz_invariants.rs +++ b/crates/wardnet-core/tests/fuzz_invariants.rs @@ -8,7 +8,7 @@ use proptest::prelude::*; use std::net::{IpAddr, Ipv4Addr}; -use waf_ids_core::{ +use wardnet_core::{ AppData, DnsblEntry, Severity, ThreatIndicator, export_dnsbl_zone, score_request, validate_dnsbl, }; diff --git a/deploy/docker-compose.yml b/deploy/docker-compose.yml index 85a02768..79d86a77 100644 --- a/deploy/docker-compose.yml +++ b/deploy/docker-compose.yml @@ -1,18 +1,18 @@ services: - waf-ids-ai-soc: + wardnet: build: context: .. - image: contextualwisdomlab/waf-ids-ai-soc:local + image: contextualwisdomlab/wardnet:local environment: BIND_ADDR: 0.0.0.0:8080 ADMIN_TOKEN: ${ADMIN_TOKEN:?set ADMIN_TOKEN} DNSBL_ORIGIN: ${DNSBL_ORIGIN:-dnsbl.local} EVENT_LIMIT: ${EVENT_LIMIT:-1000} - WAF_IDS_STATE_PATH: /var/lib/waf-ids-ai-soc/state.json + WARDNET_STATE_PATH: /var/lib/wardnet/state.json ports: - "8080:8080" volumes: - - waf_ids_state:/var/lib/waf-ids-ai-soc + - wardnet_state:/var/lib/wardnet healthcheck: test: ["CMD", "curl", "-fsS", "http://127.0.0.1:8080/healthz"] interval: 30s @@ -21,4 +21,4 @@ services: start_period: 10s volumes: - waf_ids_state: + wardnet_state: diff --git a/deploy/kubernetes/waf-ids-ai-soc.yaml b/deploy/kubernetes/wardnet.yaml similarity index 73% rename from deploy/kubernetes/waf-ids-ai-soc.yaml rename to deploy/kubernetes/wardnet.yaml index f811ecb4..63b87b50 100644 --- a/deploy/kubernetes/waf-ids-ai-soc.yaml +++ b/deploy/kubernetes/wardnet.yaml @@ -1,13 +1,13 @@ apiVersion: v1 kind: Namespace metadata: - name: waf-ids-ai-soc + name: wardnet --- apiVersion: v1 kind: Secret metadata: - name: waf-ids-ai-soc-admin - namespace: waf-ids-ai-soc + name: wardnet-admin + namespace: wardnet type: Opaque stringData: ADMIN_TOKEN: replace-with-secret-manager-sync @@ -15,8 +15,8 @@ stringData: apiVersion: v1 kind: PersistentVolumeClaim metadata: - name: waf-ids-ai-soc-state - namespace: waf-ids-ai-soc + name: wardnet-state + namespace: wardnet spec: accessModes: - ReadWriteOnce @@ -27,19 +27,19 @@ spec: apiVersion: apps/v1 kind: Deployment metadata: - name: waf-ids-ai-soc - namespace: waf-ids-ai-soc + name: wardnet + namespace: wardnet labels: - app.kubernetes.io/name: waf-ids-ai-soc + app.kubernetes.io/name: wardnet spec: replicas: 1 selector: matchLabels: - app.kubernetes.io/name: waf-ids-ai-soc + app.kubernetes.io/name: wardnet template: metadata: labels: - app.kubernetes.io/name: waf-ids-ai-soc + app.kubernetes.io/name: wardnet spec: securityContext: runAsNonRoot: true @@ -48,7 +48,7 @@ spec: type: RuntimeDefault containers: - name: gateway - image: ghcr.io/contextualwisdomlab/waf-ids-ai-soc:0.1.0 + image: ghcr.io/contextualwisdomlab/wardnet:0.1.0 imagePullPolicy: IfNotPresent ports: - containerPort: 8080 @@ -60,16 +60,16 @@ spec: value: dnsbl.example - name: EVENT_LIMIT value: "1000" - - name: WAF_IDS_STATE_PATH - value: /var/lib/waf-ids-ai-soc/state.json + - name: WARDNET_STATE_PATH + value: /var/lib/wardnet/state.json - name: ADMIN_TOKEN valueFrom: secretKeyRef: - name: waf-ids-ai-soc-admin + name: wardnet-admin key: ADMIN_TOKEN volumeMounts: - name: state - mountPath: /var/lib/waf-ids-ai-soc + mountPath: /var/lib/wardnet readinessProbe: httpGet: path: /healthz @@ -98,16 +98,16 @@ spec: volumes: - name: state persistentVolumeClaim: - claimName: waf-ids-ai-soc-state + claimName: wardnet-state --- apiVersion: v1 kind: Service metadata: - name: waf-ids-ai-soc - namespace: waf-ids-ai-soc + name: wardnet + namespace: wardnet spec: selector: - app.kubernetes.io/name: waf-ids-ai-soc + app.kubernetes.io/name: wardnet ports: - name: http port: 80 diff --git a/docs/architecture.md b/docs/architecture.md index 89291bf5..105ece41 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -7,7 +7,7 @@ flowchart LR operator["Security Operator"] --> admin["Admin Console"] admin --> api["Management API"] api --> app["App Crate"] - app --> core["waf-ids-core"] + app --> core["wardnet-core"] core --> state["Runtime State"] state --> file["Optional JSON State File"] client["HTTP Client"] --> gateway["Rust Gateway"] @@ -27,9 +27,9 @@ flowchart LR ## Components -- `src/main.rs`: process startup and operator configuration from `BIND_ADDR`, `ADMIN_TOKEN`, `WAF_IDS_STATE_PATH`, `DNSBL_ORIGIN`, and `EVENT_LIMIT`. +- `src/main.rs`: process startup and operator configuration from `BIND_ADDR`, `ADMIN_TOKEN`, `WARDNET_STATE_PATH`, `DNSBL_ORIGIN`, and `EVENT_LIMIT`. - `src/lib.rs`: Axum app, routing, management APIs, optional JSON persistence, gateway handler, upstream proxying, admin console, support bundle assembly, NDJSON event export, and in-crate HTTP tests. -- `crates/waf-ids-core`: reusable domain models plus validation, upsert, scoring, DNSBL zone export, event retention, threat-feed freshness, KPI snapshot, and commercial readiness logic. +- `crates/wardnet-core`: reusable domain models plus validation, upsert, scoring, DNSBL zone export, event retention, threat-feed freshness, KPI snapshot, and commercial readiness logic. - `/admin`: embedded web console. - `/gateway/{path}`: route selection, request scoring, monitor/block decision, optional upstream proxying. - `/dnsbl/zone`: DNSBL zone text using the configured origin, suitable for publication through an authoritative DNS server. @@ -53,7 +53,7 @@ flowchart LR - Default bind address is localhost. - Remote management requires `ADMIN_TOKEN` plus external TLS and identity controls. -- `WAF_IDS_STATE_PATH` enables JSON state persistence for standalone operation. Without it, the service uses seeded in-memory state. +- `WARDNET_STATE_PATH` enables JSON state persistence for standalone operation. Without it, the service uses seeded in-memory state. - File-backed writes use temporary sibling files followed by atomic rename. Management API mutations roll back in memory if the state file cannot be replaced. - Block mode is route-scoped to avoid global accidental enforcement. - JSON persistence is a baseline durability mechanism, not a substitute for a production database, backup plan, or audited change workflow. diff --git a/docs/commercial/20b-krw-sale-readiness.md b/docs/commercial/20b-krw-sale-readiness.md index 502188b4..2f8a9dd3 100644 --- a/docs/commercial/20b-krw-sale-readiness.md +++ b/docs/commercial/20b-krw-sale-readiness.md @@ -11,7 +11,7 @@ This project treats a 2B KRW sale as an enterprise due-diligence threshold, not 5. Threat feed updates must be importable through `POST /api/threat-feeds/import`. 6. The product must expose fresh/stale threat feed evidence through `GET /api/threat-feeds/freshness`. 7. The product must expose SOC event export through `GET /api/events.ndjson`. -8. The product must retain threat feed status, imported HTTP indicators, DNSBL entries, gateway routes, and security events across restart when `WAF_IDS_STATE_PATH` is configured. +8. The product must retain threat feed status, imported HTTP indicators, DNSBL entries, gateway routes, and security events across restart when `WARDNET_STATE_PATH` is configured. 9. The readiness API must report blockers instead of returning a vague success state. 10. The support bundle API must return health, KPIs, license metadata, readiness checks, feed freshness, and evidence counts without secrets. 11. The product must expose a buyer evidence manifest through `GET /api/commercial/evidence-manifest` so evaluators can verify required runtime APIs, committed documents, and deployment assets from one contract. @@ -53,4 +53,4 @@ This project treats a 2B KRW sale as an enterprise due-diligence threshold, not The project is still a commercial baseline, not a complete enterprise WAF/IDS suite. Production buyers should require follow-on integration of Coraza/OWASP CRS, Suricata EVE ingest, durable database storage, SSO/RBAC, audit logs, signed release artifacts, and production SIEM mapping before internet-edge deployment. -The current library boundary is `crates/waf-ids-core`, a local workspace crate. A submodule is not justified until an independently versioned adapter or SDK exists. +The current library boundary is `crates/wardnet-core`, a local workspace crate. A submodule is not justified until an independently versioned adapter or SDK exists. diff --git a/docs/commercial/buyer-due-diligence.md b/docs/commercial/buyer-due-diligence.md index a9402535..823f0cad 100644 --- a/docs/commercial/buyer-due-diligence.md +++ b/docs/commercial/buyer-due-diligence.md @@ -42,7 +42,7 @@ - [Dockerfile](../../Dockerfile) - [Compose stack](../../deploy/docker-compose.yml) -- [Kubernetes manifest](../../deploy/kubernetes/waf-ids-ai-soc.yaml) +- [Kubernetes manifest](../../deploy/kubernetes/wardnet.yaml) ## Buyer Lab Script diff --git a/docs/deployment/production.md b/docs/deployment/production.md index 34ff1978..5af507cc 100644 --- a/docs/deployment/production.md +++ b/docs/deployment/production.md @@ -5,7 +5,7 @@ Build: ```bash -docker build -t contextualwisdomlab/waf-ids-ai-soc:local . +docker build -t contextualwisdomlab/wardnet:local . ``` Run: @@ -16,8 +16,8 @@ docker run --rm \ -e BIND_ADDR=0.0.0.0:8080 \ -e ADMIN_TOKEN=replace-me \ -e DNSBL_ORIGIN=dnsbl.example \ - -v waf_ids_state:/var/lib/waf-ids-ai-soc \ - contextualwisdomlab/waf-ids-ai-soc:local + -v wardnet_state:/var/lib/wardnet \ + contextualwisdomlab/wardnet:local ``` ## Compose @@ -29,10 +29,10 @@ ADMIN_TOKEN=replace-me docker compose up --build ## Kubernetes -Review `deploy/kubernetes/waf-ids-ai-soc.yaml` before applying. Replace the placeholder admin secret with a secret-manager synchronization flow. +Review `deploy/kubernetes/wardnet.yaml` before applying. Replace the placeholder admin secret with a secret-manager synchronization flow. ```bash -kubectl apply -f deploy/kubernetes/waf-ids-ai-soc.yaml +kubectl apply -f deploy/kubernetes/wardnet.yaml ``` ## Production Requirements diff --git a/docs/design-system.md b/docs/design-system.md index c30523ac..670fea0c 100644 --- a/docs/design-system.md +++ b/docs/design-system.md @@ -1,4 +1,4 @@ -# WAF IDS AI SOC — Design System +# Wardnet — Design System Canonical reference for the admin console UI. Tokens and components are implemented as vanilla HTML/CSS/JS embedded in the binary (`ADMIN_HTML` in `src/lib.rs`, served diff --git a/docs/figma/enterprise-product-architecture.md b/docs/figma/enterprise-product-architecture.md index a1100f20..4ac21c72 100644 --- a/docs/figma/enterprise-product-architecture.md +++ b/docs/figma/enterprise-product-architecture.md @@ -3,7 +3,7 @@ ## Artifact - FigJam: https://www.figma.com/board/JExziD87eUWKLERECUGhWQ?utm_source=codex&utm_content=edit_in_figjam&oai_id=&request_id=a97d2861-82f8-4d43-9d16-27e07b13b10c&architecture=true -- Title: WAF IDS AI SOC 2B KRW Product Architecture +- Title: Wardnet 2B KRW Product Architecture - Added section: `Freshness and SOC Export Evidence` - Added diagram: `Buyer Evidence Manifest Runtime Map` - Figma Code Connect: not used @@ -15,8 +15,8 @@ flowchart LR buyer["Enterprise Buyer Lab"] --> console["Web Admin Console"] operator["SOC Operator"] --> console console --> api["Management API"] - api --> app["waf-ids-ai-soc App Crate"] - app --> core["waf-ids-core Domain Crate"] + api --> app["wardnet App Crate"] + app --> core["wardnet-core Domain Crate"] core --> scoring["Scoring and Readiness"] core --> dnsbl["DNSBL Zone Export"] core --> kpis["SOC KPI Snapshot"] diff --git a/docs/fuzzing.md b/docs/fuzzing.md index b252299c..07e90f2f 100644 --- a/docs/fuzzing.md +++ b/docs/fuzzing.md @@ -16,10 +16,10 @@ entry points for arbitrary input. | Fuzz target | Surface (function) | Invariants | | --------------------------- | ------------------------------------------- | ---------- | -| `fuzz_score_request` | `waf_ids_core::score_request` | no panic on arbitrary path/query/body/IP; `reason` never empty; scoring deterministic | +| `fuzz_score_request` | `wardnet_core::score_request` | no panic on arbitrary path/query/body/IP; `reason` never empty; scoring deterministic | | `fuzz_appdata_json` | `serde_json::from_str::` (state file) | no panic; parsed values round-trip through serde | -| `fuzz_parse_admin_tokens` | `waf_ids_ai_soc::parse_admin_tokens` | no panic; no empty token key; no empty principal actor value | -| `fuzz_dnsbl_zone` | `waf_ids_core::export_dnsbl_zone` / `validate_dnsbl` | no panic; every TXT payload fully escaped (no zone break-out); every published A-record response code is an IPv4 loopback literal (127.0.0.0/8) | +| `fuzz_parse_admin_tokens` | `wardnet::parse_admin_tokens` | no panic; no empty token key; no empty principal actor value | +| `fuzz_dnsbl_zone` | `wardnet_core::export_dnsbl_zone` / `validate_dnsbl` | no panic; every TXT payload fully escaped (no zone break-out); every published A-record response code is an IPv4 loopback literal (127.0.0.0/8) | ## Layout @@ -30,7 +30,7 @@ fuzz/ # separate cargo workspace (isolated from the root corpus//* # committed seed corpus (attack payloads, edge cases) ``` -The property-test mirror lives in `crates/waf-ids-core/tests/fuzz_invariants.rs` +The property-test mirror lives in `crates/wardnet-core/tests/fuzz_invariants.rs` and `tests/fuzz_invariants.rs` (proptest); it enforces the same invariants on stable as part of `cargo test --workspace`. diff --git a/docs/goals/2026-07-02-initial-mvp-goal.md b/docs/goals/2026-07-02-initial-mvp-goal.md index e377c58a..2607f824 100644 --- a/docs/goals/2026-07-02-initial-mvp-goal.md +++ b/docs/goals/2026-07-02-initial-mvp-goal.md @@ -12,7 +12,7 @@ Create and publish a concrete ContextualWisdomLab WAF/IDS/AI SOC project MVP tha ## Acceptance Criteria -- Repository exists under `ContextualWisdomLab/waf-ids-ai-soc`. +- Repository exists under `ContextualWisdomLab/wardnet`. - `cargo test` passes locally and in CI. - `cargo fmt --check` passes locally and in CI. - `GET /admin` renders a management console. diff --git a/docs/migrations/wardnet-rename.md b/docs/migrations/wardnet-rename.md new file mode 100644 index 00000000..877aafd3 --- /dev/null +++ b/docs/migrations/wardnet-rename.md @@ -0,0 +1,18 @@ +# Wardnet rename + +The product, Rust packages, binary, container image, deployment resources, and +documentation now use `wardnet`. Operators should rename persisted volume and +image references during deployment. + +Two environment names remain as temporary startup-only compatibility aliases: + +- `WAF_IDS_STATE_PATH` falls back when `WARDNET_STATE_PATH` is unset. +- `WAF_IDS_CREDENTIALS_PATH` falls back when `WARDNET_CREDENTIALS_PATH` is unset. + +The `WARDNET_*` names take precedence when both are present. The aliases exist +only to avoid breaking an existing deployment during migration; new +configuration must use `WARDNET_*`. + +Prometheus metrics are now emitted with the `wardnet_` prefix. The previous +`waf_ids_` series are emitted in parallel as deprecated compatibility aliases +so existing dashboards and alerts continue to work during migration. diff --git a/docs/ponytail/2026-07-02-complexity-audit.md b/docs/ponytail/2026-07-02-complexity-audit.md index 67af2edd..77f6c573 100644 --- a/docs/ponytail/2026-07-02-complexity-audit.md +++ b/docs/ponytail/2026-07-02-complexity-audit.md @@ -2,11 +2,11 @@ ## Decision -Split reusable WAF/IDS/SOC domain logic into `crates/waf-ids-core` inside the same Cargo workspace. Do not use a git submodule at this stage. +Split reusable WAF/IDS/SOC domain logic into `crates/wardnet-core` inside the same Cargo workspace. Do not use a git submodule at this stage. ## Findings -- Shrink: `src/lib.rs` mixed Axum handlers, persistence, proxying, domain models, scoring, readiness, and DNSBL formatting. Moving deterministic domain logic to `waf-ids-core` reduces the app crate surface without changing runtime behavior. +- Shrink: `src/lib.rs` mixed Axum handlers, persistence, proxying, domain models, scoring, readiness, and DNSBL formatting. Moving deterministic domain logic to `wardnet-core` reduces the app crate surface without changing runtime behavior. - YAGNI: a submodule would add versioning, CI, review, and release overhead before there is an independent library consumer. - Delete: do not create a mock "enterprise WAF engine" to justify the sale package. Keep WAF and IDS engines as explicit future adapters to Coraza/OWASP CRS and Suricata. - Keep: the single binary, JSON state option, admin console, smoke test, and deployment assets remain important for buyer lab time-to-value. @@ -14,7 +14,7 @@ Split reusable WAF/IDS/SOC domain logic into `crates/waf-ids-core` inside the sa ## Resulting Boundaries -- `crates/waf-ids-core`: pure models, validation, upserts, scoring, event retention, feed freshness, KPI snapshots, readiness snapshots, and DNSBL zone formatting. +- `crates/wardnet-core`: pure models, validation, upserts, scoring, event retention, feed freshness, KPI snapshots, readiness snapshots, and DNSBL zone formatting. - `src/lib.rs`: Axum app, admin console, management endpoints, optional persistence, upstream proxying, NDJSON event export, support bundle assembly, and integration tests. - `src/main.rs`: process configuration and server startup. diff --git a/docs/runbooks/operations.md b/docs/runbooks/operations.md index 9b6b7015..b4ddc58a 100644 --- a/docs/runbooks/operations.md +++ b/docs/runbooks/operations.md @@ -4,7 +4,7 @@ ```bash ADMIN_TOKEN=dev-secret \ -WAF_IDS_STATE_PATH=./waf-ids-state.local.json \ +WARDNET_STATE_PATH=./wardnet-state.local.json \ DNSBL_ORIGIN=dnsbl.local \ EVENT_LIMIT=1000 \ cargo run @@ -21,7 +21,7 @@ on each request. | Bootstrap transport | Variable / path | Notes | | --- | --- | --- | | Env (dev / CI) | `ADMIN_TOKEN`, `ADMIN_TOKENS` | Still supported; seeds the registry only | -| Credentials file (preferred for lab/prod packaging) | `WAF_IDS_CREDENTIALS_PATH` | JSON object with `admin_token` and/or `admin_tokens` keys; file values win per key over env | +| Credentials file (preferred for lab/prod packaging) | `WARDNET_CREDENTIALS_PATH` | JSON object with `admin_token` and/or `admin_tokens` keys; file values win per key over env | Example credentials file: @@ -45,8 +45,8 @@ Example credentials file: Token values never appear in audit log payloads. ```bash -WAF_IDS_CREDENTIALS_PATH=./credentials.local.json \ -WAF_IDS_STATE_PATH=./waf-ids-state.local.json \ +WARDNET_CREDENTIALS_PATH=./credentials.local.json \ +WARDNET_STATE_PATH=./wardnet-state.local.json \ cargo run ``` @@ -76,7 +76,7 @@ scripts/smoke.sh The smoke test starts the service on a temporary port with a temporary JSON state file, verifies admin and management surfaces, creates a blocking route, registers a commercial license, imports a threat feed, triggers a blocked gateway request, checks KPIs, readiness, support bundle, and DNSBL export, restarts the process, and verifies that route/license/feed data persisted. -When `WAF_IDS_STATE_PATH` is enabled, the process writes a temporary sibling file and atomically replaces the configured state path. If a management write cannot be persisted, the in-memory mutation is rolled back and the API returns `500`. +When `WARDNET_STATE_PATH` is enabled, the process writes a temporary sibling file and atomically replaces the configured state path. If a management write cannot be persisted, the in-memory mutation is rolled back and the API returns `500`. ## Safe Change Procedure diff --git a/docs/security/threat-model.md b/docs/security/threat-model.md index 8cf0a35b..edaaab7e 100644 --- a/docs/security/threat-model.md +++ b/docs/security/threat-model.md @@ -28,7 +28,7 @@ | Upstream SSRF through routes | Internal network exposure | Upstream scheme validation | Upstream allowlists, egress policy | | Gateway DoS | Availability loss | Rust memory safety, event retention limit | Rate limits, body limits, async event sink | | DNSBL abuse | Reputation damage | Loopback response-code validation | Authoritative DNS service, signing, publisher workflow | -| Secret disclosure | Admin compromise | Support bundle excludes admin token; secrets bootstrapped into credential registry (`WAF_IDS_CREDENTIALS_PATH` preferred over long-lived env); health exposes source label only | External secret manager / SSO, rotation, access review | +| Secret disclosure | Admin compromise | Support bundle excludes admin token; secrets bootstrapped into credential registry (`WARDNET_CREDENTIALS_PATH` preferred over long-lived env); health exposes source label only | External secret manager / SSO, rotation, access review | ## Human Approval Boundary diff --git a/docs/superpowers/plans/2026-07-02-enterprise-product-package.md b/docs/superpowers/plans/2026-07-02-enterprise-product-package.md index 6ed06d0e..c8202fb7 100644 --- a/docs/superpowers/plans/2026-07-02-enterprise-product-package.md +++ b/docs/superpowers/plans/2026-07-02-enterprise-product-package.md @@ -4,7 +4,7 @@ **Goal:** Turn the current runnable WAF/IDS/AI SOC baseline into a stronger 2B KRW enterprise product package by adding a maintainable core library boundary and committed product/design/analytics/complexity evidence. -**Architecture:** One Rust workspace. The root crate remains the web-managed gateway and deployment unit. `crates/waf-ids-core` contains deterministic domain logic that future Coraza, Suricata, MISP, TAXII, DNS, and AI SOC adapters can reuse. +**Architecture:** One Rust workspace. The root crate remains the web-managed gateway and deployment unit. `crates/wardnet-core` contains deterministic domain logic that future Coraza, Suricata, MISP, TAXII, DNS, and AI SOC adapters can reuse. **Tech Stack:** Rust 2024, Axum, Tokio, Reqwest with rustls, Serde, Cargo workspace, shell smoke test, GitHub Actions, Scorecard. @@ -16,7 +16,7 @@ - [x] Use Ponytail to decide the smallest useful complexity reduction. - [x] Use Data Analytics to define sale-readiness and SOC value metrics. - [x] Select a local workspace crate instead of a git submodule. -- [x] Add `crates/waf-ids-core` and move pure domain logic into it. +- [x] Add `crates/wardnet-core` and move pure domain logic into it. - [x] Commit Figma, Product Design, Ponytail, Data Analytics, Superpowers, README, and architecture documentation. - [x] Run full local verification: format, workspace tests, coverage gate, clippy, actionlint, smoke, and diff check. - [ ] Publish a PR and address real review/check findings. diff --git a/docs/superpowers/plans/2026-07-02-feed-freshness-siem-evidence.md b/docs/superpowers/plans/2026-07-02-feed-freshness-siem-evidence.md index ea10e355..f54ee314 100644 --- a/docs/superpowers/plans/2026-07-02-feed-freshness-siem-evidence.md +++ b/docs/superpowers/plans/2026-07-02-feed-freshness-siem-evidence.md @@ -4,7 +4,7 @@ **Goal:** Add buyer-verifiable threat-feed freshness and SOC/SIEM event export evidence to the existing 2B KRW product package. -**Architecture:** Keep one Rust workspace. Add deterministic freshness helpers to `waf-ids-core`, keep NDJSON serialization in the root app crate where `serde_json` already exists, expose both through Axum, and verify through tests and smoke. +**Architecture:** Keep one Rust workspace. Add deterministic freshness helpers to `wardnet-core`, keep NDJSON serialization in the root app crate where `serde_json` already exists, expose both through Axum, and verify through tests and smoke. **Tech Stack:** Rust 2024, Axum, Tokio, Serde, Cargo workspace, shell smoke test, GitHub Actions, FigJam. diff --git a/docs/superpowers/plans/2026-07-02-program-completion-baseline.md b/docs/superpowers/plans/2026-07-02-program-completion-baseline.md index 8ccf65ee..8b388d38 100644 --- a/docs/superpowers/plans/2026-07-02-program-completion-baseline.md +++ b/docs/superpowers/plans/2026-07-02-program-completion-baseline.md @@ -4,7 +4,7 @@ **Goal:** Upgrade the MVP into a standalone program-complete baseline with persistent state, deterministic management writes, smoke verification, and updated CI/docs. -**Architecture:** Keep the app as one Rust Axum binary. Add file-backed JSON persistence behind optional `WAF_IDS_STATE_PATH`, strengthen validators and upserts, retain gateway events with a cap, and prove the end-to-end control loop through a shell smoke test. +**Architecture:** Keep the app as one Rust Axum binary. Add file-backed JSON persistence behind optional `WARDNET_STATE_PATH`, strengthen validators and upserts, retain gateway events with a cap, and prove the end-to-end control loop through a shell smoke test. **Tech Stack:** Rust 2024, Axum 0.8, Tokio, Serde/Serde JSON, Reqwest rustls, Bash/curl for smoke verification, GitHub Actions CI. @@ -14,7 +14,7 @@ - Do not implement fake WAF/IDS engines; keep Coraza/OWASP CRS and Suricata as future real adapters. - Default bind address remains `127.0.0.1:8080`. - `ADMIN_TOKEN` protects management writes when configured. -- `WAF_IDS_STATE_PATH` is optional; absence means seeded in-memory mode. +- `WARDNET_STATE_PATH` is optional; absence means seeded in-memory mode. - CI must run `cargo fmt --check`, `cargo test --locked`, and `cargo clippy --locked -- -D warnings`. --- @@ -31,7 +31,7 @@ - Produces: `AppState::load(config: AppConfig) -> Result` - Produces: `AppState::seeded(admin_token: Option) -> AppState` for existing tests -- [x] Add environment parsing for `WAF_IDS_STATE_PATH`, `DNSBL_ORIGIN`, and `EVENT_LIMIT`. +- [x] Add environment parsing for `WARDNET_STATE_PATH`, `DNSBL_ORIGIN`, and `EVENT_LIMIT`. - [x] Serialize/deserialize `AppData`. - [x] Load state from JSON file when configured; seed and write the file when it does not exist. - [x] Persist successful management writes. diff --git a/docs/superpowers/plans/2026-07-02-waf-ids-ai-soc-mvp.md b/docs/superpowers/plans/2026-07-02-wardnet-mvp.md similarity index 96% rename from docs/superpowers/plans/2026-07-02-waf-ids-ai-soc-mvp.md rename to docs/superpowers/plans/2026-07-02-wardnet-mvp.md index 2e98d7e1..71e48d21 100644 --- a/docs/superpowers/plans/2026-07-02-waf-ids-ai-soc-mvp.md +++ b/docs/superpowers/plans/2026-07-02-wardnet-mvp.md @@ -1,4 +1,4 @@ -# WAF IDS AI SOC MVP Implementation Plan +# Wardnet MVP Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. @@ -89,5 +89,5 @@ - [x] Add CI for formatting and tests. - [x] Add Scorecard workflow and Dependabot baseline. - [x] Run final local verification. -- [x] Create `ContextualWisdomLab/waf-ids-ai-soc` if absent. +- [x] Create `ContextualWisdomLab/wardnet` if absent. - [x] Push the initial implementation to `main`. diff --git a/docs/superpowers/plans/2026-07-03-audit-log-surface.md b/docs/superpowers/plans/2026-07-03-audit-log-surface.md index 46fa3b85..531f27e8 100644 --- a/docs/superpowers/plans/2026-07-03-audit-log-surface.md +++ b/docs/superpowers/plans/2026-07-03-audit-log-surface.md @@ -4,7 +4,7 @@ **Goal:** Add buyer-verifiable management audit logs for admin write operations without storing secrets. -**Architecture:** Store audit records in `AppData` so file persistence and support-bundle export use the existing atomic state boundary. Keep audit record creation deterministic in `waf-ids-core`; keep HTTP header parsing and endpoint routing in `src/lib.rs`. +**Architecture:** Store audit records in `AppData` so file persistence and support-bundle export use the existing atomic state boundary. Keep audit record creation deterministic in `wardnet-core`; keep HTTP header parsing and endpoint routing in `src/lib.rs`. **Tech Stack:** Rust 2024, Axum 0.8, Serde, Tokio, existing JSON state persistence. @@ -15,15 +15,15 @@ - Audit logs must not persist `X-Admin-Token` or request bodies. - Audit logs must cover successful admin writes to routes, threats, DNSBL entries, commercial license metadata, and threat feed imports. - Failed authorization or validation must not create audit records. -- Audit records must survive restart when `WAF_IDS_STATE_PATH` is configured. +- Audit records must survive restart when `WARDNET_STATE_PATH` is configured. --- ### Task 1: Core Audit Model **Files:** -- Modify: `crates/waf-ids-core/src/lib.rs` -- Test: `crates/waf-ids-core/src/lib.rs` +- Modify: `crates/wardnet-core/src/lib.rs` +- Test: `crates/wardnet-core/src/lib.rs` **Interfaces:** - Produces: `AuditLogEntry { id, timestamp_unix, actor, action, resource, resource_id, outcome }` @@ -173,7 +173,7 @@ Expected: PASS. ### Task 3: Buyer Evidence And Smoke Coverage **Files:** -- Modify: `crates/waf-ids-core/src/lib.rs` +- Modify: `crates/wardnet-core/src/lib.rs` - Modify: `src/lib.rs` - Modify: `scripts/smoke.sh` - Modify: `docs/commercial/20b-krw-sale-readiness.md` diff --git a/docs/superpowers/plans/2026-07-03-buyer-evidence-manifest.md b/docs/superpowers/plans/2026-07-03-buyer-evidence-manifest.md index fd12b522..59c131c0 100644 --- a/docs/superpowers/plans/2026-07-03-buyer-evidence-manifest.md +++ b/docs/superpowers/plans/2026-07-03-buyer-evidence-manifest.md @@ -6,7 +6,7 @@ Advance the 2B KRW buyer-readiness baseline by giving enterprise evaluators one ## Scope -- Add a pure `BuyerEvidenceManifest` model and deterministic snapshot function to `crates/waf-ids-core`. +- Add a pure `BuyerEvidenceManifest` model and deterministic snapshot function to `crates/wardnet-core`. - Expose `GET /api/commercial/evidence-manifest` from the app crate. - Include the manifest in `GET /api/support-bundle` so support handoff and buyer due diligence share the same evidence map. - Add the manifest to the embedded admin console. diff --git a/docs/superpowers/specs/2026-07-02-enterprise-product-package-design.md b/docs/superpowers/specs/2026-07-02-enterprise-product-package-design.md index 11a53260..9fe0ec04 100644 --- a/docs/superpowers/specs/2026-07-02-enterprise-product-package-design.md +++ b/docs/superpowers/specs/2026-07-02-enterprise-product-package-design.md @@ -28,14 +28,14 @@ Move the current WAF/IDS/AI SOC runtime from a commercial baseline into a buyer- ## Architecture Decision -Create `crates/waf-ids-core` inside the same Cargo workspace. It owns pure domain models and deterministic logic: +Create `crates/wardnet-core` inside the same Cargo workspace. It owns pure domain models and deterministic logic: - route, threat, DNSBL, feed, event, license, KPI, and readiness models - route/threat/DNSBL/feed validation and upsert semantics - request scoring and DNSBL zone export - event retention, KPI snapshot, and readiness snapshot -The root `waf-ids-ai-soc` crate keeps process startup, Axum routes, persistence, upstream proxying, admin console, and integration tests. This boundary gives a future SDK/adapters a stable domain surface without adding submodule governance overhead. +The root `wardnet` crate keeps process startup, Axum routes, persistence, upstream proxying, admin console, and integration tests. This boundary gives a future SDK/adapters a stable domain surface without adding submodule governance overhead. ## Product Design Scope @@ -74,7 +74,7 @@ Guardrails: ## Ponytail Audit Outcome -- Shrink: move pure domain logic out of `src/lib.rs` into `crates/waf-ids-core`. +- Shrink: move pure domain logic out of `src/lib.rs` into `crates/wardnet-core`. - YAGNI: do not add a git submodule before an independent release boundary exists. - Delete: do not build a fake WAF/IDS engine; keep Coraza and Suricata as explicit future adapters. - Net: reduce app crate complexity while preserving the single-binary sales-demo path. diff --git a/docs/superpowers/specs/2026-07-02-feed-freshness-siem-evidence-design.md b/docs/superpowers/specs/2026-07-02-feed-freshness-siem-evidence-design.md index 9af26ef4..73584bbf 100644 --- a/docs/superpowers/specs/2026-07-02-feed-freshness-siem-evidence-design.md +++ b/docs/superpowers/specs/2026-07-02-feed-freshness-siem-evidence-design.md @@ -25,7 +25,7 @@ Strengthen the 2B KRW buyer-lab package with evidence that threat updates are fr Keep the existing Rust workspace boundary: -- `crates/waf-ids-core`: pure freshness classification, KPI counts, and readiness checks. +- `crates/wardnet-core`: pure freshness classification, KPI counts, and readiness checks. - `src/lib.rs`: Axum routes, NDJSON event export, support bundle assembly, admin console, and HTTP tests. - `scripts/smoke.sh`: buyer-lab verification of freshness, readiness, support bundle, and event export. diff --git a/docs/superpowers/specs/2026-07-02-program-completion-baseline-design.md b/docs/superpowers/specs/2026-07-02-program-completion-baseline-design.md index 36503c7f..977b8bc2 100644 --- a/docs/superpowers/specs/2026-07-02-program-completion-baseline-design.md +++ b/docs/superpowers/specs/2026-07-02-program-completion-baseline-design.md @@ -8,8 +8,8 @@ The previous delivery completed the published MVP: a Rust Axum gateway with web The program is complete for this baseline when all of these are true: -1. The service can load and persist routes, threat indicators, DNSBL entries, events, and the next event id through a JSON state file configured by `WAF_IDS_STATE_PATH`. -2. If `WAF_IDS_STATE_PATH` is absent, the service still runs in seeded in-memory mode for demos and tests. +1. The service can load and persist routes, threat indicators, DNSBL entries, events, and the next event id through a JSON state file configured by `WARDNET_STATE_PATH`. +2. If `WARDNET_STATE_PATH` is absent, the service still runs in seeded in-memory mode for demos and tests. 3. The health endpoint reports persistence mode, DNSBL origin, and event retention settings. 4. Management writes require `ADMIN_TOKEN` when configured, validate records, upsert deterministic records, and persist successful changes before returning. 5. Gateway events are retained with a configurable cap and persisted when file-backed state is enabled. diff --git a/docs/superpowers/specs/2026-07-02-waf-ids-ai-soc-design.md b/docs/superpowers/specs/2026-07-02-wardnet-design.md similarity index 98% rename from docs/superpowers/specs/2026-07-02-waf-ids-ai-soc-design.md rename to docs/superpowers/specs/2026-07-02-wardnet-design.md index 1759ac16..e7d8bafb 100644 --- a/docs/superpowers/specs/2026-07-02-waf-ids-ai-soc-design.md +++ b/docs/superpowers/specs/2026-07-02-wardnet-design.md @@ -1,4 +1,4 @@ -# WAF IDS AI SOC Design +# Wardnet Design ## Objective diff --git a/docs/superpowers/specs/2026-07-03-buyer-evidence-manifest-design.md b/docs/superpowers/specs/2026-07-03-buyer-evidence-manifest-design.md index c6fde1a0..39aa23bb 100644 --- a/docs/superpowers/specs/2026-07-03-buyer-evidence-manifest-design.md +++ b/docs/superpowers/specs/2026-07-03-buyer-evidence-manifest-design.md @@ -6,7 +6,7 @@ The runtime already exposes commercial readiness, feed freshness, SOC NDJSON exp ## Design -Keep the evidence manifest in `waf-ids-core` because it is deterministic domain evidence assembled from existing `AppData`, KPI, and readiness snapshots. +Keep the evidence manifest in `wardnet-core` because it is deterministic domain evidence assembled from existing `AppData`, KPI, and readiness snapshots. The app crate remains responsible only for: diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index efa8fb36..772c04df 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -1,4 +1,4 @@ -# Coverage-guided fuzzing harness for waf-ids-ai-soc. +# Coverage-guided fuzzing harness for wardnet. # # This is a SEPARATE workspace (note the empty `[workspace]` table below) so it # is excluded from the root workspace. `cargo build`/`cargo test --workspace` @@ -10,7 +10,7 @@ # - libfuzzer-sys MIT OR Apache-2.0 OR NCSA # - arbitrary MIT OR Apache-2.0 [package] -name = "waf-ids-fuzz" +name = "wardnet-fuzz" version = "0.0.0" publish = false edition = "2021" @@ -22,8 +22,8 @@ cargo-fuzz = true libfuzzer-sys = "0.4" arbitrary = { version = "1", features = ["derive"] } serde_json = "1" -waf-ids-core = { path = "../crates/waf-ids-core" } -waf-ids-ai-soc = { path = ".." } +wardnet-core = { path = "../crates/wardnet-core" } +wardnet = { path = ".." } [[bin]] name = "fuzz_score_request" diff --git a/fuzz/fuzz_targets/fuzz_appdata_json.rs b/fuzz/fuzz_targets/fuzz_appdata_json.rs index 0abaacb8..7c21f65a 100644 --- a/fuzz/fuzz_targets/fuzz_appdata_json.rs +++ b/fuzz/fuzz_targets/fuzz_appdata_json.rs @@ -8,7 +8,7 @@ //! any value that deserializes must round-trip back through serde_json. use libfuzzer_sys::fuzz_target; -use waf_ids_core::AppData; +use wardnet_core::AppData; fuzz_target!(|data: &[u8]| { let Ok(text) = std::str::from_utf8(data) else { diff --git a/fuzz/fuzz_targets/fuzz_dnsbl_zone.rs b/fuzz/fuzz_targets/fuzz_dnsbl_zone.rs index 4cf0937f..829486f0 100644 --- a/fuzz/fuzz_targets/fuzz_dnsbl_zone.rs +++ b/fuzz/fuzz_targets/fuzz_dnsbl_zone.rs @@ -14,7 +14,7 @@ use arbitrary::Arbitrary; use libfuzzer_sys::fuzz_target; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr}; -use waf_ids_core::{export_dnsbl_zone, validate_dnsbl, DnsblEntry}; +use wardnet_core::{export_dnsbl_zone, validate_dnsbl, DnsblEntry}; /// A response code drawn from the raw fuzz bytes: arbitrary strings plus real IP /// literals (loopback, non-loopback IPv4, IPv6) so the zone A-record invariant diff --git a/fuzz/fuzz_targets/fuzz_parse_admin_tokens.rs b/fuzz/fuzz_targets/fuzz_parse_admin_tokens.rs index 80fadda8..6989b0d9 100644 --- a/fuzz/fuzz_targets/fuzz_parse_admin_tokens.rs +++ b/fuzz/fuzz_targets/fuzz_parse_admin_tokens.rs @@ -1,5 +1,5 @@ #![no_main] -//! Fuzz the admin-token config parser: `waf_ids_ai_soc::parse_admin_tokens`. +//! Fuzz the admin-token config parser: `wardnet::parse_admin_tokens`. //! //! This parses the `ADMIN_TOKENS` operator config string //! (`token:actor[:role],...`) into an RBAC principal map. Malformed or @@ -9,7 +9,7 @@ //! * every actor value is non-empty (defaults to "admin"). use libfuzzer_sys::fuzz_target; -use waf_ids_ai_soc::parse_admin_tokens; +use wardnet::parse_admin_tokens; fuzz_target!(|data: &[u8]| { let Ok(raw) = std::str::from_utf8(data) else { diff --git a/fuzz/fuzz_targets/fuzz_score_request.rs b/fuzz/fuzz_targets/fuzz_score_request.rs index ea75f48b..c7119e9c 100644 --- a/fuzz/fuzz_targets/fuzz_score_request.rs +++ b/fuzz/fuzz_targets/fuzz_score_request.rs @@ -1,5 +1,5 @@ #![no_main] -//! Fuzz the core WAF request scorer: `waf_ids_core::score_request`. +//! Fuzz the core WAF request scorer: `wardnet_core::score_request`. //! //! This is the primary untrusted-input surface (surfaced via CodeGraph: //! `codegraph_explore "score_request anomaly_signal normalize decode ..."`). @@ -16,7 +16,7 @@ use arbitrary::Arbitrary; use libfuzzer_sys::fuzz_target; use std::net::{IpAddr, Ipv4Addr}; -use waf_ids_core::{score_request, DnsblEntry, Severity, ThreatIndicator}; +use wardnet_core::{score_request, DnsblEntry, Severity, ThreatIndicator}; #[derive(Arbitrary, Debug)] struct Indicator { diff --git a/scripts/smoke.sh b/scripts/smoke.sh index 5df0c730..c1eff7fc 100755 --- a/scripts/smoke.sh +++ b/scripts/smoke.sh @@ -31,7 +31,7 @@ start_server() { cd "$ROOT_DIR" BIND_ADDR="127.0.0.1:$PORT" \ ADMIN_TOKEN="$ADMIN_TOKEN_VALUE" \ - WAF_IDS_STATE_PATH="$STATE_FILE" \ + WARDNET_STATE_PATH="$STATE_FILE" \ DNSBL_ORIGIN="dnsbl.test" \ EVENT_LIMIT="5" \ cargo run --quiet @@ -78,7 +78,7 @@ assert_json_field "$health" 'data["persistence"] == "file"' assert_json_field "$health" 'data["dnsbl_origin"] == "dnsbl.test"' assert_json_field "$health" 'data["event_limit"] == 5' -curl -fsS "$BASE_URL/admin" | grep -q "ContextualWisdomLab WAF/IDS/AI SOC Gateway" +curl -fsS "$BASE_URL/admin" | grep -q "

Wardnet

" unauthorized_code="$( curl -sS -o /dev/null -w '%{http_code}' \ diff --git a/src/credentials.rs b/src/credentials.rs index 02b7f39e..2f3c5899 100644 --- a/src/credentials.rs +++ b/src/credentials.rs @@ -16,7 +16,7 @@ pub const CRED_ADMIN_TOKENS: &str = "admin_tokens"; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)] #[serde(rename_all = "snake_case")] pub enum CredentialSource { - /// At least one secret came from `WAF_IDS_CREDENTIALS_PATH`. + /// At least one secret came from `WARDNET_CREDENTIALS_PATH`. File, /// Secrets came only from env bootstrap (`ADMIN_TOKEN` / `ADMIN_TOKENS`). Env, diff --git a/src/lib.rs b/src/lib.rs index 8f54751d..42776ec1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,14 +19,14 @@ use tokio::{ fs, sync::{Mutex, RwLock}, }; -use waf_ids_core::{ +use wardnet_core::{ AppData, BLOCK_SCORE, buyer_evidence_manifest_at, commercial_readiness_snapshot_at, enforce_event_limit, kpi_snapshot_at, prometheus_exposition, rate_limit_step, record_audit_log, select_route, signature_catalog, threat_feed_freshness_snapshot, upsert_dnsbl, upsert_route, upsert_threat, upsert_threat_feed, validate_commercial_profile, validate_dnsbl, validate_route, validate_threat, validate_threat_feed_import, }; -pub use waf_ids_core::{ +pub use wardnet_core::{ AuditLogEntry, BuyerEvidenceEndpoint, BuyerEvidenceManifest, BuyerEvidenceRuntimeCounts, CommercialProfile, CommercialReadiness, DnsblEntry, EnforcementMode, LicenseStatus, NewAuditLogEntry, ProductEdition, ReadinessCheck, ReadinessStatus, RouteConfig, ScoredRequest, @@ -519,7 +519,7 @@ fn clearfolio_tenant_headers(config: &ClearfolioConfig) -> [(&'static str, &str) ] } -/// Renders a waf-ids document to plain-text bytes for Clearfolio ingest. +/// Renders a Wardnet document to plain-text bytes for Clearfolio ingest. /// Clearfolio only blocks `hwp`/`hwpx`, so text uploads convert normally. /// Returns `(filename, bytes)` or `None` for an unknown kind. fn clearfolio_document(kind: &str, data: &AppData) -> Option<(String, Vec)> { @@ -563,7 +563,7 @@ async fn clearfolio_config(State(state): State) -> Json, @@ -2630,7 +2630,7 @@ const ADMIN_HTML: &str = r##" -WAF IDS AI SOC — Console +Wardnet — Console