Governance Conformance and Signed Rejection Evidence

Overview

v1.1.0 is a minor release on top of v1.0.6. Every governed ingress now returns typed, Operator-signed evidence for a rejection or refusal, not just for success. A new governance conformance suite drives a case catalogue over every ingress and posture, grades each case from that signed evidence alone, and verifies the sealed record offline. Tier-qualification campaigns carry the signed receipt of every governed tool call and reject unverifiable evidence. Runtime scratch moves under .ztd/. The plaintext master key file is removed.

The release adds wire contracts and removes CLI surface (ztd eval boundary, --master-key-file), so it is a minor bump. See Removed for the upgrade path.

Highlights

  • Signed rejection evidence on every ingress — Direct envelope submission, governed dispatch, MCP and A2A return a typed GovernanceRejection carrying the executing Operator's signed rejection receipt, or a typed IngressRefusal for pre-envelope refusals. A denied L3 approval is now a signed L3_DENIED rejection too.
  • Governance conformance suite — ztd eval conformance run | verify | report runs governance-conformance@1.0.0 against a live native stack, seals the record with a digest sidecar, and re-derives every verdict offline.
  • Evidence-first campaigns — agent-trace.v2 keeps each governed tool call's signed receipt, the Data Operator's ledger bracket is verified on import, and unverifiable evidence grades INVALID_EVIDENCE.
  • Fail-closed key custody — The plaintext --master-key-file path is gone. Hosts without an OS key store seal under ssh-agent or fail closed.
  • Windows onboarding script (native end-to-end validation pending) — irm https://zerotrustdata.io/start.ps1 | iex.

Added

  • Typed governance outcomes on every ingress — GovernanceRejection (with the Operator-signed rejection receipt and its rejection_code), IngressRefusal (pre-envelope refusals, including MCP and A2A error.data), the full signed ActionReceipt on governed dispatch, MCP tools/call and A2A a2a/call, and one typed GovernedSuspension for a suspended call. ztd operator run prints the transaction ID and rejection code.
  • Signed L3 deny — L3Metadata.denied (outside the transaction hash) and GOVERNANCE_REJECTION_CODE_L3_DENIED. A denied approval resubmits the suspended envelope with the deny marker. L4 rejects it in every posture with ErrTxL3Denied and keeps the nonce spent, and L5 signs the rejection on the executing runtime. POST /api/v1/approvals/respond with approved: false returns that receipt, and approval.completed carries it to every consumer: ztd auth approve fails with ErrTxL3Denied, the TUI reports "denied", and the console has a Deny button.
  • Governance conformance suite — ztd eval conformance run, verify and report. governance-conformance@1.0.0 drives a 26-row case catalogue over direct, dispatch, CLI, MCP and A2A ingresses in every posture, and grades each case from the Operator's signed evidence (oracle, canary manifest, commitment-ledger bracket). The run is sealed as a ConformanceRunRecord with a digest sidecar. verify recomputes every verdict offline, and any changed byte is INVALID_EVIDENCE. report builds a case-by-posture coverage table and an L1 doctrine recall report. The doctrine adversarial corpus is an embedded package.
  • Conformance protocol — protocol/proto/ztd/eval/v1/eval.proto adds ConformanceRunRecord, ConformanceCaseEvidence, ConformanceObservation, ConformanceVerdict(Set), ConformanceFailureClass, ConformanceSuiteVerdict, CanaryManifest, ConformanceAnchor, EVALUATION_SCENARIO_CATEGORY_CONTAINMENT and EVALUATION_ASSIGNMENT_LIFECYCLE_STATUS_INVALID_EVIDENCE. Go and TypeScript bindings are regenerated.
  • Agent SUT groundwork — Canonical SUT contract vectors, the SUT client, per-persona graders, the secondary judge, and three seeded-investigation scenarios under eval/investigations. Live runs wait on an external SUT.
  • Ephemeral ssh-agent for multi-process harnesses — ztd test scale and ztd test release-smoke run a private in-process ssh-agent under the run Gateway's .ztd/agent/ (keystore.EphemeralAgent, Linux). Children seal under it without touching the host keyring.
  • Runtime scratch API — RuntimeFileService.MkdirTemp creates scratch under .ztd/tmp/, and ssh.MkdirRemoteScratch stages remote uploads under <login>/.ztd/tmp. INV-FS-11 documents both.
  • Windows onboarding script — website/static/start.ps1 implements installation, Gateway startup, sign-in, and a first governed action. Automated script tests, including PowerShell cases, passed on Linux. The complete flow has not been verified on a native Windows host; Windows end-to-end readiness is not claimed for this release. The homepage quick start and Releases pages list the script.

