From 168c6a182cf8a470fbdaf7c62bb7969f1c14e25d Mon Sep 17 00:00:00 2001 From: Diogo Santos Date: Tue, 11 Aug 2026 06:10:38 +0100 Subject: [PATCH 1/6] docs: define the Weaver Kernel security contract --- docs/security-contract.md | 158 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 158 insertions(+) create mode 100644 docs/security-contract.md diff --git a/docs/security-contract.md b/docs/security-contract.md new file mode 100644 index 0000000..35a6912 --- /dev/null +++ b/docs/security-contract.md @@ -0,0 +1,158 @@ +# Weaver Kernel Security Contract + +This page defines the security claim that Weaver Kernel is prepared to make today, the assumptions behind that claim, and the work that must land before stronger claims are made. + +The short version is: + +> **Weaver Kernel mediates submitted agent actions, applies an authorization decision before the configured driver executes them, bounds the result returned through the Kernel path, and records an auditable action trace. It does not make an LLM trustworthy and it cannot protect execution paths that bypass Kernel mediation.** + +## Product boundary + +Weaver Kernel is an **embeddable execution-enforcement runtime**. It can use its built-in policy engine, but policy authoring is not intended to be the only or eventual integration model. + +A deployment may obtain identity and policy decisions elsewhere: + +```text +identity / workload authentication + ↓ +policy decision +(native policy, Cedar/OpenFGA/Auth0/OAP/custom) + ↓ + Weaver Kernel +(bind authority → enforce → execute → bound result → receipt) + ↓ + MCP / API / tool +``` + +The long-term interoperability goal is **bring your identity and policy; enforce the resulting authority consistently at the agent-action boundary**. + +## What is enforced on the supported path + +For an invocation that is submitted through Kernel: + +1. the caller supplies a `Principal`; +2. the requested capability is evaluated by a policy engine; +3. an allowed request receives a signed, expiring `CapabilityToken`; +4. `invoke()` verifies the token and principal before driver execution; +5. driver output is transformed by the Context Firewall before it is returned through the default LLM-safe path; +6. an `ActionTrace` records the mediated execution path and can be inspected with `kernel.explain()` / trace APIs. + +Current capability tokens bind at least: + +- `principal_id`; +- `capability_id`; +- signed constraints; +- expiry. + +Handle expansion separately re-checks the principal and the grant constraints persisted on the handle. + +## What is **not** currently claimed + +### Complete mediation + +Weaver Kernel is an in-process library. If an application, framework, hosted tool, subprocess, SDK or agent can execute the underlying action without going through Kernel, Kernel cannot prevent that bypass. + +Therefore integrations must document the execution surfaces they mediate. Do not describe a framework as “secured by Weaver Kernel” when only a subset of its tool surfaces pass through the adapter. + +For a stronger process boundary, use an out-of-process control such as AgentFence or another gateway/sandbox in addition to, or instead of, the embedded Kernel. + +### Transaction-level / exact-action authorization + +The current token contract does **not** yet claim to bind every authorization decision to the complete executable transaction (for example exact normalized arguments, resolved resource identity, run/plan identifier, tool-descriptor digest, pre-state digest, approval receipt, destination and single-use semantics). + +That stronger model is being evaluated in #258. Until it lands, describe Kernel tokens as **principal- and capability-scoped grants with signed constraints**, not as cryptographic proof that one exact transaction was authorized. + +### Authentication + +A `Principal` is authorization input, not proof of identity. The host is responsible for deriving the principal from an authentication mechanism it trusts. A production authentication seam is tracked in #103. + +### Sandbox / prompt-injection cure + +Kernel policy can constrain mediated actions after a model has made a bad or adversarially influenced decision. It is not a VM/container sandbox, malware detector, prompt-injection cure, model-alignment system, or proof that the model's reasoning is correct. + +### Distributed consistency + +Some enforcement and audit state is process-local today. Multi-worker deployments can fragment revocation state, handles, rate-limit state, budgets and traces. The exact consistency model and mitigation are tracked in #226. + +## Fail-closed requirements for advertised integrations + +A security integration is not ready to be advertised as a supported path unless all of the following hold: + +- unknown/unclassified tools do not silently receive permissive authority (#181); +- the documented rate-limit semantics match the execution path, including token reuse (#170); +- the integration's supported protocol/SDK versions are continuously tested against real implementations (#173, #263); +- the integration publishes a mediation/coverage matrix; +- policy denials and execution failures remain auditable without exposing secrets; +- the README and released package describe the same supported behavior. + +## Policy interoperability + +The built-in policy engine is a useful standalone default and a conformance target. It should not force adopters to replace mature IAM systems. + +The preferred architecture is to keep the enforcement contract stable while allowing policy decisions to come from: + +- the native Weaver Kernel policy engine; +- a shared Weaver policy contract / AgentFence-compatible policy (#111, #116); +- external authorization systems through a narrow provider interface; +- portable authorization artifacts if an external standard gains adoption. + +This avoids coupling Kernel adoption to a bet on which policy ecosystem wins. + +## Action-binding direction + +A stronger grant should be able to express and, where applicable, cryptographically bind authority such as: + +```text +principal +capability / action +resolved resource identity + provenance +normalized argument constraints or exact-argument digest +run / approved-plan identity +approval reference +expiry +use count / single-use semantics +policy decision reference +``` + +The important property is monotonicity: execution must not widen the authority that was actually approved. + +This direction should reuse portable contracts from `weaver-spec` / IntentFlow where they are suitable rather than inventing a second incompatible transaction language inside Kernel. + +## Evidence contract + +An `ActionTrace` is operational evidence about the Kernel-mediated path. It should let a reviewer answer: + +- which principal requested the action; +- which capability was involved; +- which policy decision/reason applied; +- whether the action executed or was denied; +- which driver performed the action; +- what bounded result metadata was returned; +- what follow-up expansion or approval events occurred. + +A trace is not automatically non-repudiation. Trust depends on secret custody, trace-store integrity and deployment architecture; see [security.md](security.md). + +## Security-claim vocabulary + +Prefer precise language: + +- **Good:** “The mediated tool call was denied before the configured driver executed.” +- **Good:** “Kernel verified a principal-scoped capability grant before this invocation.” +- **Good:** “The default Kernel return path produced a bounded `Frame` rather than returning the raw driver result.” +- **Avoid:** “The model was compromised but the data could not leave.” +- **Avoid:** “This secures OpenAI/LangChain/MCP” without a surface-by-surface coverage statement. +- **Avoid:** “Production hardened” until the production-hardening gates are met. + +## Current hardening gates + +The security-critical work that takes precedence over speculative feature growth is: + +- #181 — fail closed for unclassified MCP tools; +- #170 — define/enforce invocation-time rate-limit and token-use semantics; +- #226 — document and address multi-process consistency limitations; +- #263 + #173 — MCP v2 migration and real interoperability testing; +- #103 — production authentication/secrets hardening; +- #199 + #245 — executable invariants and adversarial/property tests; +- #258 — exact action / transaction binding analysis. + +See [ROADMAP.md](../ROADMAP.md) for the adoption gates that sequence this work. From cba42ba6bef31eeb8b064d5c47d8a5caf07e9fad Mon Sep 17 00:00:00 2001 From: Diogo Santos Date: Tue, 11 Aug 2026 06:11:06 +0100 Subject: [PATCH 2/6] docs: add adoption-gated roadmap --- ROADMAP.md | 176 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 176 insertions(+) create mode 100644 ROADMAP.md diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 0000000..99aac81 --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,176 @@ +# Weaver Kernel Roadmap + +This roadmap is deliberately **gate-driven, not calendar-driven**. Weaver Kernel should not advance because an internal backlog exists; it should advance when the previous product and security hypothesis survives external use. + +The current strategic thesis is: + +> **Authorize elsewhere if you want. Weaver Kernel enforces narrowly scoped authority at the agent-action boundary and leaves evidence of what ran.** + +The built-in policy engine remains a first-class standalone option. The project should not require adopters to replace mature identity or authorization systems. + +## Operating rules + +1. Security-contract gaps outrank surface-area growth. +2. External adopter evidence outranks internally imagined demand. +3. Unknown authority fails closed on advertised security paths. +4. Framework integrations publish exactly which execution surfaces they mediate. +5. Main, released package and documentation should describe the same product. +6. Prefer interoperability with identity/policy ecosystems over competing with all of them. +7. Keep speculative work documented, but do not let it consume the critical path. + +## Gate 0 — Falsify the wedge + +**Question:** Is there a real problem that native framework permissions and existing authorization products do not already solve well enough? + +Work: + +- compare Kernel against native OpenAI/LangChain-style controls and at least one mature IAM/policy system; +- compare against agent-specific authorization systems rather than assuming the category is empty; +- integrate the current Kernel into three realistic applications without changing their architecture more than necessary; +- test at least two candidate beachheads (coding agents and application/workflow agents); +- record what users still need after existing controls are enabled. + +Exit condition: + +- three independent developers can explain, in their own words, why they would use Kernel rather than only their framework's native permissions or an ordinary authorization check. + +If this condition is not met, reposition before adding features. + +## Gate 1 — Define the security contract + +**Question:** Can we state exactly what Kernel protects without hidden caveats? + +Work: + +- maintain [`docs/security-contract.md`](docs/security-contract.md) as the concise claim surface; +- define complete-mediation assumptions and non-guarantees; +- decide the target for exact action/resource/argument binding (#258); +- define the relationship among identity, policy decision, capability grant, invocation and `ActionTrace`; +- add coverage matrices for every advertised framework/protocol integration. + +Exit condition: + +- the security promise fits in five precise sentences and a reviewer can map each sentence to executable tests or explicitly documented assumptions. + +## Gate 2 — Close supported-path blockers + +**Question:** Are there known fail-open or misleading semantics on the path we advertise? + +Critical work: + +- #181 — require deliberate classification of unknown MCP tools; +- #170 — resolve invocation-time rate-limit/token-reuse semantics; +- #226 — make the multi-worker consistency model explicit and choose a mitigation path; +- #263 — deliberately support MCP Python SDK v2; +- #173 — test MCP against real/reference servers; +- #103 — harden authentication/secrets claims; +- #199 and #245 — executable invariants + adversarial/property tests; +- #219 — ensure policy decisions and explanations cannot drift. + +Exit condition: + +- no known fail-open issue remains on the advertised path; +- protocol/version support is continuously tested; +- production limitations are visible before integration, not buried after it. + +## Gate 3 — One exceptional on-ramp + +**Question:** Can an adopter get value without adopting the entire internal architecture? + +Primary work: + +- #104 — drop-in middleware/wrapper path; +- support an **observe → classify → enforce** migration mode rather than unsafe optimistic auto-classification; +- build one generic Python-callable integration and one framework-native integration exceptionally well before multiplying adapters; +- make denials actionable (#221) where this improves the agent loop without leaking policy information; +- keep the full registry/token/driver API as the advanced path. + +Exit condition: + +- a stranger reaches a correctly denied action and an inspectable trace without maintainer help; +- unknown/unclassified actions cannot accidentally become permissive in the easy path. + +## Gate 4 — Prove rather than claim + +**Question:** Can a third party reproduce both the security benefit and the usability cost? + +Benchmark dimensions: + +- unauthorized-action success rate; +- authorized-task completion rate; +- false-deny rate; +- unmediated/bypass surfaces; +- policy/configuration effort; +- p50/p95 enforcement latency; +- audit completeness; +- cross-framework decision consistency; +- scope/attenuation correctness where supported. + +Use the coding-agent scenario in #253 only if Gate 0 validates it as the best wedge. + +Exit condition: + +- a third party can clone a benchmark and reproduce the advertised claims without private infrastructure or maintainer interpretation. + +## Gate 5 — Establish external trust + +**Question:** Would a security-conscious team trust this component on the boundary it claims to own? + +Sequence: + +1. external architecture/threat-model review; +2. fix findings and update the contract; +3. stabilize the advertised path; +4. code-level security review/audit; +5. publish findings and remediations. + +Exit condition: + +- supported guarantees, limitations and review findings are public and materially consistent with the implementation. + +## Gate 6 — Distribution through interoperability + +**Question:** Is discovery happening outside the maintainer's own GitHub ecosystem? + +Work: + +- upstream integrations/examples where framework maintainers accept them; +- accept external policy decisions through a narrow provider seam rather than forcing policy migration; +- interoperate with shared Weaver/AgentFence policy contracts (#111, #116); +- prefer compatibility with successful external authorization standards over inventing an incompatible one; +- publish concrete technical case studies and reproducible comparisons. + +Exit condition: + +- at least three independent downstream repositories use Kernel; +- at least one meaningful external contributor has landed work; +- at least one external ecosystem/framework/security resource points users to Kernel for a specific supported job. + +## Gate 7 — Expand only when pulled + +Candidate work that is valuable **only after** the supported path has external pull: + +- remote/sidecar Kernel (#227); +- A2A driver (#130); +- cross-language verifier/token wire ecosystem (#228); +- packaged driver SPI/plugin ecosystem (#190); +- browser playground (#146); +- mission-control/activity-event surfaces (#240, #243, #255); +- domain-specific capability profiles (#248, #252, #257); +- adaptation/session-learning controls (#254); +- broader federation/delegation/token-format work (#129, #224). + +These issues can remain open as research options, but they should not outrank Gates 0–6 without external adopter evidence or a security dependency. + +## Kill / reposition criteria + +Radically change the current thesis if, after deliberately testing it: + +1. developers consistently find native framework permissions sufficient; +2. Kernel cannot provide meaningful enforcement beyond an ordinary middleware callback; +3. exact-action binding requires an API too complex for the value it provides; +4. external security reviewers conclude that the in-process enforcement model adds little assurance; +5. the project cannot obtain three genuinely independent downstream users despite hands-on integration support; +6. users consistently prefer an out-of-process AgentFence/gateway boundary rather than embedded enforcement. + +The goal is not to preserve the current architecture. The goal is to discover and build the smallest durable open-source enforcement layer that users actually trust and adopt. From 3f07d468285ce46c28e55a7ef57e670a605f9901 Mon Sep 17 00:00:00 2001 From: Diogo Santos Date: Tue, 11 Aug 2026 06:12:00 +0100 Subject: [PATCH 3/6] docs: sharpen Weaver Kernel product boundary --- README.md | 388 ++++++++++++++++++++++-------------------------------- 1 file changed, 159 insertions(+), 229 deletions(-) diff --git a/README.md b/README.md index 9da18bb..e9d6629 100644 --- a/README.md +++ b/README.md @@ -1,311 +1,240 @@ -# agent-kernel +# Weaver Kernel [![CI](https://github.com/dgenio/agent-kernel/actions/workflows/ci.yml/badge.svg)](https://github.com/dgenio/agent-kernel/actions/workflows/ci.yml) [![CodeQL](https://github.com/dgenio/agent-kernel/actions/workflows/codeql.yml/badge.svg)](https://github.com/dgenio/agent-kernel/actions/workflows/codeql.yml) [![Coverage ≥90%](https://img.shields.io/badge/coverage-%E2%89%A590%25-brightgreen.svg)](https://github.com/dgenio/agent-kernel/actions/workflows/ci.yml) [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](https://www.python.org/) [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE) -[![Read the Weaver Stack overview on Towards AI](https://img.shields.io/badge/Read_the_overview-Towards_AI-black?logo=medium&logoColor=white)](https://pub.towardsai.net/the-weaver-stack-one-contract-layer-for-safe-llm-agents-7f733cad5eac) - +**Execution enforcement and audit for AI-agent actions.** +Weaver Kernel is an embeddable Python runtime that turns an authorization decision into a scoped, expiring capability grant, checks that grant before a configured driver executes, bounds the result returned through the Kernel path, and records an `ActionTrace` explaining what happened. -**Least-privilege, revocable, principal-scoped authorization for agent tool calls — with a tamper-evident audit of everything that ran.** +> **Authorize elsewhere if you want. Enforce here. Prove what ran.** -A capability-based security kernel for AI agents operating in large tool ecosystems (MCP, A2A, 1000+ tools). +The built-in policy engine works standalone. The longer-term design deliberately keeps execution enforcement separable from whichever identity or policy system an adopter prefers. -## Least privilege for coding agents +## See it in under a minute -Give an agent enough authority to read a repository, edit bounded paths, and -run tests—without also granting secrets, workflow rewrites, network/package -operations, or publishing authority. - -```mermaid -flowchart TD - A["Coding-agent request"] --> K["weaver-kernel policy"] - K -->|denied| D["Explain or escalate"] - K -->|allowed| T["Signed scoped grant"] - T --> E["Driver executes"] - E --> R["ActionTrace receipt"] -``` - -> **Availability:** the direct module command ships in the first package -> release containing [#253](https://github.com/dgenio/agent-kernel/issues/253). +No model API key or network service is required: ```bash python -m pip install weaver-kernel python -m weaver_kernel.coding_agent_demo ``` -It runs a hermetic fake driver, prints only after all assertions pass, and visibly proves bounded -read/edit/test, denied secret and out-of-scope access, explicit task-bound PR -escalation, signed scope enforcement, and the corresponding `kernel.explain()` -path. [Read the expected receipt and security boundaries.](docs/coding-agent-security.md) - -| Project | Use it for | -| --- | --- | -| **agent-kernel / `weaver-kernel`** | embedded capability authorization and audit | -| [AgentFence](https://github.com/dgenio/AgentFence) | an external firewall at the tool boundary | -| [ContextWeaver](https://github.com/dgenio/contextweaver) | bounded capability/context visibility | -| ChainWeaver | deterministic multi-step execution | - -Every tool call gets a **capability token** (HMAC-signed, time-bounded, scoped to one principal and one capability) and a **tamper-evident audit trace** (`ActionTrace`) recording who invoked what, under which policy decision, with what result. That **authorization + audit** layer is `agent-kernel`'s unique contribution to the [Weaver stack](#part-of-the-weaver-stack) — neither `contextweaver` nor `AgentFence` provides it. - -### Why `agent-kernel` and not `contextweaver` or `AgentFence`? - -- **`contextweaver`** decides *what context the LLM sees*. **`agent-kernel`** decides *what the agent is allowed to run, and proves what it ran.* -- **`AgentFence`** is an external proxy that gates tool calls *at the process boundary*. **`agent-kernel`** is the *in-process* runtime that mints the capability token, enforces policy, firewalls the result, and writes the audit trace — compiled into your agent host. -- They compose: author policy once and enforce it both embedded (`agent-kernel`) and at the edge (`AgentFence`); produce a `Frame` in `agent-kernel` and let `contextweaver` do budgeted selection over it. See the boundary notes below. +The hermetic coding-agent scenario demonstrates a useful agent that can read a repository, edit bounded paths and run tests while secret access, out-of-scope writes and unapproved publication remain denied. It also prints the corresponding `kernel.explain()` / `ActionTrace` evidence. + +[Read the coding-agent scenario and its boundaries.](docs/coding-agent-security.md) + +## The boundary + +```text +identity / workload authentication + ↓ +policy decision +(native Kernel policy today; external policy providers are an interoperability goal) + ↓ +┌────────────────────────────────────────────┐ +│ Weaver Kernel │ +│ │ +│ scoped grant → verify → execute │ +│ ↓ │ +│ bounded result + ActionTrace │ +└──────────────────────┬─────────────────────┘ + ↓ + MCP / HTTP / tool +``` -## 30-second pitch +For an action submitted through Kernel: -Modern AI agents face three hard problems when given access to hundreds or thousands of tools: +1. a `Principal` identifies the authorization subject supplied by the host; +2. policy evaluates the requested capability; +3. an allowed request receives a signed, expiring `CapabilityToken`; +4. `invoke()` verifies the token and principal before driver execution; +5. the Context Firewall transforms raw driver output before the default LLM-safe return path; +6. the invocation is recorded as an auditable `ActionTrace`. -1. **No authorization or audit** — nothing scopes what a tool call may do, and there's no record of what ran, when, and why. -2. **Tool-space interference** — agents accidentally invoke the wrong tool or escalate privileges. -3. **Context blowup** — raw tool output floods the LLM context window. +## What Kernel does **not** claim -`agent-kernel` solves all three with a thin, composable layer that sits above your tool execution layer. The first two features are its **unique, non-overlapping contribution**; the last two it *also* provides, with explicit boundaries against its siblings: +This is a security component, so the non-claims are part of the API contract: -- **Capability Tokens** *(unique to agent-kernel)* — HMAC-signed, time-bounded, principal-scoped. No token → no execution. -- **Audit Trail** *(unique to agent-kernel)* — every invocation creates an `ActionTrace` retrievable via `kernel.explain()`. -- **Policy Engine** *(boundary vs AgentFence)* — READ/WRITE/DESTRUCTIVE safety classes + PII/PCI sensitivity handling, enforced **in-process**. `AgentFence` enforces an equivalent gate at the **external boundary**; the goal is to author one policy and enforce it both places (shared-policy contract — [#111](https://github.com/dgenio/agent-kernel/issues/111)). -- **Context Firewall** *(boundary vs contextweaver)* — raw driver output is *never* returned to the LLM; always a bounded `Frame`. `agent-kernel` is the **producer** of the canonical `Frame` at the execution boundary; `contextweaver` is a **consumer** that does budgeted selection over Frames — deliberate layering, not redundancy (canonical-`Frame` seam — [#110](https://github.com/dgenio/agent-kernel/issues/110)). +- **Not complete mediation by itself.** Kernel is an in-process library. Code or framework surfaces that can invoke a tool around Kernel remain outside its protection. +- **Not authentication.** A `Principal` is authorization input. The host must derive it from an authentication mechanism it trusts. +- **Not a sandbox or prompt-injection cure.** Kernel can reject an unauthorized mediated action even when a model makes a bad decision; it does not make the model trustworthy. +- **Not yet transaction-level cryptographic authorization.** Current grants bind principal + capability + signed constraints + expiry. Exact binding to every normalized argument/resource/run/approval is an active design question (#258). +- **Not yet a distributed authorization service.** Some revocation, rate-limit, handle, budget and trace state is process-local; see #226 and the [security contract](docs/security-contract.md). -## Architecture +Read the concise **[Security Contract](docs/security-contract.md)** before relying on Kernel as a control boundary. -```mermaid -graph LR - LLM["LLM / Agent"] -->|goal| K["Kernel"] - K -->|search| REG["Registry"] - K -->|evaluate| POL["Policy Engine"] - K -->|sign| TOK["HMAC Token"] - K -->|route| DRV["Driver (MCP/HTTP/Memory)"] - DRV -->|RawResult| FW["Context Firewall"] - FW -->|Frame| LLM - K -->|record| AUD["Audit Trace"] -``` +## Why not just framework guardrails? -## Part of the Weaver Stack +Framework-native approvals and guardrails are useful and should be used where they solve the problem. Kernel is intended for teams that need an enforcement contract that is independent of one model framework and that couples the execution decision with scoped authority, bounded output and an inspectable receipt. -`agent-kernel` is the **execution / authorization runtime** of the **Weaver -stack** — a set of composable, independently usable projects for building safe -LLM-agent systems. On the request path: +Every framework integration should publish a surface-by-surface coverage matrix. “Integrated with framework X” must never be interpreted as “every execution path in framework X is mediated.” -``` -contextweaver ─► ChainWeaver ─► agent-kernel ─► AgentFence -(select & (deterministic (capability tokens, (external policy - compile context) tool chains) policy, firewall, gate at the edge) - tamper-evident audit) -``` +## Quickstart: the full Kernel API -| Project | Role in the stack | -|---|---| -| [contextweaver](https://github.com/dgenio/contextweaver) | Selects and compiles the context the LLM sees. | -| ChainWeaver | Orchestrates deterministic multi-step tool chains. | -| **agent-kernel** *(this repo)* | Authorizes, executes, firewalls, and audits tool calls in-process. | -| [AgentFence](https://github.com/dgenio/AgentFence) | Enforces a policy gate at the external process boundary. | -| [weaver-spec](https://github.com/dgenio/weaver-spec) | The shared contracts (invariants; capability/token/`Frame`/policy) the others conform to. | - -**Standalone by design.** `agent-kernel` has no hard dependency on any sibling -project — its only runtime dependencies are `httpx` and `pydantic`. Use it on -its own, or compose it with the rest of the stack; the siblings interoperate -through the shared [weaver-spec](https://github.com/dgenio/weaver-spec) -contracts, not through tight coupling. A deeper, per-project comparison — -including *when not* to reach for `agent-kernel` — is in -[How this relates to neighboring projects](#how-this-relates-to-neighboring-projects). - -The minimal-install guarantee is enforced in CI: a dedicated job installs the -package with **no extras** (`pip install weaver-kernel`), imports the entire -public API, and runs the quickstart — so an accidental hard dependency on an -optional extra (`mcp`, `yaml`, `opentelemetry`, `tiktoken`) fails the build. - -**Supply-chain & security automation.** CI runs [`pip-audit`](https://pypi.org/project/pip-audit/) -over the runtime dependency tree and [CodeQL](https://codeql.github.com/) -(`security-and-quality`) on every PR and weekly; Dependabot keeps pinned -GitHub Actions and Python dependencies fresh. Releases carry a CycloneDX SBOM -and PEP 740 PyPI attestations (see [RELEASE.md](RELEASE.md)). A `pip-audit` -false positive can be allow-listed with `pip-audit --ignore-vuln ` plus a -justifying comment in the workflow. - -## Quickstart +The low-level API is explicit by design. A drop-in wrapper/middleware path is tracked in #104; the full API remains useful when you want direct control over capabilities, principals, drivers and traces. ```bash pip install weaver-kernel ``` ```python -import weaver_kernel -``` - -> ### 📦 Repo ↔ package ↔ import — read this once -> -> | Where you see it | Name | -> |---|---| -> | GitHub repository | `dgenio/agent-kernel` | -> | PyPI — what you `pip install` | **`weaver-kernel`** | -> | Python — what you `import` | **`weaver_kernel`** | -> -> **Decision (2026-06):** the install name and the import name are unified on -> **`weaver-kernel` / `weaver_kernel`** — the two names you actually type. There -> is **no `agent_kernel` import any more**; use `weaver_kernel`. The GitHub repo -> keeps its historical `agent-kernel` slug for now (GitHub redirects old URLs); -> the package is part of the [**Weaver stack**](#part-of-the-weaver-stack), which -> is why the distribution is `weaver-`prefixed. See -> [docs/architecture.md](docs/architecture.md#naming) for the full rationale. - -> **New here?** [docs/tutorial.md](docs/tutorial.md) walks through register → grant → invoke → expand → explain in five minutes. +import asyncio +import os -```python -import asyncio, os -os.environ["WEAVER_KERNEL_SECRET"] = "my-secret" +os.environ["WEAVER_KERNEL_SECRET"] = "replace-me-for-real-deployments" from weaver_kernel import ( - Capability, CapabilityRegistry, - InMemoryDriver, Kernel, Principal, SafetyClass, StaticRouter, + Capability, + CapabilityRegistry, + InMemoryDriver, + Kernel, + Principal, + SafetyClass, + StaticRouter, ) from weaver_kernel.models import CapabilityRequest -# 1. Register a capability registry = CapabilityRegistry() -registry.register(Capability( - capability_id="tasks.list", - name="List Tasks", - description="List all tasks", - safety_class=SafetyClass.READ, - tags=["tasks", "list"], -)) - -# 2. Wire up a driver +registry.register( + Capability( + capability_id="tasks.list", + name="List Tasks", + description="List tasks", + safety_class=SafetyClass.READ, + tags=["tasks"], + ) +) + driver = InMemoryDriver() -driver.register_handler("tasks.list", lambda ctx: [{"id": 1, "title": "Buy milk"}]) +driver.register_handler( + "tasks.list", + lambda ctx: [{"id": 1, "title": "Buy milk"}], +) -# 3. Build the kernel -kernel = Kernel(registry=registry, router=StaticRouter(routes={"tasks.list": ["memory"]})) +kernel = Kernel( + registry=registry, + router=StaticRouter(routes={"tasks.list": ["memory"]}), +) kernel.register_driver(driver) -async def main(): - principal = Principal(principal_id="alice", roles=["reader"]) - # 4. Discover → grant → invoke → expand → explain +async def main() -> None: + principal = Principal(principal_id="alice", roles=["reader"]) token = kernel.get_token( CapabilityRequest(capability_id="tasks.list", goal="list tasks"), - principal, justification="", + principal, + justification="", ) - frame = await kernel.invoke(token, principal=principal, args={}) - print(frame.facts) # ['Total rows: 1', 'Top keys: id, title', ...] - print(frame.handle) # Handle(handle_id='...', ...) - # `principal` is required: the handle is bound to the granting principal, - # so an omitted principal raises HandleConstraintViolation. - expanded = kernel.expand( - frame.handle, query={"limit": 1, "fields": ["title"]}, principal=principal - ) - print(expanded.table_preview) # [{'title': 'Buy milk'}] + frame = await kernel.invoke(token, principal=principal, args={}) + print(frame.facts) + print(kernel.explain(frame.action_id)) - trace = kernel.explain(frame.action_id) - print(trace.driver_id) # 'memory' asyncio.run(main()) ``` -> This snippet is extracted and executed by CI (`tests/test_readme_quickstart.py`), and -> a standalone runnable mirror lives at -> [`examples/readme_quickstart.py`](examples/readme_quickstart.py) (run by `make example`). -> CI fails if either stops producing the documented output, so this quickstart cannot -> silently drift from the working API. +The README quickstart is exercised in CI; a runnable mirror lives at [`examples/readme_quickstart.py`](examples/readme_quickstart.py). -## Where it fits +For a guided walkthrough, see [`docs/tutorial.md`](docs/tutorial.md). -``` -┌─────────────────────────────────────────────┐ -│ LLM / Agent loop │ -├─────────────────────────────────────────────┤ -│ agent-kernel ← you are here │ -│ (registry · policy · tokens · firewall) │ -├────────────────┬────────────────────────────┤ -│ contextweaver │ tool execution layer │ -│ (context │ (MCP · HTTP · A2A · │ -│ compilation) │ internal APIs) │ -└────────────────┴────────────────────────────┘ -``` +## Security properties -`agent-kernel` sits **above** `contextweaver` (context compilation) and **above** raw tool execution. It provides the authorization, execution, and audit layer. +The current design centers on three Weaver invariants: -## How this relates to neighboring projects +| Invariant | Property | +|---|---| +| **I-01** | Raw driver output does not enter the default LLM-safe path; the Context Firewall produces a bounded `Frame`. | +| **I-02** | Kernel-mediated execution is authorized and auditable. | +| **I-06** | Capability tokens bind the principal, capability and constraints and expire. | -`agent-kernel` is the embeddable runtime layer of the **Weaver ecosystem**. The -projects below solve adjacent problems and are designed to compose, not to -overlap. +Important hardening work takes priority over speculative surface growth. The current critical path includes: -| Project | Role | Where it runs | Use it when… | -|---|---|---|---| -| **agent-kernel** *(this repo)* | Embeddable library/runtime: capability registry, policy, HMAC tokens, context firewall, audit trace. | In-process inside your agent host. | You need authorization, redaction, and audit between an LLM loop and a large tool ecosystem. | -| [**AgentFence**](https://github.com/dgenio/AgentFence) | External CLI / local proxy that intercepts tool calls and applies a policy gate. | Out-of-process, alongside your agent. | You want a policy boundary without changing your agent code, or you need to gate a third-party agent host you can't modify. | -| [**contextweaver**](https://github.com/dgenio/contextweaver) | Library that selects and compiles the context an LLM receives. | In-process, before the LLM call. | You need to assemble relevant context for a prompt. It sits *under* the LLM loop; agent-kernel sits *between* the LLM and tools. | -| **ChainWeaver** | Orchestrator for deterministic tool chains. | In-process or as a separate service. | You need to run a multi-step deterministic flow rather than free-form LLM tool use. | -| [**weaver-spec**](https://github.com/dgenio/weaver-spec) | Specification: invariants, capability/token/frame contracts, conformance suite. | Not a runtime — it's docs + a contract test suite. | You're building another Weaver-compatible implementation, or you want to verify an existing one. | +- #181 — unknown MCP tools must not silently receive permissive authority; +- #170 — invocation-time rate-limit / token-use semantics; +- #226 — multi-worker consistency; +- #263 + #173 — MCP v2 migration and real interoperability testing; +- #103 — authentication/secrets production hardening; +- #199 + #245 — executable invariants and adversarial/property tests; +- #258 — exact action/transaction binding analysis. -A minimal architecture using `agent-kernel` as the central runtime: +See the gate-driven **[ROADMAP](ROADMAP.md)** for sequencing and kill criteria. -``` -LLM / agent loop - │ - ▼ -contextweaver ─► agent-kernel ─► driver ─► MCP / HTTP / A2A / internal API - │ - ▼ - ActionTrace +## MCP + +MCP is a strategically important execution surface, but Kernel intentionally does not claim blanket “MCP security.” The supported SDK/protocol envelope must be tested continuously, and unknown tool authority must fail closed before broad MCP-security promotion. + +Install MCP support with: + +```bash +pip install "weaver-kernel[mcp]" ``` -### When *not* to use this +See [`docs/integrations.md`](docs/integrations.md) and the live MCP compatibility/hardening issues (#173, #181, #263) before production use. -- You only need a process-level policy gate around an existing agent host — - reach for `AgentFence` instead. -- You only need to compile context for a prompt — use `contextweaver`. -- You want a deterministic, scripted workflow with no LLM in the inner loop — - use `ChainWeaver`. -- You're writing a static analyzer or one-shot CLI scanner with no - per-invocation runtime — `agent-kernel` would be overkill. +## Policy -See [docs/tutorial.md](docs/tutorial.md) for an end-to-end "secure your first -MCP tool in 5 minutes" walkthrough. +Kernel ships a deterministic built-in policy engine with READ / WRITE / DESTRUCTIVE safety classes, sensitivity handling and stable denial reason codes. -## Weaver Spec Compatibility: v0.1.0 +That engine is a standalone default, **not a requirement that adopters replace their existing IAM**. The roadmap favors a narrow policy-provider/interoperability seam and shared policy contracts where they reduce duplication (#111, #116). -agent-kernel is a compliant implementation of [weaver-spec v0.1.0](https://github.com/dgenio/weaver-spec). -The following invariants are satisfied: +Unknown authority should fail closed. The planned easy-mode experience is **observe → classify → enforce**, not “guess that an unknown tool is safe.” -| Invariant | Description | How agent-kernel satisfies it | -|-----------|-------------|-------------------------------| -| **I-01** | LLM never sees raw tool output by default | `Context Firewall` always transforms `RawResult → Frame`; raw driver output is not returned by default, and non-admin principals cannot obtain `raw` response mode | -| **I-02** | Every execution is authorized and auditable | `PolicyEngine` authorizes at grant time; a valid `CapabilityToken` (HMAC-verified on every `invoke()`) carries the authorization decision; `TraceStore` records every `ActionTrace` | -| **I-06** | CapabilityTokens are scoped | Tokens bind `principal_id + capability_id + constraints` with an explicit TTL; `revoke(token_id)` / `revoke_all(principal_id)` are supported | +## Audit and bounded output -See [docs/agent-context/invariants.md](docs/agent-context/invariants.md) for the full internal invariant list and [weaver-spec INVARIANTS.md](https://github.com/dgenio/weaver-spec/blob/main/docs/INVARIANTS.md) for the specification. +Every Kernel-mediated invocation can produce an `ActionTrace`; durable trace stores add tamper-evident hash chaining. The Context Firewall produces bounded `Frame` objects and applies redaction/budget logic before data is returned on the default LLM-safe path. -## Security disclaimers +Audit evidence is not automatically non-repudiation: trust still depends on secret custody, deployment architecture and trace-store integrity. See [`docs/security.md`](docs/security.md). -> **v0.1 is not production-hardened for real authentication.** +## Where it fits in the Weaver ecosystem -- HMAC tokens are tamper-evident (SHA-256) but **not encrypted**. Do not put sensitive data in token fields. -- Set `WEAVER_KERNEL_SECRET` to a strong random value in production. If unset, a random dev secret is generated per-process with a warning. -- PII redaction is heuristic (regex). It is not a substitute for proper data governance. -- See [docs/security.md](docs/security.md) for the full threat model. +Each project is independently usable: + +| Project | Job | +|---|---| +| **Weaver Kernel** *(this repository)* | Embedded execution enforcement, scoped capability grants, bounded results and action traces. | +| [AgentFence](https://github.com/dgenio/AgentFence) | Out-of-process policy/tool boundary when you cannot or do not want to trust the agent host to mediate every call. | +| [ContextWeaver](https://github.com/dgenio/contextweaver) | Select and compile bounded context/capability visibility for the model. | +| ChainWeaver | Deterministic multi-step execution. | +| [weaver-spec](https://github.com/dgenio/weaver-spec) | Implementation-neutral contracts and conformance work. | + +Use Kernel alone when that is all you need. Composition should be a second step, not an onboarding requirement. + +## When not to use Weaver Kernel + +- You only need a coarse external policy boundary around a third-party host: AgentFence or another gateway may be simpler. +- You cannot ensure sensitive actions cross the embedded Kernel boundary: use a stronger out-of-process enforcement/sandbox boundary. +- You only need prompt/context selection: use ContextWeaver. +- You only need a deterministic scripted workflow with no agentic authorization problem: use a normal workflow engine or ChainWeaver. +- You need mature organization-wide IAM/policy authoring: keep that system and integrate Kernel only where its execution/evidence boundary adds value. + +## Project identity + +| Surface | Name | +|---|---| +| Product | **Weaver Kernel** | +| GitHub repository | `dgenio/agent-kernel` | +| PyPI | `weaver-kernel` | +| Python import | `weaver_kernel` | +| CLI | `weaver-kernel` | + +The historical GitHub slug is intentionally retained for now. A repository rename is not part of the current critical path. ## Documentation +- [Security Contract](docs/security-contract.md) +- [Security Model](docs/security.md) +- [Roadmap](ROADMAP.md) +- [Tutorial](docs/tutorial.md) - [Architecture](docs/architecture.md) -- [Security model](docs/security.md) -- [Integrations (MCP, HTTPDriver)](docs/integrations.md) - - [contextweaver: policy before action](docs/integrations/contextweaver.md) - - [Repository safety checks as a capability](docs/integrations/repository_safety_check.md) - - [ChainWeaver compiled flows as capabilities](docs/integrations/chainweaver.md) - - [Policy guardrails for evaluation artifacts](docs/integrations/evaluation_artifacts.md) +- [Integrations](docs/integrations.md) - [Designing capabilities](docs/capabilities.md) - [Context Firewall](docs/context_firewall.md) +- [Coding-agent security scenario](docs/coding-agent-security.md) ## Development @@ -313,10 +242,11 @@ See [docs/agent-context/invariants.md](docs/agent-context/invariants.md) for the git clone https://github.com/dgenio/agent-kernel cd agent-kernel pip install -e ".[dev]" -make ci # fmt-check + lint + type + test + examples +make ci ``` +The project enforces strict typing, a ≥90% coverage floor, dependency auditing and CodeQL. Releases include supply-chain metadata described in [`RELEASE.md`](RELEASE.md). + ## License Apache-2.0 — see [LICENSE](LICENSE). - From 59b5b7bce906e7a166583b5466ef4c0aa8922920 Mon Sep 17 00:00:00 2001 From: Diogo Santos Date: Tue, 11 Aug 2026 06:12:39 +0100 Subject: [PATCH 4/6] docs: align security model with current maturity --- docs/security.md | 326 +++++++++++++++++++++++------------------------ 1 file changed, 162 insertions(+), 164 deletions(-) diff --git a/docs/security.md b/docs/security.md index 3e136f1..9b9197e 100644 --- a/docs/security.md +++ b/docs/security.md @@ -1,176 +1,174 @@ # Security Model +Start with the concise [Security Contract](security-contract.md). This page gives the implementation-level threat model and operational caveats behind that contract. + ## Threat model -| Threat | Mitigation | -|--------|-----------| -| Tool-space interference (agent calls wrong tool) | Capability registry + policy gate before any execution | -| Confused deputy attack | Tokens are bound to `principal_id` — cannot be reused by another principal | -| Token forgery / tampering | HMAC-SHA256 signature; any bit flip → `TokenInvalid` | -| Token replay after expiry | Expiry checked on every `verify()` call | -| Context injection via raw tool output | Firewall always transforms `RawResult → Frame`; raw data never reaches LLM by default | -| PII / PCI leakage | Redaction + `allowed_fields` enforcement in the firewall, applied on every egress path (summary/table/raw, handle expansion, streaming) | -| PII / secret leak below the depth budget | Redaction fails *closed* at `max_depth`: leaf strings are scrubbed; nested containers are elided rather than returned verbatim (#149) | -| Inline secret leak via handle expansion | `HandleStore.expand()` runs projected rows through the firewall redactor, so a secret in a permitted field is scrubbed (#150) | -| Cross-chunk secret split in streaming | `Firewall.apply_stream()` holds back a per-field overlap window so a secret spanning two chunks is reassembled before redaction (#151) | -| Privilege escalation via WRITE/DESTRUCTIVE | Policy engine enforces role requirements | -| Audit evasion | Every `invoke()` creates an immutable `ActionTrace` | -| Handle scope escape (expand exceeds grant) | Handles persist grant constraints; `HandleStore.expand` rechecks `max_rows`, `allowed_fields`, `scope`, and principal binding (#76) | -| Sensitive data reaching the audit log via args/errors | `ActionTrace.args` and driver `error` text are run through the firewall redactor for **every** capability; memory payloads (`payload`/`content`/`value`/`memory`/`text`/`body`) are additionally stripped wholesale for `memory.*` capabilities (#75, #172) | -| Scanned content / raw result reaching audit log | `ActionTrace.result_summary` is built only from the post-firewall `Frame` (counts and flags, never raw driver data), so the audit trail records an invocation's outcome without re-introducing the data the firewall removed | - -## Token scopes - -A `CapabilityToken` binds: -- `capability_id` — which capability is authorized -- `principal_id` — who the token was issued to -- `constraints` — max_rows, allowed_fields, etc. (signed into the token) -- `expires_at` — validity window - -Any change to these fields invalidates the HMAC signature. - -## Confused deputy prevention - -Consider an agent that obtains a token for `billing.list_invoices` then passes it to a different agent. The second agent cannot use it because `verify()` checks that `token.principal_id == expected_principal_id`. - -The same principle extends to handles: every `Handle` carries the `principal_id` -the original grant was issued to. When `handle.principal_id` is non-empty, -`HandleStore.expand` rejects expansion unless the caller supplies a matching -`principal_id`. **An omitted or empty `principal_id` is treated as a -mismatch** (`HandleConstraintViolation`, `reason_code = HANDLE_PRINCIPAL_MISMATCH`), -so a handle ID alone is not a bearer credential — proof of the original -principal is always required. `Kernel.expand(..., principal=Principal(...))` -forwards the principal automatically. +| Threat | Current mitigation | +|---|---| +| Tool-space interference | Capability registry + policy gate before Kernel-mediated execution | +| Confused deputy / cross-principal token reuse | Tokens bind `principal_id`; verification rejects a different principal | +| Token tampering | HMAC-SHA256 signature verification | +| Token replay after expiry | Expiry is checked during token verification | +| Raw tool output reaching the default LLM-safe path | `RawResult` is transformed by the Context Firewall into a bounded `Frame` | +| PII / PCI leakage | Firewall redaction + allowed-field enforcement on supported egress paths | +| Deeply nested secret leakage | Redaction fails closed at the configured depth boundary; nested containers are elided rather than returned verbatim | +| Handle expansion escaping the grant | Handle expansion re-checks principal binding and persisted grant constraints | +| Sensitive arguments/results leaking into audit | Trace arguments/errors pass through redaction; result summaries are built from post-firewall data rather than raw driver output | +| Privilege escalation through WRITE / DESTRUCTIVE classes | Policy engine applies role / justification rules | +| Audit mutation | Durable trace stores can use HMAC hash chaining to make mutation/interior deletion/reordering evident | -## Handle expansion boundary +The important qualifier is **Kernel-mediated**. Weaver Kernel is an in-process library and cannot protect an execution path that bypasses it. + +## Capability-token scope + +A current `CapabilityToken` binds: + +- `capability_id` — which capability is authorized; +- `principal_id` — the authorization subject; +- `constraints` — signed scope such as row/field limits where used; +- `expires_at` — the validity window. + +Changing a signed field invalidates the HMAC signature. + +### Current boundary of the token claim -Calling `kernel.expand(handle, query=...)` does not re-run the policy engine — -the original grant already authorised the dataset, and handles are short-lived. -But the grant's _constraints_ must still apply, otherwise an over-broad -`expand` query would silently return data the original grant never covered. +The current format should be described as a **principal- and capability-scoped grant with signed constraints**, not proof that one exact executable transaction was authorized. -`HandleStore.expand` rechecks the constraints the kernel persists on the handle -at creation time (`token.constraints`): +Exact binding to all normalized arguments, resolved resource identity/provenance, run/plan identity, approval receipt, tool-descriptor digest, pre-state and single-use semantics is an active design topic (#258). See [security-contract.md](security-contract.md). -| Constraint | Enforced behavior on expand | -|------------|-----------------------------| -| `max_rows` | A request `limit` larger than the cap raises `HandleConstraintViolation`. An unspecified or larger implicit limit is silently clamped. | -| `allowed_fields` | A request `fields` entry that is not in `allowed_fields` raises `HandleConstraintViolation`. An unscoped expand applies `allowed_fields` as the default projection, so disallowed fields never leak. | -| `scope` (e.g. `{"region": "eu"}`) | The scope filter is AND-merged into the request filter. A request filter that disagrees on a scoped dimension raises `HandleConstraintViolation`. | -| `principal_id` | A mismatched `principal_id` parameter raises `HandleConstraintViolation` (`HANDLE_PRINCIPAL_MISMATCH`). | +## Principal identity and authentication + +`Principal` is authorization input supplied by the host. Kernel does not currently prove that the asserted principal corresponds to a human, workload or authenticated session. + +The host must derive the principal from a trusted authentication mechanism. A production authentication/provider seam is tracked in #103. + +## Confused-deputy prevention + +A token issued to one principal cannot be reused by a different principal because verification checks the principal binding. + +Handles follow the same principle. A stored handle carries the original grant principal; expansion requires a matching principal and treats an omitted/mismatched identity as `HandleConstraintViolation` / `HANDLE_PRINCIPAL_MISMATCH`. + +## Handle expansion boundary -Errors carry stable `reason_code` values (`handle_constraint_violation`, -`handle_principal_mismatch`) — assert on those, not on the message text. +`kernel.expand()` does not re-run the original policy decision, but it re-applies the grant constraints persisted with the handle. + +| Constraint | Expansion behavior | +|---|---| +| `max_rows` | Requests above the cap are rejected or clamped according to the API path. | +| `allowed_fields` | Out-of-scope fields are rejected; default projection cannot reveal disallowed fields. | +| `scope` | Stored scope is merged into the query and conflicting scope is rejected. | +| `principal_id` | A different/omitted principal is rejected. | + +Stable reason codes should be used by integrations instead of parsing human-readable messages. ## Memory actions -Capabilities tagged `SensitivityTag.MEMORY` represent durable agent memory -(project notes, session handoff, learned context). Reads of project-scoped -memory are allowed by default; reads of sensitive-scoped memory require an -explicit role. Writes always require the `memory_writer` role (or `admin`) -because they persist into future sessions. - -| Action | Required role | Denial reason code | -|--------|---------------|--------------------| -| `memory.read` with `scope["memory_scope"] == "project"` | none | — | -| `memory.read` with `scope["memory_scope"] == "sensitive"` | `memory_reader_sensitive` or `admin` | `memory_sensitive_read_denied` | -| `memory.write` (any scope) | `memory_writer` or `admin` | `memory_write_requires_writer` | -| `memory.forget` (DESTRUCTIVE) | `admin` (then `memory_writer` or `admin`) | `missing_role`, then `memory_write_requires_writer` | - -To prevent durable memory content from leaking into the audit log, the kernel -strips payload-like fields (`payload`, `content`, `value`, `memory`, `text`, -`body`) from `ActionTrace.args` for any capability whose ID begins with -`memory.`. Non-sensitive metadata keys (`key`, `id`, `scope`, ...) are -preserved so audit can still confirm an action took place. - -## Audit-log integrity (hash chain) - -When traces are persisted to a durable store (`SQLiteTraceStore`, -`JsonlTraceStore`), each record is wrapped in a hash chain: `record_hash = -HMAC-SHA256(secret, {seq, prev_hash, trace})`, where `prev_hash` is the previous -record's hash (the first record links to a genesis value). `verify_chain()` -recomputes every hash and checks the linkage, so it detects: - -- **mutation** of any persisted record (recomputed hash diverges), -- **interior insertion, deletion, or reordering** (broken `prev_hash` linkage or a - non-contiguous `seq`), - -and reports the `seq` of the first divergent record. `SQLiteTraceStore.prune()` -removes old records while preserving verifiability of the retained suffix by -recording the last pruned record's hash as a checkpoint. - -**Truncation is the exception.** The chain stores no signed head/length anchor, so -dropping the **most recent** records (tail truncation) — or deleting the whole -store — leaves a self-consistent prefix that still verifies: there is no broken -link or sequence gap to detect, and an empty store verifies vacuously. Detecting -truncation requires anchoring the expected head out of band (a separately stored, -signed record count + head hash); that is a planned follow-up. Until then, treat -append-only durability (JSONL shipped to a write-once collector, or a SQLite file -on append-only storage) as the truncation defense. - -**What this is — and is not.** This is **tamper-evidence**: anyone who does not -hold `WEAVER_KERNEL_SECRET` cannot alter the log without `verify_chain()` -detecting it. It is **not non-repudiation**: a host that controls the secret can -forge a self-consistent chain, and the same secret signs tokens, so the audit -log is only as trustworthy as secret custody. It does not encrypt trace contents -at rest, and it does not anchor the chain to an external timestamping authority. -The chain payload is the redaction-safe export shape — chaining adds no field the -in-memory trace did not already hold and cannot widen the I-01 boundary. - -The CLI exposes verification to operators: `weaver-kernel audit verify --store -audit.db` exits non-zero on any divergence (see [cli.md](cli.md)). - -## What the audit trail captures (#175) - -Auditability (I-02) covers authorization decisions and data-access events, not -only successful invocations. Every recorded `ActionTrace` carries an `event_type`: - -- `invoke` — a capability invocation (success or driver failure). -- `expand` — a `Kernel.expand()` data-access event (more rows of a stored - handle). Expansion Frames carry the expanding principal in - `Provenance.principal_id`. -- `deny` — a `grant_capability()` rejected by policy, recorded with the stable - `reason_code` (a `DenialReason`) and a redacted reason message *before* the - `PolicyDenied` exception propagates. - -So `explain()` and `query_traces()` can answer "who was refused what, when, and -why" and "which rows were expanded by whom". Expansion query arguments and denial -messages pass through the same firewall redactor as invocation args, so these new -records never make the trace store a sensitive-data sink. - -## Retention bounding (#182) - -Long-lived processes accumulate one trace per invocation and one revocation entry -per revoked token. Both in-memory structures are bounded: - -- The in-memory `TraceStore` caps at `max_entries` (default 10 000), evicting - oldest-first. Eviction discards audit data, so it is deliberately loud (a - warning on first eviction) and counted (`evicted_count`). For unbounded - retention, use a durable backend. -- Revocation state records each token's expiry and is swept for already-expired - tokens (lazily, and via `HMACTokenProvider.sweep_revocations()`). A sweep never - un-revokes a live token — only entries for tokens that already fail the expiry - check are removed. - -## Security disclaimers - -> **v0.1 is not production-hardened for real authentication.** - -- HMAC tokens are tamper-evident but **not encrypted**. Do not put sensitive data in token fields. -- The `WEAVER_KERNEL_SECRET` must be kept secret. Rotate it if compromised. -- The default `InMemoryDriver` has no persistence — suitable for testing only. -- PII redaction is heuristic (regex-based). It is not a substitute for proper data governance. -- Streaming redaction (`Firewall.apply_stream`) reassembles patterns split across - chunks by holding back a bounded overlap window. A contiguous secret - (JWT/Bearer/API-key/connection-string body) is never split across a commit - boundary, but a pattern containing internal whitespace (phone, SSN, spaced card - number) split exactly at the held boundary may still evade detection. The - holdback buffer is also memory-bounded (`overlap * 4`); a single contiguous - secret longer than that bound is force-committed and may be severed at the cut, - so an extremely long unbroken token can escape per-segment detection — a - deliberate memory-vs-safety trade. -- Rate limiting is enforced per `(principal_id, capability_id)` pair using a sliding window. - Default limits: 60 READ / 10 WRITE / 2 DESTRUCTIVE invocations per 60-second window. - Principals with the `"service"` role receive 10× the default limits. Limits are - configurable via `DefaultPolicyEngine(rate_limits=...)`. There is no distributed or - persistent rate-limit state — limits reset on process restart. +Capabilities tagged for durable memory receive additional policy treatment. Sensitive memory reads and memory writes require explicit roles, and payload-like memory fields are stripped from `ActionTrace.args` so the audit trail does not become a durable memory-content sink. + +## Context Firewall + +The Context Firewall is the boundary between a raw driver result and the default LLM-safe representation. + +It provides: + +- bounded `Frame` output; +- field / row budgets; +- redaction; +- handle-based expansion under persisted constraints; +- streaming redaction with bounded overlap handling. + +Redaction is defense in depth, not a substitute for data governance. The built-in detector is heuristic and can miss domain-specific or adversarial representations. + +Streaming redaction also has a bounded overlap/memory trade-off: sufficiently pathological secrets or patterns split outside the supported overlap assumptions may evade heuristic detection. Do not advertise regex redaction as a formal confidentiality guarantee. + +## MCP discovery and tool classification + +MCP tool annotations are hints, not a trusted authorization statement. + +The current backlog contains a security-critical hardening item (#181) because unannotated tools must not silently receive the least-restricted safety classification on an advertised least-privilege path. + +Until that item is resolved, production integrations should supply an explicit `safety_class_map` for discovered tools rather than trusting the default fallback. + +MCP SDK/protocol compatibility is also a live support boundary (#263, #173). Check the supported dependency range before relying on the integration. + +## Rate limiting and token reuse + +The default policy includes per-principal/per-capability sliding-window limits, but the exact relationship between grant-time evaluation and repeated invocation of a reusable token is being hardened in #170. + +Do not describe the current rate limiter as a complete runaway-agent control until invocation-time semantics are explicitly enforced and tested. + +## Multi-process / multi-worker deployments + +Several stateful components are process-local today, including parts of: + +- revocation state; +- rate limiting; +- handles; +- budgets; +- trace storage (unless a shared durable store is configured). + +A horizontally scaled deployment can therefore have different security/operational semantics from a single process. In particular, token signature verification is stateless while revocation and some enforcement state are not, which can create non-obvious divergence. + +The consistency model and mitigation architecture are tracked in #226. High-assurance deployments should understand this limitation before scaling worker count. + +## Audit trail and tamper evidence + +Kernel records authorization/execution-related events as `ActionTrace` records, including supported invoke, deny and expansion events. + +Trace fields are intended to answer questions such as: + +- who requested the action; +- which capability was involved; +- why policy allowed/denied it; +- which driver executed; +- what bounded result metadata was produced; +- what follow-up expansion/approval activity occurred. + +Durable stores (`SQLiteTraceStore`, `JsonlTraceStore`) can wrap records in an HMAC hash chain. This can detect mutation and broken interior linkage. + +### What hash chaining does not prove + +- It does not provide non-repudiation when the host controls the signing secret. +- It does not encrypt trace contents at rest. +- Without an independently anchored expected head/length, a self-consistent truncated tail can remain undetectable. +- Deleting the whole local store is not prevented by the chain. + +For higher assurance, ship traces to storage with independent retention/integrity controls. + +## Security automation + +The repository uses CI, strict typing, a coverage floor, dependency auditing and CodeQL. Release artifacts include the supply-chain metadata documented in [`../RELEASE.md`](../RELEASE.md). + +These controls improve software assurance; they do not by themselves establish production security suitability. + +## Current maturity statement + +Weaver Kernel is pre-1.0. The package has moved materially beyond the old “v0.1” wording that previously appeared in this document, but it should **not** be described as fully production-hardened authentication/authorization infrastructure yet. + +Known hardening gates include: + +- #103 — authentication, secrets and production-hardening criteria; +- #181 — fail-closed MCP classification; +- #170 — invocation-time rate-limit/token-use semantics; +- #226 — distributed consistency semantics; +- #263 + #173 — MCP v2 and real interoperability; +- #199 + #245 — executable invariants and adversarial/property testing; +- #258 — exact action/transaction binding. + +See [`../ROADMAP.md`](../ROADMAP.md) for sequencing. + +## Deployment checklist + +Before relying on Kernel in a security-sensitive deployment: + +1. set and protect a strong `WEAVER_KERNEL_SECRET`; do not rely on the generated development secret; +2. derive `Principal` from authenticated identity/workload context; +3. explicitly classify high-risk/unknown tools; +4. review which framework execution surfaces actually pass through Kernel; +5. test allow **and deny** paths for your own policy; +6. verify constraints on the exact resources/arguments that matter to your tools; +7. choose durable trace storage and retention appropriate to the threat model; +8. understand multi-worker state semantics before horizontal scaling; +9. pin/test the MCP/other SDK versions you deploy; +10. read [security-contract.md](security-contract.md) and do not broaden its claims in downstream documentation. From 530c584ed874bd3a66d123ff88ff5099092d39ed Mon Sep 17 00:00:00 2001 From: Diogo Santos Date: Tue, 11 Aug 2026 06:12:57 +0100 Subject: [PATCH 5/6] chore: align package description with enforcement positioning --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 8c8333a..17d75c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -5,7 +5,7 @@ build-backend = "hatchling.build" [project] name = "weaver-kernel" version = "0.11.0" -description = "Capability-based security kernel for AI agents operating in large tool ecosystems" +description = "Execution enforcement and audit for AI-agent actions" readme = "README.md" license = { file = "LICENSE" } requires-python = ">=3.10" From d477147b9726adc201b6b227347c42c82b0f76d3 Mon Sep 17 00:00:00 2001 From: Diogo Santos Date: Tue, 11 Aug 2026 06:23:07 +0100 Subject: [PATCH 6/6] docs: preserve executable quickstart contract --- README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index e9d6629..6772a9e 100644 --- a/README.md +++ b/README.md @@ -131,14 +131,19 @@ async def main() -> None: ) frame = await kernel.invoke(token, principal=principal, args={}) - print(frame.facts) + expanded = kernel.expand( + frame.handle, + query={"limit": 1, "fields": ["title"]}, + principal=principal, + ) + print(expanded.table_preview) # [{'title': 'Buy milk'}] print(kernel.explain(frame.action_id)) asyncio.run(main()) ``` -The README quickstart is exercised in CI; a runnable mirror lives at [`examples/readme_quickstart.py`](examples/readme_quickstart.py). +The README quickstart is extracted and executed in CI; a runnable mirror lives at [`examples/readme_quickstart.py`](examples/readme_quickstart.py). For a guided walkthrough, see [`docs/tutorial.md`](docs/tutorial.md).