From 198cf9965dc1264ac377d03899653b09b69cbb4b Mon Sep 17 00:00:00 2001 From: Diogo Santos Date: Tue, 11 Aug 2026 07:04:42 +0100 Subject: [PATCH 1/7] fix: make development-secret warning actionable --- src/weaver_kernel/_secrets.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/weaver_kernel/_secrets.py b/src/weaver_kernel/_secrets.py index 3a81ca7..673c552 100644 --- a/src/weaver_kernel/_secrets.py +++ b/src/weaver_kernel/_secrets.py @@ -21,6 +21,11 @@ SECRET_ENV_VAR = "WEAVER_KERNEL_SECRET" """Environment variable holding the HMAC secret used for tokens and audit chains.""" +PRODUCTION_CHECKLIST_URL = ( + "https://github.com/dgenio/agent-kernel/blob/main/docs/production-checklist.md" +) +"""Stable operator guidance linked from the development-secret warning.""" + _DEV_SECRET: str | None = None _DEV_SECRET_LOCK = threading.Lock() @@ -39,10 +44,13 @@ def _get_secret() -> str: if _DEV_SECRET is None: _DEV_SECRET = secrets.token_hex(32) logger.warning( - "%s is not set. Using a random development secret — tokens and " - "audit-chain signatures will not survive restarts. Set %s in production.", + "%s is not set. Using a process-local random development secret; " + "tokens and audit-chain signatures will be invalid after restart and " + "will not share signing state with another process. Set %s before " + "production. Production checklist: %s", SECRET_ENV_VAR, SECRET_ENV_VAR, + PRODUCTION_CHECKLIST_URL, ) return _DEV_SECRET From 94c113c258c487ec23cd69b6e2ba45554037cb02 Mon Sep 17 00:00:00 2001 From: Diogo Santos Date: Tue, 11 Aug 2026 07:04:57 +0100 Subject: [PATCH 2/7] test: pin the first-run secret warning contract --- tests/test_secrets.py | 51 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 tests/test_secrets.py diff --git a/tests/test_secrets.py b/tests/test_secrets.py new file mode 100644 index 0000000..c602b04 --- /dev/null +++ b/tests/test_secrets.py @@ -0,0 +1,51 @@ +"""Tests for HMAC secret resolution and the first-run development warning.""" + +from __future__ import annotations + +import logging + +import weaver_kernel._secrets as secret_module + + +def test_missing_secret_warns_once_with_consequence_and_fix( + monkeypatch, + caplog, +) -> None: + monkeypatch.delenv(secret_module.SECRET_ENV_VAR, raising=False) + monkeypatch.setattr(secret_module, "_DEV_SECRET", None) + + with caplog.at_level(logging.WARNING, logger="weaver_kernel._secrets"): + first = secret_module.resolve_hmac_secret() + second = secret_module.resolve_hmac_secret() + + assert first == second + messages = [record.getMessage() for record in caplog.records] + assert len(messages) == 1 + message = messages[0] + assert secret_module.SECRET_ENV_VAR in message + assert "process-local random development secret" in message + assert "invalid after restart" in message + assert "another process" in message + assert secret_module.PRODUCTION_CHECKLIST_URL in message + + +def test_environment_secret_avoids_development_warning(monkeypatch, caplog) -> None: + monkeypatch.setenv(secret_module.SECRET_ENV_VAR, "explicit-test-secret") + monkeypatch.setattr(secret_module, "_DEV_SECRET", None) + + with caplog.at_level(logging.WARNING, logger="weaver_kernel._secrets"): + resolved = secret_module.resolve_hmac_secret() + + assert resolved == "explicit-test-secret" + assert caplog.records == [] + + +def test_explicit_secret_takes_precedence_without_warning(monkeypatch, caplog) -> None: + monkeypatch.delenv(secret_module.SECRET_ENV_VAR, raising=False) + monkeypatch.setattr(secret_module, "_DEV_SECRET", None) + + with caplog.at_level(logging.WARNING, logger="weaver_kernel._secrets"): + resolved = secret_module.resolve_hmac_secret("constructor-secret") + + assert resolved == "constructor-secret" + assert caplog.records == [] From ecc3f2dc548f7a801e6d4cb2df667923c03aa86d Mon Sep 17 00:00:00 2001 From: Diogo Santos Date: Tue, 11 Aug 2026 07:05:38 +0100 Subject: [PATCH 3/7] docs: add production deployment checklist --- docs/production-checklist.md | 172 +++++++++++++++++++++++++++++++++++ 1 file changed, 172 insertions(+) create mode 100644 docs/production-checklist.md diff --git a/docs/production-checklist.md b/docs/production-checklist.md new file mode 100644 index 0000000..5071e2c --- /dev/null +++ b/docs/production-checklist.md @@ -0,0 +1,172 @@ +# Production checklist + +Weaver Kernel is pre-1.0 security infrastructure. Treat production readiness as a set of explicit guarantees and deployment choices, not as a single configuration flag. + +Start with the [Security Contract](security-contract.md). This checklist covers the operator decisions most likely to invalidate that contract when a development setup becomes a real deployment. + +## 1. Establish authenticated principal identity + +Kernel authorizes a `Principal`; it does not authenticate that principal for you. + +- derive principal identity from an authentication/workload-identity mechanism you trust; +- do not let model-provided text choose `principal_id` or privileged roles directly; +- document whether the principal represents a human user, workload/service, agent instance, or delegated identity; +- test that authentication failure prevents a capability grant from being minted. + +Production authentication/provider hardening is tracked in #103/#279. + +## 2. Set and protect the HMAC signing secret + +Set a strong `WEAVER_KERNEL_SECRET` before production use. + +The development fallback is intentionally process-local and random. It is useful for examples because it requires no setup, but: + +- tokens signed with it become invalid after restart; +- separate processes generate different secrets; +- local audit-chain signatures use the same secret-resolution path; +- it is not a substitute for managed secret distribution/rotation. + +Do not store sensitive payloads inside capability tokens: HMAC provides integrity/authenticity inside the shared-secret trust domain, **not encryption**. + +Key rotation and token-lifecycle hardening are tracked in #185 / PR #259. + +## 3. Review capability and tool classification + +Unknown authority should fail closed. + +For MCP and other discovered tool surfaces: + +- maintain an operator-reviewed mapping for high-risk tools; +- treat server/framework safety metadata as advisory rather than authoritative; +- verify WRITE/DESTRUCTIVE classifications against actual side effects; +- include paths/resources/destinations in constraints where the action needs narrower authority than the tool name alone expresses. + +#181 / PR #277 hardens the MCP default so missing metadata does not silently become READ. + +## 4. Test policy with both allow and deny cases + +Before deploying a policy: + +- test actions that should succeed; +- test adjacent actions that must fail; +- test a different principal attempting to reuse authority; +- test malformed/oversized constraints; +- test approval/escalation paths without granting adjacent authority; +- prefer stable reason codes over parsing human-readable messages. + +Run the named invariant suite as part of normal CI: + +```bash +pytest -q tests/test_invariants.py +``` + +Your application should also have integration tests for its own principal/capability/resource semantics. + +## 5. Decide token TTL, reuse and revocation semantics deliberately + +A long-lived reusable grant is more authority than a short, single-purpose one. + +Review: + +- token TTL by safety class/use case; +- whether a token should be reusable or single/max-use; +- invoke-time rate limits; +- revocation expectations; +- what must happen when constraints are malformed or cannot be enforced. + +Current lifecycle hardening is concentrated in #170/#185 and PR #259. Do not claim stronger invocation-limit or rotation guarantees until the deployed release actually contains that work. + +## 6. Verify every security-sensitive execution path is mediated + +Kernel protects actions that pass through its enforcement path. It cannot stop a host/framework from calling the underlying tool around it. + +For each integration, make a coverage table: + +| Execution surface | Goes through Kernel? | Alternative boundary | +| --- | --- | --- | +| Custom function/tool call | yes/no | wrapper/gateway/sandbox | +| Hosted/provider tool | yes/no | provider controls | +| Shell/subprocess | yes/no | execution sandbox/policy | +| MCP call | yes/no | Kernel/AgentFence/gateway | +| Handoff/sub-agent path | yes/no | explicit integration | + +Do not publish “framework X is secured by Kernel” unless every relevant execution surface is actually mediated. + +## 7. Understand multi-worker consistency before scaling + +Sharing `WEAVER_KERNEL_SECRET` lets workers verify the same HMAC signatures; it does **not** automatically share all enforcement state. + +Review [deployment-consistency.md](deployment-consistency.md) when that supported-path documentation is present in your release. In the current design, process-local state can include revocation, rate-limit windows, handles, budgets and traces unless an appropriate shared backend owns that state. + +If your guarantee requires immediate deployment-wide revocation or one global rate limit, prove that the backing architecture provides it before adding workers. + +## 8. Configure audit storage and retention + +Decide what evidence you need after an incident or policy review: + +- where `ActionTrace` records are stored; +- retention period; +- access control for traces; +- whether the store is local, shared, append-only, backed up, or externally anchored; +- what fields are safe to retain. + +Hash chaining can make mutation/reordering evident within its trust assumptions. It is not automatically non-repudiation, and deleting an unanchored local store remains possible. + +Never turn audit into a secret-exfiltration path by storing raw credentials/tool results unnecessarily. + +## 9. Treat redaction as defense in depth + +The Context Firewall structurally bounds result size/shape and applies redaction, but built-in secret/PII detection is heuristic. + +- minimize sensitive data before it reaches an agent tool when possible; +- use field/resource constraints rather than relying only on post-hoc regex redaction; +- test your domain-specific sensitive data with synthetic canaries; +- do not describe the built-in redactor as a complete DLP/data-governance system. + +## 10. Pin and test protocol/integration versions + +A dependency specifier is not proof of compatibility. + +For every optional integration you deploy: + +- use a version range that the project actually tests; +- pin/lock at the application layer according to your release practice; +- run integration tests before dependency upgrades; +- review major protocol/SDK migrations separately from routine dependency updates. + +For MCP specifically, current v1 support and the v2 migration are tracked in #263/#173. Do not blindly widen the MCP major range. + +## 11. Run software-supply-chain checks + +At minimum: + +- run `make ci` on the exact commit/release you deploy; +- review dependency-audit and CodeQL results; +- use the released package/SBOM/attestation workflow described in `RELEASE.md` where applicable; +- avoid adding optional integrations to the base runtime unless they are genuinely required. + +## 12. Establish operational failure behavior + +Decide how the host responds when Kernel cannot safely decide or execute: + +- policy provider timeout/error; +- expired/revoked/invalid token; +- tool/driver timeout; +- malformed constraints; +- audit-store failure; +- rate-limit/budget exhaustion; +- approval broker unavailable; +- protocol incompatibility. + +Security-sensitive uncertainty should normally fail closed. A fallback that widens authority requires explicit review, not an implicit exception handler. + +## 13. Re-read the claims before launch + +Before describing the deployment externally, compare your architecture to: + +- [Security Contract](security-contract.md); +- [Security Model](security.md); +- [Roadmap](../ROADMAP.md); +- the exact released version's CHANGELOG/release notes. + +If your deployment adds a stronger boundary (for example an external gateway/sandbox or shared authoritative store), document that as a property of **your deployment**, not as an unconditional Kernel guarantee. From 666291106076fea8706ab6c5dc33ba235c9aa7db Mon Sep 17 00:00:00 2001 From: Diogo Santos Date: Tue, 11 Aug 2026 07:06:21 +0100 Subject: [PATCH 4/7] docs: link the production checklist from the README --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6772a9e..fa99119 100644 --- a/README.md +++ b/README.md @@ -233,6 +233,7 @@ The historical GitHub slug is intentionally retained for now. A repository renam - [Security Contract](docs/security-contract.md) - [Security Model](docs/security.md) +- [Production checklist](docs/production-checklist.md) - [Roadmap](ROADMAP.md) - [Tutorial](docs/tutorial.md) - [Architecture](docs/architecture.md) From d8fd0b315b87b249a9ceb63190b30730d4f0716f Mon Sep 17 00:00:00 2001 From: Diogo Santos Date: Sun, 16 Aug 2026 11:10:42 +0100 Subject: [PATCH 5/7] fix: remove premature deployment consistency link --- docs/production-checklist.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/production-checklist.md b/docs/production-checklist.md index 5071e2c..28701a1 100644 --- a/docs/production-checklist.md +++ b/docs/production-checklist.md @@ -96,7 +96,7 @@ Do not publish “framework X is secured by Kernel” unless every relevant exec Sharing `WEAVER_KERNEL_SECRET` lets workers verify the same HMAC signatures; it does **not** automatically share all enforcement state. -Review [deployment-consistency.md](deployment-consistency.md) when that supported-path documentation is present in your release. In the current design, process-local state can include revocation, rate-limit windows, handles, budgets and traces unless an appropriate shared backend owns that state. +Deployment-consistency guidance is still being formalized. In the current design, process-local state can include revocation, rate-limit windows, handles, budgets and traces unless an appropriate shared backend owns that state. If your guarantee requires immediate deployment-wide revocation or one global rate limit, prove that the backing architecture provides it before adding workers. From 1209ab155cc88ba3f669d5d7eb6c57b906a23aac Mon Sep 17 00:00:00 2001 From: Diogo Santos Date: Sun, 16 Aug 2026 11:10:50 +0100 Subject: [PATCH 6/7] fix: avoid version-skewed production guidance URL --- src/weaver_kernel/_secrets.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/weaver_kernel/_secrets.py b/src/weaver_kernel/_secrets.py index 673c552..8362664 100644 --- a/src/weaver_kernel/_secrets.py +++ b/src/weaver_kernel/_secrets.py @@ -21,10 +21,8 @@ SECRET_ENV_VAR = "WEAVER_KERNEL_SECRET" """Environment variable holding the HMAC secret used for tokens and audit chains.""" -PRODUCTION_CHECKLIST_URL = ( - "https://github.com/dgenio/agent-kernel/blob/main/docs/production-checklist.md" -) -"""Stable operator guidance linked from the development-secret warning.""" +PRODUCTION_CHECKLIST_PATH = "docs/production-checklist.md" +"""Repository-relative operator guidance referenced by the development warning.""" _DEV_SECRET: str | None = None _DEV_SECRET_LOCK = threading.Lock() @@ -50,7 +48,7 @@ def _get_secret() -> str: "production. Production checklist: %s", SECRET_ENV_VAR, SECRET_ENV_VAR, - PRODUCTION_CHECKLIST_URL, + PRODUCTION_CHECKLIST_PATH, ) return _DEV_SECRET From c5ab556f949a3f9ee8a3bffa4acdd701c8adc7d6 Mon Sep 17 00:00:00 2001 From: Diogo Santos Date: Sun, 16 Aug 2026 11:10:58 +0100 Subject: [PATCH 7/7] test: scope development-secret warning assertion --- tests/test_secrets.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/test_secrets.py b/tests/test_secrets.py index c602b04..0d56db8 100644 --- a/tests/test_secrets.py +++ b/tests/test_secrets.py @@ -19,14 +19,19 @@ def test_missing_secret_warns_once_with_consequence_and_fix( second = secret_module.resolve_hmac_secret() assert first == second - messages = [record.getMessage() for record in caplog.records] + messages = [ + record.getMessage() + for record in caplog.records + if secret_module.SECRET_ENV_VAR in record.getMessage() + and "process-local random development secret" in record.getMessage() + ] assert len(messages) == 1 message = messages[0] assert secret_module.SECRET_ENV_VAR in message assert "process-local random development secret" in message assert "invalid after restart" in message assert "another process" in message - assert secret_module.PRODUCTION_CHECKLIST_URL in message + assert secret_module.PRODUCTION_CHECKLIST_PATH in message def test_environment_secret_avoids_development_warning(monkeypatch, caplog) -> None: