CBOR deterministic encoding replaces ad-hoc serialization across the signing path; CDDL schemas define the canonical envelopes; and tool provenance becomes a cryptographically-bound, anchorable attestation.
Wardex v2.2 introduced the Configuration Provenance Link (CPL) — a SHA-256 hash binding each gate decision to the configuration in effect. The hashing was correct, but the canonicalization layer was fragile: it relied on JSON marshalling with implicit key ordering and was vulnerable to edge cases (float/int representation, unicode normalization, map key ordering) that produce different bytes for the same semantic content across implementations or Go versions.
v2.3.0 replaces the entire canonicalization substrate with CBOR Core Deterministic Encoding (RFC 8949 §4.2.3) and formalizes every envelope with CDDL schemas (RFC 8610). On top of that foundation, it ships the 3CP Tool Provenance Attestation — a signed, anchorable proof of which tool produced which artifact.
internal/cpl/canonicalConfig() now serializes configuration via github.com/fxamacker/cbor/v2 in deterministic mode instead of encoding/json. The same logical config always produces byte-identical signed bytes across any CBOR implementation and platform.
SealMessage() produces CBOR deterministic bytes instead of \n-separated text. The .wexstate file remains YAML for human readability; only the signed message changes.VerifySeal tries CBOR verification first, falls back to the legacy format for existing v1 states, enabling safe migration without breaking deployed files.SealMessage() now returns ([]byte, error) instead of []byte. Callers updated: SealConfig() and VerifySeal().config_hash values changed. Audit logs with config_hash computed before v2.3.0 will fail link verification if the config is re-hashed. Migrate by re-hashing configs with the new canonicalizer (see internal/doc/CBOR_MIGRATION_v2.3.0.md).The spec/cddl/ directory defines three formal schemas serving as the source of truth for serialization and interoperability:
| Schema | Defines |
|---|---|
cpl-entry.cddl | CPL audit log entries |
wexstate.cddl | WexState sealed config envelopes |
tool-attestation.cddl | 3CP tool provenance attestations |
These are both design artifacts and the foundation for conformance testing — any implementation producing bytes that fail CDDL validation is, by definition, non-conformant.
The new pkg/attest/ package defines 3CP tool provenance envelopes with CBOR deterministic signing and verification. A ToolAttestation carries tool identity, input/output hashes, config hash, and timestamp; a SignedAttestation wraps it with an Ed25519 signature.
The Anchorer interface abstracts the 3CP backend — Gleipnir is one backend among equals (embedded, gRPC, noop). The attestation format (spec/cddl/tool-attestation.cddl) is the canonical 3CP envelope, independent of any specific implementation.
New commands and flags:
| Command / Flag | Description |
|---|---|
wardex provenance attest | Create and sign a 3CP tool attestation (CBOR deterministic), with --submit to anchor it. |
--attest | Flag on the converters (grype, sbom, kev) to emit a signed attestation alongside the conversion. |
Anchorer.SubmitAttested() | Anchors hash + CBOR attestation, upgrading converted_by from a best-effort string to a verifiable cryptographically-bound envelope. |
Before v2.3.0, the converted_by field in evidence envelopes was an unverifiable string. A converted SBOM claimed it came from wardex convert grype, but nothing enforced it. The 3CP attestation makes that claim cryptographically non-repudiable: the tool identity, input hash, and output hash are bound to an Ed25519 signature and anchored to an immutable consensus log.
Deterministic encoding is not a nice-to-have for a compliance tool — it is the precondition for reproducible signatures. If the same config can produce two different signed bytes, the signature proves nothing about the config. v2.3.0 makes the signing path reproducible by construction.
go install github.com/had-nu/wardex/v2@latest
Checksums and SBOMs are available in the GitHub Release. Note the config_hash migration step above if you verify historical audit logs.