Changed

  • Receipt trust — Receipts verify only against the key bound to the executing Operator at enrollment. A self-declared signer key ID is no longer trusted.
  • Direct envelope submission — Binds requestor_user_id and acting_app_id to the authenticated identity. Returns 503 instead of 500 when the verifier cannot run, renders pre-L4 refusals as sentinels only, and returns its 200 receipt as canonical protojson (enums by name) instead of encoding/json.
  • Governed dispatch — Runs L1 doctrine before the L3 gate, renders only typed sentinels, and returns 404 for an unresolved target session.
  • Pipeline classification — An event/action mismatch is a signed L4 rejection, a forged L2 vote rejects as L2_SIGNATURE_INVALID, and L5 receipts copy the Warden's L2/L3 status.
  • A2A — Suspends only on a missing L3 proof, like MCP. The agent harness A2A client posts to /api/v1/a2a/call.
  • Approval waits — The CLI, harness and attended flows open the SSE stream before submitting, so an early approval is no longer lost.
  • MCP tools/list — Returns native tools alongside a downstream MCP server's tools.
  • Campaign traces (breaking) — Traces are agent-trace.v2, and every governed tool call keeps its signed receipt. Import verifies the Data Operator's ledger bracket, and unverifiable evidence is INVALID_EVIDENCE with no grades.
  • run_commands_with_operator — Executes the command the model wrote in command. The second command-generation inference is removed.
  • Empty model replies — An empty completion is a receipted empty InferenceResult and grades FAIL against the model instead of provider_response_invalid (INV-EVAL-CAMP-34).
  • Compliance — Admits native eval runs only from a verified conformance record.
  • Data Operator binding — ValidateAgentDataOperator accepts only a direct child of <project>/.ztd/operators (INV-EVAL-CAMP-32). operator deploy --local without --hosts/--dest-dir deploys numbered op-NNNNN dirs there.
  • Throwaway Operator trees — .ztd/operators/<name>/… is agent-editable. The operators dir, <name> itself and the nested .ztd/ stay protected.
  • Port resolution — config.LoadGateway only validates. gw start resolves free ports (config.ResolveGatewayPorts).
  • Scratch locations — The system trust installer, assignment audit, agent verify, compliance bundle verifier, chaos targets, remote stream/deploy staging and the release-smoke default root (.ztd/operators/release-smoke-<UTC ts>) all use .ztd/ scratch. The Makefile no longer sets TMPDIR.
  • Headless onboarding — start.sh tells a host with no OS key store to start an ssh-agent with a key loaded. CI onboarding and the website live smoke run under an ephemeral ssh-agent.

Removed

  • --master-key-file / ZTD_MASTER_KEY_FILE (breaking) — The plaintext master key file is removed from gw start, operator start, operator deploy, vault, report, compliance, public and the root command, along with ZTD_E2E_MASTER_KEY_FILE and the launch-profile field. Production uses Linux Secret Service, macOS Keychain, or Windows DPAPI. Linux hosts without a usable Secret Service seal under ssh-agent (an Ed25519 or RSA key reachable through SSH_AUTH_SOCK); key-store failures fail closed.
    • Upgrade: a Gateway started on v1.0.x with --master-key-file has master_key_file in its launch profile and fails closed on restart with ErrLaunchProfileCorrupted. Its vault was sealed under the file. Load the host's key source (on Linux without Secret Service, eval "$(ssh-agent)" && ssh-add), then run ./ztd gw clean --yes, ./ztd gw start with the old startup flags, and ./ztd login. gw clean keeps the old runtime as .ztd-<MMDDHHMM>. See Upgrading a v1.0.x Gateway.
  • ztd eval boundary and the core-execution-boundary suite — replaced by ztd eval conformance.
  • Second command-generation inference in run_commands_with_operator, and CommandForbiddenTerms.
  • PathTmp and all /tmp, $HOME and %TEMP% scratch in code, tests, the Makefile and remote staging.
  • Dead Windows certificate import (internal/cli/auth/windows_crypto*.go).

Fixed

  • Release smoke runtime and receipt verification — Mutable smoke runtimes now default to .ztd/operators/ and protected --root locations are rejected before startup. Receipt verification pins the Gateway-local actuator key and checks the outbound Operator key against enrollment, then verifies both the signature and persistence attestation.
  • Rejected file edit stalled dispatch — An Operator's early path-policy rejection now publishes a typed failed file-edit result before L5 signs its failed receipt, so dispatch can return the correlated failure without waiting for a result timeout.
  • L3 deny left no evidence — A denied L3 suspension on MCP and A2A wrote no signed receipt. It is now a signed L3_DENIED rejection (see Added).
  • Early approval lost — An approval that arrived before the waiter subscribed was dropped. Waiters now subscribe first.
  • Throwaway Operators could not edit their own tree — security.IsProtectedRuntimePath rejected every .ztd path component, including .ztd/operators/<name>/….
  • Campaign mirror misclassification — CampaignMirrorReconciler treated a run with no verification report as failed instead of ineligible, because it checked fs.ErrNotExist where RuntimeFileService returns constants.ErrNotFound.
  • Config loading probed ports — config.LoadGateway opened sockets while loading configuration.
  • Model-provenance preflight test race — The fake Gateway in TestPreflightModelProvenanceContext_SSEReadyBeforeDispatch signalled readiness after flushing SSE headers. It now mirrors the real Gateway's order.

