Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ target
.git
.github
.DS_Store
wardnet-state.local.json
Comment thread
coderabbitai[bot] marked this conversation as resolved.
waf-ids-state.local.json
*.profraw
*.profdata
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/target
/wardnet-state*.json
Comment thread
seonghobae marked this conversation as resolved.
# Temporary migration alias; remove with WAF_IDS_STATE_PATH support.
/waf-ids-state*.json
/runtime-state*.json
6 changes: 3 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 <crate>`, 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.
Expand All @@ -36,7 +36,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

Expand All @@ -56,4 +56,4 @@ As of 2026-08-04, code-owner review requirements (`require_code_owner_reviews` i
protection, `require_code_owner_review` in rulesets) are disabled across the ContextualWisdomLab
org: there is a single maintainer (solo developer), so a code-owner approval gate can never be
satisfied. This is ON HOLD until the org has multiple maintainers — do NOT re-enable these
settings or add CODEOWNERS-based merge gates before then.
settings or add CODEOWNERS-based merge gates before then.
30 changes: 22 additions & 8 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -23,7 +23,7 @@ Other common commands:
```bash
cargo run # serve on 127.0.0.1:8080; open /admin
cargo test --workspace <test_name_filter> # 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
```

Expand All @@ -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/<target>/`.

The core stays an in-repo workspace crate on purpose (no git submodule) until it has an independent release cadence.
Expand All @@ -57,12 +57,24 @@ 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`), `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`, `WAF_IDS_CREDENTIALS_PATH` (optional JSON bootstrap file for process-local credentials/config), `ADMIN_TOKEN` (bootstrap transport for the shared write token), and `ADMIN_TOKENS` (bootstrap transport for comma-separated `token:actor[:role]` RBAC entries). `ADMIN_TOKEN` and `ADMIN_TOKENS` are loaded into `CredentialRegistry` before the server starts; handlers read the in-process registry/AppState copy, not raw env vars. KEV imports use the built-in CISA endpoint at runtime; only in-crate tests can override it through `AppState::with_kev_catalog_url` to point at a loopback mock server.
Read in `run_from_env` (`src/lib.rs`): `BIND_ADDR` (default
`127.0.0.1:8080`), `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`,
`WARDNET_CREDENTIALS_PATH` (optional JSON bootstrap file for process-local
credentials/config; `WAF_IDS_CREDENTIALS_PATH` remains a legacy fallback),
Comment thread
coderabbitai[bot] marked this conversation as resolved.
`ADMIN_TOKEN` (bootstrap transport for the shared write token), and
`ADMIN_TOKENS` (bootstrap transport for comma-separated `token:actor[:role]`
RBAC entries). `ADMIN_TOKEN` and `ADMIN_TOKENS` are loaded into
`CredentialRegistry` before the server starts; handlers read the in-process
registry/AppState copy, not raw env vars. KEV imports use the built-in CISA
endpoint at runtime; only in-crate tests can override it through
`AppState::with_kev_catalog_url` to point at a loopback mock server.

## Key Conventions

Expand All @@ -71,7 +83,9 @@ 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

Expand Down
42 changes: 21 additions & 21 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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]
Expand All @@ -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"] }
Expand Down
14 changes: 7 additions & 7 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -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.

Expand Down Expand Up @@ -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
```
Expand Down Expand Up @@ -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.

Expand Down
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
Loading
Loading