Skip to content

Releases: craigamcw/raucle

v0.13.0 — Security fail-loud + strict proof-enforced mint mode

Choose a tag to compare

@craigamcw craigamcw released this 28 May 12:51

Security minor release. Two P1 fixes addressing structural-correctness gaps surfaced by an internal security review. No public-API breaks — every new behaviour is opt-in. Existing deployments see no surprise.

FIX 1 — Fail-loud cryptographic configuration

Six places in the codebase used silent try / except: pass patterns around signer initialisation. The behaviour is now:

  • Explicit config + broken = REFUSE. Operator misconfigured; raise ConfigurationError, exit non-zero.
  • No config + safe default exists = WARN loudly. Continue in explicitly-marked unsigned mode.
  • Never silent.

Affected paths:

  • raucle_detect/errors.py — new module: ConfigurationError, PolicyUnproven.
  • raucle_detect.server._init_compliance() — extracted from import-time init; RAUCLE_DETECT_VERDICT_KEY_PEM or RAUCLE_DETECT_AUDIT_PRIVATE_KEY_PEM set but unparseable now refuses to start.
  • VerdictSigner.__init__ / Ed25519Signer.__init__ — a signer whose public key cannot be extracted raises ConfigurationError at construction (was: silently produced unverifiable receipts).
  • audit.sink_from_env() — invalid RAUCLE_DETECT_AUDIT_PRIVATE_KEY_PEM raises instead of silently producing an unsigned sink.
  • HashChainSink — every newly-created chain emits a self-describing chain_meta header on line 1, signed when a signer is supplied. AuditVerifier surfaces signed_mode ∈ {signed, unsigned, unknown} and rejects splice attempts (signed checkpoint in unsigned chain, mismatched key_id between header and checkpoint).

FIX 2 — Strict proof-enforced mint mode

Previously, cap mint --policy-proof-hash was advisory: any string could be passed. Tokens cited proofs they had no structural relationship to. The new strict mode makes the relationship enforceable.

from raucle_detect.capability import CapabilityIssuer
from raucle_detect.prove import JSONSchemaProver

# 1. Prove the policy
result = JSONSchemaProver().prove(schema=tool_schema, policy=tool_policy)
assert result.status == "PROVEN"

# 2. Strict-mint binds proof + grammar + policy hashes into the token
issuer = CapabilityIssuer.generate(issuer="acme.bank.kyc", require_proof=True)
token = issuer.mint(
    agent_id="agent:kyc-prod",
    tool="lookup_customer",
    proof_result=result,  # required
)

# 3. The token now carries:
#   token.policy_proof_hash == result.hash
#   token.grammar_hash      == result.grammar_hash
#   token.policy_hash       == result.policy_hash
# All three covered by token_id and the Ed25519 signature.

Defence-in-depth at the gate too:

from raucle_detect.capability import CapabilityGate

gate = CapabilityGate(
    trusted_issuers={...},
    proof_enforcement_mode=\"strict\",
    trusted_proofs={result.hash: result},  # pre-loaded cache
)
# Every call now denied unless:
#   * token has policy_proof_hash, AND
#   * proof in trusted_proofs cache, AND
#   * proof.status == 'PROVEN', AND
#   * token.grammar_hash matches proof.grammar_hash, AND
#   * token.policy_hash matches proof.policy_hash

CLI:

# Strict mint:
raucle-detect cap mint \\
  --key issuer.key.pem --issuer acme.bank.kyc \\
  --agent-id agent:kyc-prod --tool lookup_customer \\
  --require-proof --proof-result proof.json \\
  --out token.json

raucle-detect prove json|url|sql already returned exit-code 2 on REFUTED and 1 on UNDECIDED — both pipeline-failing under set -e. Now covered by tests so CI never silently regresses.

Backward compatibility

Default Opt-in via
Strict mint mode OFF CapabilityIssuer(require_proof=True) or RAUCLE_REQUIRE_PROOF=1
Gate-time proof enforcement OFF `CapabilityGate(proof_enforcement_mode='lenient'
Chain chain_meta header always emitted on new chains n/a — additive on-wire format; legacy chains remain verifiable
Fail-loud crypto errors always loud n/a — replaces silent except-pass with ConfigurationError

The two Capability field additions (grammar_hash, policy_hash) are nullable; absent in pre-0.13.0 tokens. Round-tripped through to_dict() / from_dict() cleanly.

Tests

70 new tests across tests/test_audit.py, tests/test_verdicts.py, tests/test_capability.py, tests/test_server_init.py, tests/test_cli_exit_codes.py. Full suite: 429 passed, 4 skipped. No regressions.

What's not in this release

  • Hosted policy-registry fetcher (gate consults a published registry at runtime with TTL caching). [DECIDE: gate-time proof enforcement] comment in CapabilityGate.__init__ flags this as future scope; current gate-time enforcement uses a caller-supplied in-memory cache.
  • Argon2id password hashes server-side. Out of scope for this fix.

🤖 Generated with Claude Code

v0.12.0 — Microsoft AGT integration first-class (PR #2610 merged)

Choose a tag to compare

@craigamcw craigamcw released this 28 May 08:53

Microsoft's Agent Governance Toolkit just landed our PR.
microsoft/agent-governance-toolkit#2610 — adding proof_artefact and verification_pointers carry-through on AGT's BackendDecision so high-assurance external policy backends can attach offline-verifiable evidence to the AGT audit chain — merged upstream at 25abf72 on 2026-05-27 by Microsoft maintainer Imran Siddique.

raucle-detect v0.12.0 ships the first-class integration that consumes the new contract.

What's new

RauclePolicyBackend

from agent_os.policies.evaluator import PolicyEvaluator
from raucle_detect.integrations.agt_backend import RauclePolicyBackend
from raucle_detect.capability import CapabilityGate, CapabilityIssuer

issuer = CapabilityIssuer.generate(issuer="acme.bank.kyc-platform")
gate   = CapabilityGate(trusted_issuers={issuer.key_id: issuer.public_key_pem})

evaluator = PolicyEvaluator()
evaluator.add_backend(RauclePolicyBackend(
    gate=gate,
    verification_base_url="https://acme.bank",
))

Every BackendDecision raucle returns now carries:

  • proof_artefact — the cited policy-proof hash (SMT-verified, content-addressed)
  • verification_pointers{issuer_pubkey, policy_registry, lean_development} URLs where an external auditor can fetch the deploying organisation's published material and re-verify the decision offline

AGT's PolicyEvaluator propagates both into PolicyDecision.audit_entry, so any AGT audit-chain consumer gets offline-verifiable evidence on every raucle-rendered decision automatically.

Graceful degradation

The backend detects at construction whether the installed AGT carries the merged fields. On pre-merge AGT installs, verdicts remain correct; the evidence fields are silently dropped. No code changes required for users straddling the upgrade.

Tests

7 new tests in tests/test_agt_backend.py. Skipped automatically when agent_os is not installed; passing 7/7 against microsoft/agent-governance-toolkit@main post-merge.

Deprecation

The pre-merge raucle_detect.integrations.agt stub module is deprecated; remove in v0.13.0. New consumers use raucle_detect.integrations.agt_backend.RauclePolicyBackend.

Where this sits

This is the second of three Microsoft-stack integrations.

  • v0.11.0 — Agent Framework FunctionMiddleware. ✅
  • v0.12.0 — AGT ExternalPolicyBackend. ✅ shipped here.
  • v0.13.0 — Azure AI Foundry MCP Gateway sidecar (recorded walkthrough in progress).

🤖 Generated with Claude Code