Skip to content
Merged
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
28 changes: 25 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,9 @@ jobs:
- name: Pytest
# The perf gate is excluded here — it runs as its own single-runner job
# (perf-gate) where timing is stable; noisier macOS / Windows runners
# would make a latency budget flaky.
run: uv run pytest tests/ packages/ sdks/python/tests -x -q --tb=short -m "not perf"
# would make a latency budget flaky. The red-team gate is likewise its
# own job (redteam-gate) so the security suite is a named exit gate.
run: uv run pytest tests/ packages/ sdks/python/tests -x -q --tb=short -m "not perf and not redteam"

# ---------------------------------------------------------------------------
# Secrets scan — blocks if a real credential is planted in a PR.
Expand Down Expand Up @@ -333,6 +334,27 @@ jobs:
- name: Gateway p99 overhead gate
run: uv run pytest tests/perf/ -v -m perf

# ---------------------------------------------------------------------------
# Red-team — adversarial security probe suites (Step 7.3). Deterministic +
# in-process, so a single runner; named separately so the security gate is a
# first-class CI exit gate (prompt injection ≥95% block + no untrusted chunk
# in a system-trust position; ACL bypass; PII egress; tenant escape).
# ---------------------------------------------------------------------------
redteam-gate:
name: Red-team security gate
needs: changes
if: needs.changes.outputs.python == 'true'
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- uses: astral-sh/setup-uv@v7
with:
enable-cache: true
cache-dependency-glob: "uv.lock"
- run: uv sync --all-packages
- name: Adversarial probe suites
run: uv run pytest tests/redteam/ -v -m redteam

# ---------------------------------------------------------------------------
# IaC validation — terraform validate + helm lint (no live cluster needed)
# ---------------------------------------------------------------------------
Expand Down Expand Up @@ -396,7 +418,7 @@ jobs:
# ---------------------------------------------------------------------------
ci-pass:
name: CI passed
needs: [lint-test, secrets-scan, schema-drift, log-gates, audit, iac, proto-lint, proto-drift, perf-gate]
needs: [lint-test, secrets-scan, schema-drift, log-gates, audit, iac, proto-lint, proto-drift, perf-gate, redteam-gate]
if: always()
runs-on: ubuntu-22.04
steps:
Expand Down
3 changes: 2 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ AgentContextOS/
| `rag-provenance` v0.1.0 (Step 5.1) | `rag_provenance` | HMAC-signed per-query provenance: `ProvenanceSigner` (HMAC-SHA256 over the canonical record JSON, modeled on the webhook signer), `ProvenanceRecorder` (build → sign → store → emit; **degrade-open**, privacy-by-default hashes). Backed by the `ProvenanceStore` SPI + `NoopProvenanceStore` and the `ProvenanceRecord` / `ProvenanceCitation` / `ProvenanceSignature` / `SignedProvenanceRecord` / `ProvenanceVerification` / `SpanRecord` core types. The read-side `TraceCollector` (a `rag-observability` `SpanProcessor`) captures per-query spans by `rag.trace_id` for `GET /v1/query/{id}/trace` |
| `rag-feedback` v0.1.0 (Step 5.4) | `rag_feedback` | Online feedback & implicit signals: `FeedbackRecorder` (normalise signal → `[-1,1]` score → **PII-redact** comment via an injected `PIIDetector` → tenant-scoped `FeedbackStore.put` → `feedback.recorded` event; **degrade-open**) + pure `aggregate_feedback` → `FeedbackStats`. Backed by the `FeedbackStore` SPI + `NoopFeedbackStore` and the `FeedbackRecord` / `FeedbackStats` / `FeedbackKind` / `FeedbackSignal` core types; `FeedbackRequest` / `FeedbackAck` wire types. Drives `POST /v1/feedback` (explicit + implicit, one `signal` enum) + the `GET /v1/status/feedback` per-tenant dashboard |
| `rag-drift` v0.1.0 (Step 5.5) | `rag_drift` | Drift monitors: `population_stability_index` (pure binned PSI) + `DriftMonitor` (bounded reference/current windows, PSI or mean-drop, tri-state verdict) + `DriftMonitorRegistry` (five monitors — query distribution / embedding PSI / retrieval score / citation-clickthrough / faithfulness; `observe` / `evaluate` with transition-edge `drift.detected` event + webhook). Backed by the `DriftSnapshot` / `DriftReport` / `DriftMetric` / `DriftMethod` / `DriftStatus` core types. Infra-scoped (like breakers), fed from the query + feedback paths; drives `GET /v1/status/drift` + `POST /v1/status/drift/{metric}/rebaseline` |
| `rag-injection` v0.1.0 (Step 7.3) | `rag_injection` | Prompt-injection guard: `PromptInjectionGuard.inspect(ctx, chunks)` drops chunks that try to hijack the model before the LLM (pluggable `InjectionDetector` + dependency-free `HeuristicInjectionDetector` — attack-grammar regexes), paired with `INJECTION_RESISTANT_SYSTEM_PROMPT` + `build_user_message` so untrusted context is fenced *data* in the user turn (never a system-trust position). `InjectionConfig` / `InjectionResult` / `InjectionAction` / `InjectionVerdict` / `InjectionMatch`; `injection.blocked` event; degrade-open. Wired on every answer surface (`/v1/query`, OpenAI chat, MCP), off by default (`cfg.injection`) |
| `rag-gateway` v0.10.0 | `rag_gateway` | Gateway service over four surfaces: REST (`/v1/query`, `/v1/retrieve`, `/v1/corpora`, `/v1/ingest/document`, `/v1/query/{id}/trace`), gRPC (`RagService`), MCP (`query`/`retrieve`/`ingest` tools), and OpenAI-compatible (`/v1/embeddings`, `/v1/chat/completions`, `/v1/models`) — all sharing one in-process Phase 2 wiring |

## Key architecture patterns
Expand Down Expand Up @@ -239,7 +240,7 @@ Add a **Documentation** section listing the doc file(s) added or updated so revi
`rag-config`, `rag-policy`, or `rag-backends`. The dependency graph is:
gateway → observability → core; config → core; policy → core; backends → core;
provenance → observability → core; feedback → observability → core;
drift → observability → core.
drift → observability → core; injection → observability → core.
- Every SPI method takes `ctx: RequestContext` first (Step 1.1a onward).
- Every retrieval / ingest / egress call site consults `PolicyEngine` (Step 1.1c onward).
- Hot-path discipline: Pydantic at SPI boundaries only; `model_construct()` / msgspec inside loops.
23 changes: 17 additions & 6 deletions TRACKER.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,13 @@
| | |
|---|---|
| **Last updated** | 2026-06-08 |
| **Current phase** | Phase 7 — Pilot, Harden, GA (**2 / 10 steps**) — Phases 0–6 ✅ complete |
| **Overall** | **76 / 84 steps** — Phases 0–6 complete |
| **Next action** | **Step 7.3Red-team**: prompt injection, ACL bypass, PII egress, and tenant-escape probes as an adversarial test suite. |
| **Current phase** | Phase 7 — Pilot, Harden, GA (**3 / 10 steps**) — Phases 0–6 ✅ complete |
| **Overall** | **77 / 84 steps** — Phases 0–6 complete |
| **Next action** | **Step 7.4Design-partner pilots**: onboard 2–3 design partners across distinct verticals; per-pilot success criteria + weekly KPIs; case study documented. |

**Recently shipped**

