Publishing and releases
How a complete release is built and which files it contains.
Sequence
Drawing the diagram…
sequenceDiagram autonumber participant P as publish.yml participant GH as GitHub Releases participant S as site.yml participant GP as GitHub Pages participant CF as Cloudflare Pages participant V as verify_live.py P->>P: re-verify final set, seal, sign, tar.gz, metadata P->>P: verify_release.py on the exact files to publish P->>GH: create DRAFT release with every asset P->>GH: check the asset count, then publish P->>S: request site deployment S->>GH: download the release and history S->>S: verify_release.py, build catalog and stats, record verification S->>GP: deploy S->>CF: deploy mirror (preflight first) S->>V: verify the public URL V->>GP: package, every rule file, catalog, links V-->>S: PASS or FAIL logged in the transparency log
Diagram description
After the final checks, publish.yml builds the archive and metadata, re-verifies the artifacts, creates a draft release, attaches every asset and only then makes it public. site.yml then builds the catalog from the published release, verifies it again, deploys GitHub Pages, then verifies the public URL with the real bytes. The Cloudflare mirror is deployed when its credential works.
Mermaid code
sequenceDiagram
autonumber
participant P as publish.yml
participant GH as GitHub Releases
participant S as site.yml
participant GP as GitHub Pages
participant CF as Cloudflare Pages
participant V as verify_live.py
P->>P: re-verify final set, seal, sign, tar.gz, metadata
P->>P: verify_release.py on the exact files to publish
P->>GH: create DRAFT release with every asset
P->>GH: check the asset count, then publish
P->>S: request site deployment
S->>GH: download the release and history
S->>S: verify_release.py, build catalog and stats, record verification
S->>GP: deploy
S->>CF: deploy mirror (preflight first)
S->>V: verify the public URL
V->>GP: package, every rule file, catalog, links
V-->>S: PASS or FAIL logged in the transparency logpublish.ymlfirst checks (precheck) that the candidate differs from the latest release; if identical it asks for no approval and publishes nothing.- It re-verifies the final set (classification and dynamic evidence) before signing.
- It signs the checkpoint, seals and signs the manifest, writes the inclusion proof, packages and generates the metadata.
- It checks the exact artifacts before anything becomes public, creates the release as a draft, attaches every file and only then makes it public.
Files in a release
| File | Content | Mandatory |
|---|---|---|
rules-release.tar.gz | The rules (rules/) and manifest.json. | yes |
manifest.json | List of files with SHA-256, sources, rules_root, log head and statistics. | yes |
manifest.sigstore.json | Sigstore signature of the manifest. | yes |
inclusion-proof.json | Merkle inclusion proof of the release entry. | yes |
checkpoint.txt | Log checkpoint. | yes |
checkpoint.sigstore.json | Sigstore signature of the checkpoint. | yes |
release.json | Metadata: counts, sources, changes, package. | no |
changes.json, CHANGELOG.md | Files added, modified and removed against the previous one. | no |
consistency.json | Consistency proof with the previous release log. | no (if a previous release exists) |
Real example: the latest release manifest.json (abridged):
{
"schema": 1,
"created_at": "2026-10-18T17:36:07.186971+00:00",
"sources": [
{
"commit": "3ce6640c26046da8e6d8094af3eaaee3ee84e648",
"id": "nextron",
"kind": "rules",
"license_check": {
"detail": "DRL-1.1: allowed-with-attribution",
"ok": true,
"redistribution": "allowed-with-attribution",
"sha256": "8a959b2413d0c83ca1b3ce9153076c649abf2e4bbfebbb493fb24c2074a1eb8a",
"spdx_reported": "NOASSERTION"
},
"repository": "Neo23x0/signature-base"
},
{
"commit": "0f93570194a80d2f2032869055808b0ddcdfb360",
"id": "community",
"kind": "rules",
"license_check": {
"detail": "GPL-2.0: allowed-with-license-notice",
"ok": true,
"redistribution": "allowed-with-license-notice",
"sha256": "c03cea027b4b40e4402fabd08557736727ec3d5bc54ad64ab6472de432198cad",
"spdx_reported": "GPL-2.0"
},
"repository": "Yara-Rules/rules"
},
{
"commit": "649cd3bb08e1c4ecc1524cc016c7aa27a75d2193",
"id": "yara-x",
"kind": "engine",
"repository": "VirusTotal/yara-x"
}
],
"files": [
{
"bytes": 27570,
"path": "rules/community/antidebug_antivm/antidebug_antivm.yar",
"sha256": "f3ee419817136b14253a1b9b1b1d0f36de7cb61b35539b2f9c643c102f0c701d",
"source": "community"
},
"... 1173 more entries ..."
],
"rules_root": "16d082395ef1ec53d0ad25d5840d83e7f5f3a75a283ce66f8fa75da106790ec5",
"log": {
"origin": "macguard-rules-log v1",
"root": "1bab9bb9364b3a42f76835ae138bffad33eaa3694d0021afd208f5fd54e63d96",
"size": 75
},
"stats": {
"expanded_bytes": 7861571,
"files": 1174,
"yara_rules": 9958
},
"validation": {
"checked": 1174,
"engine": "yara"
},
"status": "PUBLISHED"
}Manifest keys: created_at, files, log, rejected, rules_root, schema, sources, stats, status, validation.
Publication rules
- Every release is the complete set, not a patch. A correction is a new release; tags are never reused.
- No effective change, no release.
- Changes to the web or documentation only never create rule releases.
- GitHub immutable releases are not enabled (a repository security setting that needs a person's authorization); the draft → public flow is already the one that setting requires.