Tests

  • The Tier 1 socket check follows helpers transitively to dials and listeners, and its seeds cover tls.Listen/Dial/DialWithDialer, http.Serve/ServeTLS, and the net.Dialer, net.ListenConfig, tls.Dialer, http.Server and httptest.Server methods. Every test it flagged moved to a same-package integration file.
  • testutil.TempDir is project-local (<module>/.ztd/tmp/tNNN), and testutil.OperatorDir gives agent-editable Operator roots. forbidigo rejects os.TempDir and t.TempDir, and make lint rejects empty-dir os.MkdirTemp("")/os.CreateTemp("").
  • In-process tests use memory keyrings. ephemeral_agent_linux_integration_test.go covers the ephemeral ssh-agent.

Candidate verification (Linux, 2026-10-14):

Command Result
./ztd test unit Pass
./ztd test integration Pass (118 packages, race detector; before final lint fixes)
./ztd test integration --pkg <package> (each of ./internal/services/gateway, ./internal/services/evaluation, ./internal/cli/cmd/gwremote, ./internal/cli/auth, ./internal/cli/cmd/eval) Pass after lint fixes, race detector enabled
./ztd test lint 0 issues
make lint Pass after lint fixes: 0 issues, doctrine JSON valid, no called-code vulnerabilities (one non-called module vulnerability reported)
make console-lint, make console-test Pass (44/44)
make explorer-lint, make explorer-test Pass (590/590)
make website-test Pass (123/123, PowerShell included)
make website-build Pass
make test-onboarding Pass
make version-sync-check Pass (1.1.0, 16 declarations)
make proto-generate, make swagger-generate No diff
make console-embed-check, make constants-check, make doctrines-validate, make third-party-notices-check Pass
  • Release smoke (./ztd test release-smoke) — Pass (exit 0, result: PASS in .ztd/operators/release-smoke-20261014T232325Z/evidence/smoke-summary.json): 3 scenarios (file-create, document-merge, embedded-file-create), 6 local receipts (2 worker, 4 gateway, all EXECUTION_STATUS_COMPLETED), 0 failed rows, 0 skipped rows across both Gateway and worker reports.

Documentation

  • CHANGELOG.md — Added the ## v1.1.x index with the 1.1.0 row. The ## Unreleased bullets moved into these notes.
  • VERSION — Bumped to v1.1.0. make version-sync updated the console, evaluation explorer, adapter and protocol/node packages and the protocol/docs/ headers.
  • docs/architecture/evals.md (conformance suite, INV-EVAL-CAMP-32/34), governance.md (INV-GOV-ACT-13, protected runtime paths), auth.md, console.md, gateway.md, encryption.md and storage.md (key custody, scratch), docs/devs/devs.md (INV-FS-11) and tests.md.
  • Guides: install examples and the cosign identity name v1.1.0, and every master key file reference is replaced by the ssh-agent path.

Compliance Evidence

Operational evidence bundle generated by ./ztd compliance release-prepare:

  • Scope: gateway-operational-20261014 (window 2026-10-14T20:52:32.092Z to 2026-10-14T20:53:02.092Z, 3 receipts, 4 commitments, 2 source signer keys).
  • Public bundle: release-evidence/v1.1.0/report-bundle/manifest.json.
  • Offline verification: compliance_bundle@2.0.0 valid=true, failures=0, checksum root 10f63dd77790705606ff0189b2df8631674abb52173f80bccdceda23e799b8dc.
  • Source revision: 6e9dd54869add43ccea6029d026ed2a8bc513d54.
  • Canonical projections: docs/release_notes/v1.1.x/v1.1.0-compliance-evidence.md and docs/release_notes/v1.1.x/v1.1.0-compliance-evidence.csv.

Deferred

  • evals-reimagined: F21 (single posture table), T26, T27/T28, T34, T35, T37, T39, T40, T41, M5 attended, M6, M7, M9, M10, E1/E2 (ztde).
  • Standalone Operator + Autochthon tribunal: all of T00–T16, G1, G2. This is the v1.2.0 candidate.
  • Multi-OS onboarding VMs: all six phases, including removing the Docker-based scripts/onboarding/.
  • macOS onboarding: Developer ID signing and notarization, first macOS CI run, integration timeouts under load, Keychain isolation audit, live guide walkthrough.
  • clean-dead-code: the second deadcode cascade. It overlaps F21 and T35, so it follows them.
  • clean-store-paths: AuditService idempotency race, FinalizeNonce, logBlockedTransaction, TransactionAuditStore removal, live HTTP-dispatch checks, pubsub setter injection, NULL-session receipts, and the open owner decisions (L12 SSE plaintext, Operator active after disconnect).
  • v1.0.0 carry-forward: owner security G09/G10 (g8ee.key history, off-machine backup, rulesets and immutable releases, Dependabot), distribution D1–D3, docs-site proposal, scope trim B5 (ensemble/ztde/Python mentions, reputation endpoint, orphan collections), console/TUI parity.
  • Pending plans: llama.cpp Inference Operator backend, Autochthon host-unique culture, Operator follow-ups (TUI liveness, native harnesses), and the Go-side env-config ratchet.
  • On Windows, .env ownership and mode checking remains deferred.