- **7.3** ✅ Red-team / security — turns the governance stack into an **adversarial probe gate** across four classes (prompt injection / PII egress / ACL bypass / tenant escape) under a `redteam` marker + a first-class **`redteam-gate`** CI job (`task redteam`). Closes the verified injection gap: new **`rag-injection`** package — a pluggable `InjectionDetector` (dependency-free `HeuristicInjectionDetector`, regexes anchored on attack *grammar* so benign prose isn't flagged) + `PromptInjectionGuard.inspect` that drops hijack chunks **before the LLM**, paired with `INJECTION_RESISTANT_SYSTEM_PROMPT` + `build_user_message` so untrusted context is **fenced data in the user turn, never a system-trust position** (fixes the OpenAI-chat surface that put context in a `system` message); wired on `/v1/query` + `/v1/chat/completions` + MCP; off by default (`cfg.injection`); PII-free `injection.blocked` event. Deterministic gate: a ≥ 500 known + ≥ 500 generated corpus (`eval/redteam_v0/`) hits **96.6 % block** (≥ 95 % bar) at **0 false positives**, plus the no-system-position invariant end-to-end — building the corpus *hardened the detector* (~20 missed phrasings). New PII-egress probe over `PiiPolicyEngine` (zero leakage, second-detector verified); `pip-audit` is the CVE gate; the external pentest is a documented process item. Injection types stay internal (no attacker signal, `dist/schemas`/`openapi` untouched). [#166](https://github.com/officialCodeWork/AgentContextOS/pull/166)
- **7.2** ✅ Chaos engineering — a deterministic in-process **kill-matrix gate** (`eval/gateway_chaos_v0/kill_matrix.py` + `tests/perf/test_chaos_kill_matrix.py`) that extends 7.1 from the three retrieval backends to the **full hot-path set** (vector/keyword/graph/embedder/retrieval_cache/reranker/llm): kill each backend in turn (100% unavailable) behind the real breakers + fallback, drive `/v1/query`, and assert **no single failure 5xx-es the gateway** (no 5xx, on-path retrieval breaker opens, expected degraded shape; a seeded keyword corpus + real `hydrate` make rerank/generate actually run). **Chaos fixed what it found** — the matrix exposed that a down **retrieval cache** or **reranker** 5xx-ed, so the gateway gained two minimal **degrade-open** guards (`gateway.cache.degraded` → miss on `/v1/query` + `/v1/retrieve`; `gateway.rerank.degraded` → retrieval-only, honouring `RerankPipeline`'s "caller decides" contract); LLM + embedder already degraded. **LitmusChaos cluster manifests** (`infra/chaos/`: gateway pod-delete + backend `pod-network-loss`/`latency` with httpProbe acceptance) as the cluster runbook + `task chaos-kill`. No `dist/`/SPI/config change. [#165](https://github.com/officialCodeWork/AgentContextOS/pull/165)
- **7.1** ✅ Load + chaos testing — a **chaos-under-load** CI gate (`eval/gateway_chaos_v0/`): drives the in-process gateway under concurrent load while injecting backend faults (`FaultSpec` + `Chaos{Vector,Keyword,Graph}RetrievalBackend` SPI wrappers behind real breakers) and asserts **graceful degradation** — no 5xx, 100% success, the failing backend's breaker opens (validating the Phase-4 breakers + fallback, builds no new resilience); `tests/perf/test_chaos_under_load.py` (timing-independent, `perf`-marked) + `task chaos-test`. An extended **Locust v1 suite** (weighted read/write mix + varied queries + a ramp `LoadTestShape`) + documented **acceptance targets** (≥ 1000 RPS sustained, e2e p99 < 500 ms) as a cluster runbook — [#164](https://github.com/officialCodeWork/AgentContextOS/pull/164)

Expand Down Expand Up @@ -68,8 +69,8 @@
| 4 | Reliability | 6 | **6** | 0 |
| 5 | Eval & Observability | 7 | **7** | 0 |
| 6 | Governance & Tenancy | 10 | **10** | 0 |
| 7 | Pilot, Harden, GA | 10 | **2** | 8 |
| **Total** | | **84** | **76** | **8** |
| 7 | Pilot, Harden, GA | 10 | **3** | 7 |
| **Total** | | **84** | **77** | **7** |

---

Expand Down Expand Up @@ -802,7 +803,7 @@ New ground — the only prior crypto was HMAC signing. The V1 plan calls for en
|------|-------|:------:|----------------------|
| 7.1 | Load testing | ✅ | [#164](https://github.com/officialCodeWork/AgentContextOS/pull/164) — chaos-under-load CI gate (`eval/gateway_chaos_v0` fault backends + harness; `tests/perf/test_chaos_under_load.py`; graceful degradation: no 5xx + breaker opens); Locust v1 suite (varied-query mix + ramp shape); acceptance targets + runbook |
| 7.2 | Chaos engineering | ✅ | [#165](https://github.com/officialCodeWork/AgentContextOS/pull/165) — in-process **kill-matrix** gate (kill each hot-path backend → no 5xx, breaker opens, expected degraded shape); two new gateway **degrade-open** guards (retrieval cache + reranker); LitmusChaos cluster manifests (`infra/chaos/`) + `task chaos-kill` |
| 7.3 | Red-team | | Prompt injection, ACL bypass, PII egress, tenant-escape probes |
| 7.3 | Red-team | | [#166](https://github.com/officialCodeWork/AgentContextOS/pull/166) — adversarial probe gate (injection / PII / ACL / tenant-escape) under a `redteam` marker + `redteam-gate` CI job; new **`rag-injection`** guard (≥ 95 % block, no untrusted chunk in a system-trust position) wired on every answer surface; PII-egress probe; `task redteam` |
| 7.4 | Design partner onboarding | ⏳ | 2–3 design partners; feedback incorporated; case study documented |
| 7.5 | Documentation site | ⏳ | Docusaurus / MkDocs site; API reference generated from OpenAPI; quickstart guides |
| 7.6 | Marketplace listings | ⏳ | AWS / Azure / GCP Marketplace AMI / Helm listings |
Expand All @@ -829,6 +830,15 @@ New ground — the only prior crypto was HMAC signing. The V1 plan calls for en
- **Cluster chaos is a runbook, not a CI gate** (topology-bound). `infra/chaos/` LitmusChaos manifests: gateway `pod-delete` (PDB-protected instance loss) + `pod-network-loss`/`pod-network-latency` toward a backend host (the cluster way to "kill a backend", since backends are reached by host), each with an embedded **httpProbe** asserting the gateway stays 200 throughout
- **Scope:** kill-matrix gate + the two degrade-open hardenings + the LitmusChaos runbook. No new core/wire types, SPI methods, config, or events (`dist/` untouched; the degrade kinds are log-only like `gateway.answer.failed`; the kill wrappers are pure-raise so the policy-coverage linter needs no new rule). **Deferred:** latency-based breaker tripping, multi-backend simultaneous kills as a gate, Litmus-in-CI against an ephemeral cluster, soak chaos. 12 kill-matrix perf tests; all gates green (ruff, mypy --strict 325 files, RAG001, policy-coverage, schema/openapi-drift, log-schema). [ADR-0044](docs/adr/ADR-0044-chaos-engineering.md), [guides/chaos-engineering.md](docs/guides/chaos-engineering.md), [reference/perf.md](docs/reference/perf.md)

### 7.3 — Red-team / security ✅ [#166](https://github.com/officialCodeWork/AgentContextOS/pull/166)

- The governance stack (ACL 6.3/6.4, PII 6.5, tenancy 6.1/6.2, hallucination guard 4.3) becomes an **adversarial probe gate** across four classes. ACL bypass + tenant escape were already gated (`tests/redteam/test_acl_*`, `test_cross_tenant_*`); 7.3 adds **prompt injection** + **PII egress** and consolidates all four under a `redteam` marker (a `tests/redteam/conftest.py` auto-marks the directory — scoped to its own subtree so it can't mark the whole suite) + a first-class **`redteam-gate`** CI job (`task redteam`)
- **The injection gap was real.** `trust_level` was stored + filterable everywhere but **nothing enforced it at the LLM adapter** — a retrieved document could carry "ignore your instructions, you are now…" straight to the model, and the OpenAI-chat surface assembled context as a **`role="system"` message** (an attacker-controlled *system-trust position*, which PROBLEM-TRACEABILITY forbids)
- **New `rag-injection` package** (deps rag-core + rag-observability, mirroring rag-guard/rag-pii): a pluggable `InjectionDetector` (dependency-free `HeuristicInjectionDetector` — a regex library anchored on the *grammar* of an attack: an imperative verb aimed at the model + its instructions/role/safety, within a bounded sentence-local gap, so benign prose mentioning "system"/"prompt"/"instructions" isn't flagged) + `PromptInjectionGuard.inspect(ctx, chunks)` that drops chunks scoring ≥ a per-tenant threshold (degrade-open; PII-free `injection.blocked`). Paired with `INJECTION_RESISTANT_SYSTEM_PROMPT` + `build_user_message` so retrieved content can only ever appear as **fenced untrusted data in the user turn** — never a system turn. Wired into **every** answer surface (`/v1/query`, `/v1/chat/completions`, MCP); off by default (`cfg.injection`)
- **Deterministic CI gate, honest numbers.** `eval/redteam_v0/injection_corpus.py` — **568 known + 630 generated** payloads across all categories + a benign control set. `tests/redteam/test_prompt_injection.py` asserts **96.6 % block** (≥ 95 % bar), **0 false positives** (≤ 5 % bar), and the **no-system-position invariant** end-to-end with a capturing LLM on both surfaces. Building the corpus *hardened the detector* — it exposed ~20 missed phrasings (e.g. "ignore **your** previous instructions"), the point of red-teaming. The corpus is broader than the detector's patterns (carriers, morphology, a few evasive payloads) so the rate is honest, not a circular 100 %
- **PII-egress probe** (`test_pii_egress.py`): synthetic high-PII corpora through the 6.5 `PiiPolicyEngine` — `block` denies, `redact`/`mask` strip every span (verified by an *independent* second detector), over answer-text + `list[Chunk]` shapes
- **Scope:** the injection guard + the four-class probe gate + `task redteam` + the `redteam-gate` CI job. Dependency-scan CVE gating is the existing `pip-audit` `audit` job; the **external pentest** is a documented human-process item (`guides/red-team.md`), not code. Injection types are package-local + internal (no attacker signal on the wire; `dist/schemas`/`openapi` untouched). **Deferred:** a real ML injection classifier behind the seam, per-tenant pattern tuning, nightly staging runs. ~23 new tests (12 package-local + 11 red-team) on top of the existing suite; all gates green (ruff, mypy --strict 329 files, RAG001, policy-coverage, schema/openapi-drift, log-schema/event-registry). [ADR-0045](docs/adr/ADR-0045-red-team-security.md), [guides/red-team.md](docs/guides/red-team.md), [reference/injection.md](docs/reference/injection.md)

---

## PR & Branch History
Expand Down Expand Up @@ -975,6 +985,7 @@ Complete log of every PR. Routine Dependabot bumps are grouped; everything else
| [#157](https://github.com/officialCodeWork/AgentContextOS/pull/157) | 2026-06-08 | feat(crypto): GCP / Azure / Vault KMS providers (Step 6.7c) |
| [#158](https://github.com/officialCodeWork/AgentContextOS/pull/158) | 2026-06-08 | feat(crypto): zero-downtime key rotation — RotatingKeyManager (Step 6.7d) |
| [#165](https://github.com/officialCodeWork/AgentContextOS/pull/165) | 2026-06-08 | test(perf): chaos kill-matrix gate + cache/rerank degrade-open + LitmusChaos (Step 7.2) |
| [#166](https://github.com/officialCodeWork/AgentContextOS/pull/166) | 2026-06-08 | feat(security): prompt-injection guard (rag-injection) + red-team probe gate (Step 7.3) |
| #78–#80, #116–#118 | Open | Dependabot bumps — awaiting merge |
| #81 | Closed | Dependabot bump — superseded |

Expand Down
5 changes: 5 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,11 @@ tasks:
cmds:
- "{{.PYTHON}} -m eval.gateway_chaos_v0.kill_matrix --check"

redteam:
desc: "Run the red-team gate — prompt injection / ACL bypass / PII egress / tenant escape probes"
cmds:
- "{{.PYTEST}} tests/redteam/ -v -m redteam"

# ---------------------------------------------------------------------------
# Schemas
# env: sets PYTHONPATH cross-platform (Task handles Windows vs Unix syntax)
Expand Down
Loading
Loading