Added — the receipt format is now a published, independently testable spec
docs/RECEIPT_SPEC.mddocuments the
signetry.remediation-receiptv1 format in full: envelope, payload,
canonicalization, signing, the verification algorithm, the §4.3 invariants, and a
change contract. RFC 2119 language throughout. A receipt is meant to be verifiable
by someone who does not have this tool and is reading it years later, so the format
is an interface and is now written down as one.tests/conformance/— 17 assertions over 7 committed JSON
vectors, with published test-key seed strings so an implementation in any language
can be checked against exactly the same files. Regenerate with
python tests/conformance/generate_vectors.py; the vectors are committed rather
than computed at test time so a change to canonicalization or signing shows up as a
diff.- The spec and the suite are Apache-2.0, named as explicit
exclusions from the BUSLLicensed WorkinLICENSE. They carry no
restriction and no Change Date. Writing a competing issuer or an independent
verifier against the spec is a supported use. check_invariants(receipt)(exported fromsignetry_core.pipeline) enforces
RECEIPT_SPEC §4.3:auto_mergemust befalse,human_review_requiredmust be
true, pluskind/version/authority_levelwell-formedness.verify_receipt
now returnsconformingandinvariant_violationsalongside its cryptographic
result, because those are different questions — a receipt can be correctly signed
and still claim something the format forbids.signetry verifynow fails on a non-conforming receipt, not just an unverifiable
one, and says which of the two failed. A validly signed receipt with
auto_merge: trueprintsNON-CONFORMINGandREJECTEDand exits1; it never
prints a bareVERIFIED. This is what makes "Signetry never merges on its own
judgement" a checkable property of every receipt instead of a promise in a README.
Added — policy registry
signetry policiesandsignetry init --policy <id>. Six starter admission
contracts for common repository shapes:docs-only,dependency-bump,
python-library,node-service,monorepo-service,ci-workflow-fix. Writing the
first contract is where adoption stalls, and "which globs should an agent be allowed to
touch in this stack" is a real security decision most teams defer.- The published file is the installed file.
init --policycopies the registry bytes
verbatim — no templating, no merge — so an adopter can diff their
.signetry/admission.yamlagainst the registry and get nothing back. Verified in CI. - Every entry carries its own evidence. A policy declares example paths it must block and
must allow in# @policyheader comments, andtests/test_policy_registry.pyruns each
claim through the realevaluate_contract. A policy whose documentation does not match
its behaviour fails CI. Theallowsdirection is the one that catches an over-broad
forbidden glob quietly making a policy useless. ci-workflow-fixcarries acautionthatinitprints at adoption time, because write
access to.github/workflowsis a privilege-escalation path and a registry that shipped
it silently would be worse than one that omitted it.- New public helper
is_policy_placeholder, andsignetry_core/policies/ships in the
wheel (confirmed against a built artifact, not assumed).
Changed — licence: open core (BUSL-1.1, converting to Apache-2.0)
signetry-coreis now licensed BUSL-1.1 and converts to
Apache-2.0 on 2030-08-31, replacing the previous "All Rights Reserved"
proprietary terms. You may read, run in your own CI, use in production to govern
repositories you or your organization control, fork, modify, and redistribute it;
the one carve-out is offering it to third parties as a paid, competing hosted
service.pyproject.toml'slicensefield is nowBUSL-1.1.- The integration surface is Apache-2.0: the
Action,
plugins,
pre-commit guard, and
eval suite. - The CLA still applies — open core means code moves across the BUSL/Apache line,
and the assignment is what allows that relicensing without re-asking every past
contributor.CLA.md,CONTRIBUTING.md, andCONTRIBUTORS.mdwere rewritten for
the open-source posture; README/docs/workflow comments no longer claim the project
is "not open source" or "All Rights Reserved". - No functional or API change. Distribution is unchanged: still installed from source
by tag, not published to PyPI. - The CLA's fallback licence grant is now non-exclusive. It previously granted the
Owner an exclusive licence where copyright assignment is not permitted by law, which
would have stripped contributors of the right to use their own contribution — directly
contradicting the rights the LICENSE grants everyone. The CLA text is now identical
across all Signetry repositories (bar the engine/integration licence wording) so the
legal terms cannot drift per-repo again. See CLA.md §2–3.
Fixed
- A scaffold placeholder was reported as declared provenance.
signetry initwritespolicy_owner: your-team, andpolicy_status()reported
declared— "Policy declares a human owner and version (change-controlled
metadata)" — for a file no human had read. Every receipt from a freshly initialised
repo asserted change-control that did not exist. - Placeholder provenance is now treated as absent, with its own status value:
declared/placeholder/incomplete, each carrying anoteexplaining which.
Consumers must treat anything other thandeclaredas not change-controlled; the extra
values exist to say why, which is actionable, and never mean "good enough". - Note for consumers matching on this field: a repo that ran
signetry initand never
edited the provenance keys now reportsplaceholderwhere it previously reported
declared. That is the bug being fixed, not a regression. - Two repo-root-relative links in
docs/RELEASING.mdresolved fromdocs/and were
therefore broken.
Added — Python insecure-deserialisation coverage
marshal.load(s)andshelve.opennow flagged (CWE-502) — both execute arbitrary
code during decoding, and neither was detected.yaml.unsafe_loadflagged, and the Loader is now resolved rather than merely
counted: the previous check treated anyLoader=kwarg as safe, so an explicitly
unsafeyaml.load(x, Loader=yaml.Loader)passed silently.- Gaps identified by @AdvaitVarhade in #87/#91.
Fixed — a positional safe Loader was a false positive
yaml.load(x, yaml.SafeLoader)was flagged, because the old check only inspected
keyword arguments. The Loader is now read from the keyword or the second
positional argument, and matched on its last path segment so bothyaml.SafeLoader
and a bare importedSafeLoaderare recognised.
BUSL-1.1 (Apache-2.0 on 2030-08-31); install from source:
pip install "signetry-core @ git+https://github.com/Signetry/core@v0.8.0"