@cleocode/skills 2026.4.4 → 2026.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/ct-adr-recorder/SKILL.md +175 -0
- package/skills/ct-adr-recorder/manifest-entry.json +30 -0
- package/skills/ct-adr-recorder/references/cascade.md +82 -0
- package/skills/ct-adr-recorder/references/examples.md +141 -0
- package/skills/ct-artifact-publisher/SKILL.md +146 -0
- package/skills/ct-artifact-publisher/manifest-entry.json +30 -0
- package/skills/ct-artifact-publisher/references/artifact-types.md +126 -0
- package/skills/ct-artifact-publisher/references/handler-interface.md +187 -0
- package/skills/ct-consensus-voter/SKILL.md +158 -0
- package/skills/ct-consensus-voter/manifest-entry.json +30 -0
- package/skills/ct-consensus-voter/references/matrix-examples.md +140 -0
- package/skills/ct-grade/references/token-tracking.md +2 -2
- package/skills/ct-grade/scripts/run_all.py +1 -1
- package/skills/ct-grade-v2-1/manifest-entry.json +1 -1
- package/skills/ct-ivt-looper/SKILL.md +181 -0
- package/skills/ct-ivt-looper/manifest-entry.json +30 -0
- package/skills/ct-ivt-looper/references/escalation.md +91 -0
- package/skills/ct-ivt-looper/references/frameworks.md +119 -0
- package/skills/ct-ivt-looper/references/loop-anatomy.md +156 -0
- package/skills/ct-orchestrator/manifest-entry.json +1 -1
- package/skills/ct-provenance-keeper/SKILL.md +161 -0
- package/skills/ct-provenance-keeper/manifest-entry.json +30 -0
- package/skills/ct-provenance-keeper/references/signing.md +188 -0
- package/skills/ct-provenance-keeper/references/slsa.md +121 -0
- package/skills/ct-release-orchestrator/SKILL.md +134 -0
- package/skills/ct-release-orchestrator/manifest-entry.json +30 -0
- package/skills/ct-release-orchestrator/references/composition.md +138 -0
- package/skills/ct-release-orchestrator/references/release-types.md +130 -0
- package/skills/ct-skill-creator/manifest-entry.json +1 -1
- package/skills/ct-skill-creator/references/provider-deployment.md +9 -9
- package/skills/ct-skill-validator/evals/evals.json +1 -1
- package/skills/ct-skill-validator/manifest-entry.json +1 -1
- package/skills/manifest.json +252 -16
- package/skills/ct-skill-creator/.cleo/.context-state.json +0 -13
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ct-provenance-keeper
|
|
3
|
+
description: "Generates in-toto v1 attestations, SLSA-level provenance records, SBOMs (CycloneDX or SPDX), and sigstore/cosign signatures for published artifacts. Invoked by ct-artifact-publisher as a delegation for signing and attestation. Records the full commit, then build, then artifact, then attestation, then registry chain in .cleo/releases.json and rejects publishes whose digest does not match the attestation. Triggers when artifact-publish reaches the provenance step or when a release needs SLSA L2+ attestation."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Provenance Keeper
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Sub-protocol of ct-artifact-publisher. Generates cryptographic evidence for every published artifact: SHA-256 digests, in-toto Statement v1 attestations, SBOMs (CycloneDX 1.5+ or SPDX 2.3+), and signatures via sigstore/cosign keyless or gpg. Records the full commit-to-registry chain in `.cleo/releases.json`, verifies chain integrity before publishing attestations, and refuses to bind an attestation to an artifact whose digest does not match.
|
|
11
|
+
|
|
12
|
+
## Core Principle
|
|
13
|
+
|
|
14
|
+
> Provenance is non-falsifiable or it is not provenance.
|
|
15
|
+
|
|
16
|
+
## Immutable Constraints
|
|
17
|
+
|
|
18
|
+
| ID | Rule | Enforcement |
|
|
19
|
+
|----|------|-------------|
|
|
20
|
+
| PROV-001 | Chain MUST be recorded from source commit to published artifact. | `record_release()` writes the full chain; missing links set `metadata.completeness: incomplete`. |
|
|
21
|
+
| PROV-002 | SHA-256 digest MUST be computed for every produced artifact. | Digest binds to the in-toto subject; mismatch exits 93. |
|
|
22
|
+
| PROV-003 | Attestation MUST be in in-toto Statement v1 format. | Validator rejects other schemas; exit 94. |
|
|
23
|
+
| PROV-004 | SLSA Build Level MUST be recorded (L1 minimum). | Level stored in `releases.json`; L1 is the floor. |
|
|
24
|
+
| PROV-005 | Provenance record MUST be stored in `.cleo/releases.json` via `record_release()`. | Missing record fails validation. |
|
|
25
|
+
| PROV-006 | Chain integrity MUST be verified before publishing the attestation. | `verify_provenance_chain()` runs before emit. |
|
|
26
|
+
| PROV-007 | Manifest entry MUST set `agent_type: "provenance"`. | Validator rejects any other value. |
|
|
27
|
+
|
|
28
|
+
## SLSA Compliance Levels
|
|
29
|
+
|
|
30
|
+
The skill records the achieved SLSA level in every provenance record. A minimum of L1 is mandatory; L3 is the target for npm + docker releases via OIDC keyless signing.
|
|
31
|
+
|
|
32
|
+
| Level | Requirements | Typical achievement |
|
|
33
|
+
|-------|--------------|---------------------|
|
|
34
|
+
| L1 | Provenance exists, basic metadata recorded. | Any run of the skill produces at least L1. |
|
|
35
|
+
| L2 | Signed provenance, build on a hosted platform. | Requires sigstore keyless OR gpg signing + CI build. |
|
|
36
|
+
| L3 | Non-falsifiable provenance, hermetic build environment. | CI runs with isolated runners and OIDC-bound identity (this is the default for npm + docker releases). |
|
|
37
|
+
| L4 | Reproducible builds, two-party review, all deps signed. | Rare; requires pinned dependencies and second-reviewer sign-off. |
|
|
38
|
+
|
|
39
|
+
Full requirements and per-level checklists are in [references/slsa.md](references/slsa.md).
|
|
40
|
+
|
|
41
|
+
## Signing Methods
|
|
42
|
+
|
|
43
|
+
Three methods are supported; the skill selects based on `release.security.provenance.signing.method`:
|
|
44
|
+
|
|
45
|
+
| Method | Command | Output | When to use |
|
|
46
|
+
|--------|---------|--------|-------------|
|
|
47
|
+
| `sigstore` (default, keyless) | `cosign sign-blob --yes --output-signature <sig> --output-certificate <cert> <artifact>` | `.sig` + `.pem` + Rekor transparency log entry | Default. Requires OIDC identity (CI). |
|
|
48
|
+
| `sigstore` (key) | `cosign sign-blob --key <ref> <artifact>` | `.sig` | Offline or self-hosted without OIDC. |
|
|
49
|
+
| `gpg` | `gpg --detach-sign --armor -u <key-id> <artifact>` | `.asc` | Legacy workflows, regulated environments. |
|
|
50
|
+
| `none` | (skip) | (none) | SLSA L1 only; explicit opt-out. |
|
|
51
|
+
|
|
52
|
+
The decision matrix, including `GPG_KEY_ID` and OIDC trusted-publisher setup, lives in [references/signing.md](references/signing.md).
|
|
53
|
+
|
|
54
|
+
## Provenance Chain
|
|
55
|
+
|
|
56
|
+
The skill walks and records the full chain for every release:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
commit --> build --> artifact --> attestation --> registry
|
|
60
|
+
| | | | |
|
|
61
|
+
sha log digest signature published
|
|
62
|
+
| | | | |
|
|
63
|
+
source env capture sha256 file cert bundle URL
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Each link MUST reference the previous link's output. The chain is append-only in `releases.json`: no link is ever modified after creation. Missing links are recorded as `incomplete`, never elided. Offline verification MUST be possible — every digest is stored locally, not fetched per verification.
|
|
67
|
+
|
|
68
|
+
## SBOM Generation
|
|
69
|
+
|
|
70
|
+
SBOMs are mandatory for artifacts with runtime dependencies (docker images, npm packages) and recommended for standalone binaries.
|
|
71
|
+
|
|
72
|
+
| Format | Spec | Use case |
|
|
73
|
+
|--------|------|----------|
|
|
74
|
+
| CycloneDX | 1.5+ | Default. Machine-readable JSON. |
|
|
75
|
+
| SPDX | 2.3+ | Compliance-focused (FedRAMP, regulated environments). |
|
|
76
|
+
|
|
77
|
+
Storage locations:
|
|
78
|
+
|
|
79
|
+
- `.cleo/sbom/<artifact-name>-<version>.cdx.json` (CycloneDX)
|
|
80
|
+
- `.cleo/sbom/<artifact-name>-<version>.spdx.json` (SPDX)
|
|
81
|
+
- `<artifact>.sbom.json` (bundled alongside the artifact)
|
|
82
|
+
|
|
83
|
+
Generate with `syft packages dir:. -o cyclonedx-json` or equivalent.
|
|
84
|
+
|
|
85
|
+
## `.cleo/releases.json` Record Structure
|
|
86
|
+
|
|
87
|
+
Each release appends a record in the following shape. The skill MUST populate every field that can be known; the remainder stay as explicit nulls (never absent).
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
{
|
|
91
|
+
"version": "v2026.4.5",
|
|
92
|
+
"commitSha": "3a2f1e9c4b8d7e6a5f2c1d0e9f8a7b6c5d4e3f2a",
|
|
93
|
+
"gitTag": "v2026.4.5",
|
|
94
|
+
"buildInvocationId": "gh-actions-12345",
|
|
95
|
+
"builder": { "id": "https://github.com/actions/runner" },
|
|
96
|
+
"artifacts": [
|
|
97
|
+
{
|
|
98
|
+
"type": "npm-package",
|
|
99
|
+
"name": "@cleocode/core",
|
|
100
|
+
"sha256": "a1b2c3...",
|
|
101
|
+
"registry": "https://registry.npmjs.org",
|
|
102
|
+
"publishedAt": "2026-04-06T19:50:00Z",
|
|
103
|
+
"attestation": ".cleo/attestations/v2026.4.5-core.intoto.jsonl",
|
|
104
|
+
"signature": {
|
|
105
|
+
"method": "sigstore",
|
|
106
|
+
"keyless": true,
|
|
107
|
+
"transparencyLog": {
|
|
108
|
+
"index": "123456789",
|
|
109
|
+
"url": "https://rekor.sigstore.dev"
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
],
|
|
114
|
+
"sbom": {
|
|
115
|
+
"format": "CycloneDX",
|
|
116
|
+
"specVersion": "1.5",
|
|
117
|
+
"path": ".cleo/sbom/cleocode-core-2026.4.5.cdx.json"
|
|
118
|
+
},
|
|
119
|
+
"slsaLevel": "SLSA_BUILD_LEVEL_3",
|
|
120
|
+
"chainVerified": true,
|
|
121
|
+
"recordedAt": "2026-04-06T19:51:00Z"
|
|
122
|
+
}
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
## Integration
|
|
126
|
+
|
|
127
|
+
Validate the provenance entry through `cleo check protocol`:
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
cleo check protocol \
|
|
131
|
+
--protocolType provenance \
|
|
132
|
+
--taskId T4902 \
|
|
133
|
+
--hasAttestation true \
|
|
134
|
+
--hasSbom true
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Exit code 0 = provenance record is complete and verified. Exit code 90 = invalid config. Exit code 91 = signing key missing. Exit code 92 = signature invalid. Exit code 93 = digest mismatch (refuse to bind attestation). Exit code 94 = attestation format or subject is invalid.
|
|
138
|
+
|
|
139
|
+
## Anti-Patterns
|
|
140
|
+
|
|
141
|
+
| Pattern | Problem | Solution |
|
|
142
|
+
|---------|---------|----------|
|
|
143
|
+
| Skipping digest computation | Chain integrity cannot be verified (violates PROV-002) | Always compute SHA-256 for every artifact before attesting |
|
|
144
|
+
| Hardcoding signing keys in config | Key exposure, credentials in VCS | Reference env vars by name; actual keys stay in the environment |
|
|
145
|
+
| Generating attestation without matching digest | Attestation binds to the wrong artifact (violates PROV-006) | Compute the digest first, then attest; refuse to attest a mismatched pair |
|
|
146
|
+
| Publishing artifact before signing | Cannot retrofit signatures after publish | Sign before push; the sub-protocol order is build → sign → publish |
|
|
147
|
+
| Modifying provenance records after creation | Breaks immutability, corrupts the audit trail | `.cleo/releases.json` is append-only; never rewrite old entries |
|
|
148
|
+
| Skipping SBOM for artifacts with dependencies | Hidden supply-chain risk | Generate CycloneDX for every artifact with runtime deps |
|
|
149
|
+
| Using SHA-1 or MD5 for digests | Cryptographically broken; non-compliant with SLSA | SHA-256 is mandatory; SHA-512 is optional for high-security contexts |
|
|
150
|
+
| Storing private keys inside `.cleo/` | Key compromise if the repo is leaked | Keys live in the keystore / OIDC / HSM — never in the worktree |
|
|
151
|
+
|
|
152
|
+
## Critical Rules Summary
|
|
153
|
+
|
|
154
|
+
1. Compute SHA-256 for every artifact; bind the attestation to the exact digest.
|
|
155
|
+
2. Produce attestations in in-toto Statement v1 format only.
|
|
156
|
+
3. Record the full chain in `.cleo/releases.json` via `record_release()`.
|
|
157
|
+
4. Verify chain integrity before publishing the attestation.
|
|
158
|
+
5. Default to sigstore keyless signing via OIDC in CI; fall back to gpg only when configured.
|
|
159
|
+
6. Generate CycloneDX SBOMs for every artifact with runtime dependencies.
|
|
160
|
+
7. `.cleo/releases.json` is append-only; never mutate past entries.
|
|
161
|
+
8. Validate every run via `cleo check protocol --protocolType provenance`.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "CLEO-only metadata -- add to packages/skills/skills/manifest.json",
|
|
3
|
+
"name": "ct-provenance-keeper",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"tier": 2,
|
|
6
|
+
"token_budget": 6000,
|
|
7
|
+
"protocol": "provenance",
|
|
8
|
+
"capabilities": {
|
|
9
|
+
"inputs": ["built-artifacts", "commit-sha", "builder-identity"],
|
|
10
|
+
"outputs": ["in-toto-attestation", "sbom", "signature", "releases-json-entry"],
|
|
11
|
+
"dispatch_triggers": [
|
|
12
|
+
"generate provenance",
|
|
13
|
+
"sign artifacts",
|
|
14
|
+
"create attestation",
|
|
15
|
+
"record supply chain",
|
|
16
|
+
"run slsa attestation"
|
|
17
|
+
],
|
|
18
|
+
"compatible_subagent_types": ["general-purpose"],
|
|
19
|
+
"chains_to": [],
|
|
20
|
+
"dispatch_keywords": {
|
|
21
|
+
"primary": ["provenance", "attestation", "sbom", "slsa"],
|
|
22
|
+
"secondary": ["sigstore", "cosign", "in-toto", "sign"]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"constraints": {
|
|
26
|
+
"max_context_tokens": 60000,
|
|
27
|
+
"requires_session": false,
|
|
28
|
+
"requires_epic": false
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Signing Methods Reference
|
|
2
|
+
|
|
3
|
+
This file documents the three supported signing methods and when to pick each. The default is `sigstore` keyless via OIDC, which is what every npm and docker release in this monorepo uses.
|
|
4
|
+
|
|
5
|
+
## Method Decision Matrix
|
|
6
|
+
|
|
7
|
+
| Method | Trust root | Requires | Use when |
|
|
8
|
+
|--------|------------|----------|----------|
|
|
9
|
+
| `sigstore` keyless | Rekor transparency log | OIDC identity (CI) | Default for every CI release; produces SLSA L3. |
|
|
10
|
+
| `sigstore` keyed | Local or cloud key | Cosign key reference | Self-hosted CI without OIDC; produces SLSA L2. |
|
|
11
|
+
| `gpg` | GPG key in the local keystore | `GPG_KEY_ID` env var | Legacy workflows, air-gapped environments. |
|
|
12
|
+
| `none` | (no signing) | — | SLSA L1 only; explicit opt-out, never default. |
|
|
13
|
+
|
|
14
|
+
## Sigstore Keyless (default)
|
|
15
|
+
|
|
16
|
+
Cosign issues a short-lived certificate bound to an OIDC identity (GitHub Actions token, Google OAuth, etc.) and publishes the signature to Rekor for transparency.
|
|
17
|
+
|
|
18
|
+
### Command
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
cosign sign-blob \
|
|
22
|
+
--yes \
|
|
23
|
+
--output-signature "${ARTIFACT}.sig" \
|
|
24
|
+
--output-certificate "${ARTIFACT}.pem" \
|
|
25
|
+
"${ARTIFACT}"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Outputs
|
|
29
|
+
|
|
30
|
+
- `<artifact>.sig` — detached signature.
|
|
31
|
+
- `<artifact>.pem` — short-lived certificate from Fulcio.
|
|
32
|
+
- Transparency log entry in Rekor (the command prints the index).
|
|
33
|
+
|
|
34
|
+
### OIDC Trusted Publishing
|
|
35
|
+
|
|
36
|
+
For npm, configure the trusted publisher in the npm registry settings:
|
|
37
|
+
|
|
38
|
+
1. Go to the package page → Settings → Trusted Publishers.
|
|
39
|
+
2. Add the GitHub repo and workflow file (`.github/workflows/release.yml`).
|
|
40
|
+
3. Scope the trust to the `release` job.
|
|
41
|
+
|
|
42
|
+
The workflow then runs `npm publish --provenance` without any token; npm exchanges the OIDC token for short-lived publish permission and produces the SLSA L3 attestation automatically.
|
|
43
|
+
|
|
44
|
+
For GHCR (docker), the `GITHUB_TOKEN` in CI is already OIDC-bound; no extra setup is needed.
|
|
45
|
+
|
|
46
|
+
### Verification
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
cosign verify-blob \
|
|
50
|
+
--certificate "${ARTIFACT}.pem" \
|
|
51
|
+
--signature "${ARTIFACT}.sig" \
|
|
52
|
+
--certificate-identity-regexp "^https://github.com/kryptobaseddev/cleocode/" \
|
|
53
|
+
--certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
|
|
54
|
+
"${ARTIFACT}"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Sigstore Keyed
|
|
58
|
+
|
|
59
|
+
Use when OIDC is unavailable but cosign is still preferred.
|
|
60
|
+
|
|
61
|
+
### Command
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
cosign sign-blob \
|
|
65
|
+
--key "cosign.key" \
|
|
66
|
+
--output-signature "${ARTIFACT}.sig" \
|
|
67
|
+
"${ARTIFACT}"
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### Key storage
|
|
71
|
+
|
|
72
|
+
- **Local**: `cosign.key` file (reference only the path, never the contents).
|
|
73
|
+
- **Cloud KMS**: `cosign.key` reference like `awskms:///alias/cosign`.
|
|
74
|
+
- **HashiCorp Vault**: `hashivault://<path>`.
|
|
75
|
+
|
|
76
|
+
### Verification
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
cosign verify-blob \
|
|
80
|
+
--key cosign.pub \
|
|
81
|
+
--signature "${ARTIFACT}.sig" \
|
|
82
|
+
"${ARTIFACT}"
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## GPG
|
|
86
|
+
|
|
87
|
+
Use for air-gapped environments, regulated workflows, or legacy release lines that predate sigstore.
|
|
88
|
+
|
|
89
|
+
### Command
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
gpg --detach-sign --armor -u "${GPG_KEY_ID}" "${ARTIFACT}"
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Requirements
|
|
96
|
+
|
|
97
|
+
- `GPG_KEY_ID` environment variable set to the signing key identifier.
|
|
98
|
+
- The private key loaded in the GPG keyring (`gpg --list-secret-keys`).
|
|
99
|
+
- A corresponding public key published somewhere consumers can fetch.
|
|
100
|
+
|
|
101
|
+
### Output
|
|
102
|
+
|
|
103
|
+
- `<artifact>.asc` — ASCII-armored detached signature.
|
|
104
|
+
|
|
105
|
+
### Verification
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
gpg --verify "${ARTIFACT}.asc" "${ARTIFACT}"
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## None
|
|
112
|
+
|
|
113
|
+
Explicit opt-out. Use only for internal pre-release builds that will never leave the build environment. Producing a `none` signing method downgrades the release to SLSA L1 automatically.
|
|
114
|
+
|
|
115
|
+
```json
|
|
116
|
+
{
|
|
117
|
+
"release": {
|
|
118
|
+
"security": {
|
|
119
|
+
"provenance": {
|
|
120
|
+
"enabled": true,
|
|
121
|
+
"framework": "slsa",
|
|
122
|
+
"level": "SLSA_BUILD_LEVEL_1",
|
|
123
|
+
"signing": { "method": "none" }
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
The skill rejects any config that declares `signing.method: none` with a target level higher than L1.
|
|
131
|
+
|
|
132
|
+
## Validation Decision Tree
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
signing.method configured?
|
|
136
|
+
+-- "sigstore" (default)
|
|
137
|
+
| +-- IS keyless enabled (default: true)?
|
|
138
|
+
| +-- YES -> cosign sign-blob --yes <artifact>
|
|
139
|
+
| require OIDC identity in env
|
|
140
|
+
| +-- NO -> cosign sign-blob --key <key-ref> <artifact>
|
|
141
|
+
| require key reference resolvable
|
|
142
|
+
+-- "gpg"
|
|
143
|
+
| +-- GPG_KEY_ID set?
|
|
144
|
+
| +-- YES -> gpg --detach-sign --armor -u <key-id> <artifact>
|
|
145
|
+
| +-- NO -> Exit 91 (E_SIGNING_KEY_MISSING)
|
|
146
|
+
+-- "none"
|
|
147
|
+
+-- Target level > L1?
|
|
148
|
+
+-- YES -> Exit 90 (E_PROVENANCE_CONFIG_INVALID)
|
|
149
|
+
+-- NO -> Skip signing; record as unsigned
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Signing Metadata Record
|
|
153
|
+
|
|
154
|
+
Every signed artifact appends a `signature` block to its entry in `releases.json`:
|
|
155
|
+
|
|
156
|
+
```json
|
|
157
|
+
{
|
|
158
|
+
"signature": {
|
|
159
|
+
"method": "sigstore",
|
|
160
|
+
"keyless": true,
|
|
161
|
+
"signed": true,
|
|
162
|
+
"signedAt": "2026-04-06T19:50:32Z",
|
|
163
|
+
"signature": ".cleo/attestations/cleocode-core-2026.4.5.sig",
|
|
164
|
+
"certificate": ".cleo/attestations/cleocode-core-2026.4.5.pem",
|
|
165
|
+
"transparencyLog": {
|
|
166
|
+
"index": "123456789",
|
|
167
|
+
"url": "https://rekor.sigstore.dev"
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
For gpg:
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"signature": {
|
|
178
|
+
"method": "gpg",
|
|
179
|
+
"keyless": false,
|
|
180
|
+
"signed": true,
|
|
181
|
+
"signedAt": "2026-04-06T19:50:32Z",
|
|
182
|
+
"signature": ".cleo/attestations/cleocode-core-2026.4.5.asc",
|
|
183
|
+
"keyId": "0xDEADBEEFCAFEBABE"
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
The skill MUST populate the method and signedAt fields on every signed record. Optional fields (transparency log, certificate) are filled when the signing method provides them.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
# SLSA Compliance Reference
|
|
2
|
+
|
|
3
|
+
SLSA (Supply-chain Levels for Software Artifacts) defines four levels of build provenance rigor. This file lists the exact requirements per level and the concrete checklist this skill uses to determine which level a release achieved.
|
|
4
|
+
|
|
5
|
+
## Level Matrix
|
|
6
|
+
|
|
7
|
+
| Requirement | L1 | L2 | L3 | L4 |
|
|
8
|
+
|-------------|:--:|:--:|:--:|:--:|
|
|
9
|
+
| Provenance exists | MUST | MUST | MUST | MUST |
|
|
10
|
+
| Provenance is signed | — | MUST | MUST | MUST |
|
|
11
|
+
| Build on hosted platform | — | MUST | MUST | MUST |
|
|
12
|
+
| Non-falsifiable provenance (isolated builder) | — | — | MUST | MUST |
|
|
13
|
+
| All dependencies have provenance | — | — | — | MUST |
|
|
14
|
+
| Two-party review | — | — | — | MUST |
|
|
15
|
+
| Hermetic, reproducible build | — | — | — | MUST |
|
|
16
|
+
|
|
17
|
+
## Level 1 Checklist
|
|
18
|
+
|
|
19
|
+
- [ ] Provenance record exists in `.cleo/releases.json` for this release.
|
|
20
|
+
- [ ] `commitSha` is populated.
|
|
21
|
+
- [ ] `artifacts[].sha256` is populated for every artifact.
|
|
22
|
+
- [ ] `builder.id` is a non-empty URI (local builder is fine).
|
|
23
|
+
- [ ] `buildInvocationId` is recorded (can be a local uuid).
|
|
24
|
+
|
|
25
|
+
If any of the above is missing, the release is L0 (non-compliant). The skill refuses to mark a release L0 — it raises the missing-field errors and asks for them to be populated.
|
|
26
|
+
|
|
27
|
+
## Level 2 Checklist
|
|
28
|
+
|
|
29
|
+
All L1 items plus:
|
|
30
|
+
|
|
31
|
+
- [ ] Attestation is signed (sigstore keyless or gpg).
|
|
32
|
+
- [ ] Signature verifies against the attestation subject digest.
|
|
33
|
+
- [ ] Build ran on a hosted CI platform (GitHub Actions, GitLab CI, CircleCI, etc.).
|
|
34
|
+
- [ ] `builder.id` points at the hosted runner's identity URI.
|
|
35
|
+
|
|
36
|
+
The skill determines L2 compliance automatically by inspecting the `signature.method` field and the `builder.id` URL shape.
|
|
37
|
+
|
|
38
|
+
## Level 3 Checklist
|
|
39
|
+
|
|
40
|
+
All L2 items plus:
|
|
41
|
+
|
|
42
|
+
- [ ] Provenance is non-falsifiable: the builder cannot forge attestations for builds it did not run.
|
|
43
|
+
- [ ] Build runs on an isolated runner (no long-lived state from prior builds).
|
|
44
|
+
- [ ] Signing identity is bound to the CI run via OIDC.
|
|
45
|
+
- [ ] The attestation is published to a transparency log (Rekor for sigstore).
|
|
46
|
+
|
|
47
|
+
In practice, L3 means: GitHub Actions with OIDC trusted publishing, sigstore keyless signing, and a Rekor transparency log entry. This is the default for npm + docker releases in this monorepo.
|
|
48
|
+
|
|
49
|
+
## Level 4 Checklist
|
|
50
|
+
|
|
51
|
+
All L3 items plus:
|
|
52
|
+
|
|
53
|
+
- [ ] Every runtime dependency has its own provenance attestation.
|
|
54
|
+
- [ ] The build is reproducible (same inputs produce identical output digests).
|
|
55
|
+
- [ ] A second reviewer signed off on the build config change that produced this release.
|
|
56
|
+
- [ ] The builder image is pinned to a digest, not a tag.
|
|
57
|
+
|
|
58
|
+
L4 is rare outside regulated environments. The skill does not attempt to claim L4 automatically; a human reviewer MUST assert the two-party review and reproducibility checks before the skill records L4.
|
|
59
|
+
|
|
60
|
+
## Configuration
|
|
61
|
+
|
|
62
|
+
The desired level is declared in `.cleo/config.json`:
|
|
63
|
+
|
|
64
|
+
```json
|
|
65
|
+
{
|
|
66
|
+
"release": {
|
|
67
|
+
"security": {
|
|
68
|
+
"provenance": {
|
|
69
|
+
"enabled": true,
|
|
70
|
+
"framework": "slsa",
|
|
71
|
+
"level": "SLSA_BUILD_LEVEL_3"
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The skill attempts to achieve the declared level. If the environment cannot satisfy it (e.g., declared L3 but no OIDC identity available), the skill records the actual level achieved and flags the shortfall in the manifest entry. It does NOT silently downgrade.
|
|
79
|
+
|
|
80
|
+
## Level Detection Decision Tree
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
HAS provenance record in releases.json?
|
|
84
|
+
+-- NO -> Level 0 (non-compliant; error)
|
|
85
|
+
+-- YES
|
|
86
|
+
+-- IS signature.method in ["sigstore", "gpg"]?
|
|
87
|
+
| +-- NO -> Level 1
|
|
88
|
+
| +-- YES
|
|
89
|
+
| +-- IS builder.id a hosted CI runner URI?
|
|
90
|
+
| | +-- NO -> Level 1 (key-based but local build)
|
|
91
|
+
| | +-- YES
|
|
92
|
+
| | +-- IS signature.transparencyLog populated (Rekor for sigstore) OR equivalent?
|
|
93
|
+
| | | +-- NO -> Level 2
|
|
94
|
+
| | | +-- YES
|
|
95
|
+
| | | +-- ALL deps have provenance AND build is reproducible AND two-party reviewed?
|
|
96
|
+
| | | | +-- NO -> Level 3
|
|
97
|
+
| | | | +-- YES -> Level 4 (requires human assertion)
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Recording Level
|
|
101
|
+
|
|
102
|
+
The detected level is written to `releases.json` as a string:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"slsaLevel": "SLSA_BUILD_LEVEL_3"
|
|
107
|
+
}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Valid values: `SLSA_BUILD_LEVEL_1`, `SLSA_BUILD_LEVEL_2`, `SLSA_BUILD_LEVEL_3`, `SLSA_BUILD_LEVEL_4`. Never write `L0` — that's a validation error that the skill surfaces before writing anything.
|
|
111
|
+
|
|
112
|
+
## Verification
|
|
113
|
+
|
|
114
|
+
A consumer can verify SLSA level claims by:
|
|
115
|
+
|
|
116
|
+
1. Fetching the attestation from the registry or local store.
|
|
117
|
+
2. Verifying the signature via `cosign verify-blob` or `gpg --verify`.
|
|
118
|
+
3. Inspecting the `builder.id` in the predicate.
|
|
119
|
+
4. Checking the Rekor transparency log for the entry.
|
|
120
|
+
|
|
121
|
+
The skill provides `verify_provenance_chain()` for internal checks; consumers use the standard SLSA tooling.
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ct-release-orchestrator
|
|
3
|
+
description: "Orchestrates the full release pipeline: version bump, then changelog, then commit, then tag, then conditionally forks to artifact-publish and provenance based on release config. Parent protocol that composes ct-artifact-publisher and ct-provenance-keeper as sub-protocols: not every release publishes artifacts (source-only releases skip it), and artifact publishers delegate signing and attestation to provenance. Use when shipping a new version, running cleo release ship, or promoting a completed epic to released status."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Release Orchestrator
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Owns the top of the release pipeline: semver bump, changelog, release commit, and git tag. Composes two sub-protocols conditionally — ct-artifact-publisher when the release config has enabled artifacts, and ct-provenance-keeper when signing or attestation is required. Source-only releases (docs, spec changes) stop after the tag and skip both sub-protocols.
|
|
11
|
+
|
|
12
|
+
## Core Principle
|
|
13
|
+
|
|
14
|
+
> Release is the parent protocol; artifact-publish and provenance are conditional sub-protocols.
|
|
15
|
+
|
|
16
|
+
## Immutable Constraints
|
|
17
|
+
|
|
18
|
+
| ID | Rule | Enforcement |
|
|
19
|
+
|----|------|-------------|
|
|
20
|
+
| RLSE-001 | Version MUST follow semantic versioning (`v{major}.{minor}.{patch}`). | `validateReleaseProtocol` rejects non-semver strings; exit 53. |
|
|
21
|
+
| RLSE-002 | Changelog MUST be updated with all changes before the tag. | `hasChangelog: false` fails validation unless `--no-changelog` is explicit. |
|
|
22
|
+
| RLSE-003 | All validation gates MUST pass before the release proceeds. | Ship halts on any gate failure; exit 54. |
|
|
23
|
+
| RLSE-004 | Release MUST be tagged in version control. | Missing tag fails validation; exit 56. |
|
|
24
|
+
| RLSE-005 | Breaking changes MUST be documented with a migration path. | Required section in the changelog entry. |
|
|
25
|
+
| RLSE-006 | Version MUST be consistent across all files listed in `release.versionBump`. | Mismatched files fail validation; exit 55. |
|
|
26
|
+
| RLSE-007 | Manifest entry MUST set `agent_type: "documentation"`. | Validator rejects any other value. |
|
|
27
|
+
| RLSE-008 | Parent protocol MUST hand off to artifact-publish when `release.artifacts` is non-empty. | Composition invariant from ARTP-005. |
|
|
28
|
+
| RLSE-009 | Provenance chain MUST be recorded for every signed release. | Composition invariant from PROV-005. |
|
|
29
|
+
|
|
30
|
+
## Composition Pipeline
|
|
31
|
+
|
|
32
|
+
The release parent protocol composes with the artifact-publish and provenance sub-protocols via explicit handoffs:
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
Release Protocol Artifact Publish Protocol
|
|
36
|
+
--- ---
|
|
37
|
+
1. Version bump
|
|
38
|
+
2. Changelog generation
|
|
39
|
+
3. Validation gates
|
|
40
|
+
4. Git commit + tag
|
|
41
|
+
5. ---- HANDOFF ------------------> 6. Load artifact config
|
|
42
|
+
7. Pre-validate all artifacts
|
|
43
|
+
8. Build all artifacts
|
|
44
|
+
9. ---- HANDOFF ----> Provenance Protocol
|
|
45
|
+
10. Compute digests
|
|
46
|
+
11. Generate in-toto attestation
|
|
47
|
+
12. Sign (sigstore keyless)
|
|
48
|
+
13. Record chain in releases.json
|
|
49
|
+
14. <--- RETURN ----
|
|
50
|
+
15. Publish signed artifacts
|
|
51
|
+
16. Record provenance to releases.json
|
|
52
|
+
17. <--- RETURN ----------------------
|
|
53
|
+
18. Push to remote
|
|
54
|
+
19. Update release status to "released"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Each handoff uses a distinct exit code:
|
|
58
|
+
|
|
59
|
+
| Edge | Exit code | Meaning |
|
|
60
|
+
|------|-----------|---------|
|
|
61
|
+
| Release → artifact-publish | 65 (`HANDOFF_REQUIRED`) | Parent yields control to the sub-protocol |
|
|
62
|
+
| artifact-publish → provenance | 65 (`HANDOFF_REQUIRED`) | Sub-protocol delegates signing |
|
|
63
|
+
| provenance → artifact-publish | 0 on success | Return to parent sub-protocol |
|
|
64
|
+
| artifact-publish → release | 0 on success, 88 on publish fail | Return to parent with result |
|
|
65
|
+
| release → tag push | 0 on success, 56 on tag fail | Final commit |
|
|
66
|
+
|
|
67
|
+
Partial-failure rollback semantics are documented in [references/composition.md](references/composition.md).
|
|
68
|
+
|
|
69
|
+
## Conditional Trigger Matrix
|
|
70
|
+
|
|
71
|
+
Not every release needs both sub-protocols. The parent decides based on `release.artifacts` and `release.security.provenance.enabled`:
|
|
72
|
+
|
|
73
|
+
| Release type | Needs artifact-publish | Needs provenance |
|
|
74
|
+
|--------------|:---------------------:|:----------------:|
|
|
75
|
+
| `source-only` (docs, spec changes, code-only merges without a package) | no | no |
|
|
76
|
+
| `npm-package` | yes | yes (SLSA L3 via npm `--provenance`) |
|
|
77
|
+
| `docker-image` | yes | yes (cosign keyless attestation) |
|
|
78
|
+
| `cargo-crate` | yes | yes (GPG or sigstore) |
|
|
79
|
+
| `github-tarball` | yes | optional (MAY sign via cosign) |
|
|
80
|
+
| `multi-artifact` (npm + docker + tarball combo) | yes | yes |
|
|
81
|
+
|
|
82
|
+
The parent skill inspects `.cleo/config.json#release.artifacts[]`. If the array is empty or all entries are disabled, the release is `source-only` and the pipeline stops after the tag.
|
|
83
|
+
|
|
84
|
+
## CI Integration
|
|
85
|
+
|
|
86
|
+
The existing `.github/workflows/release.yml` uses `npm publish --provenance` with the repository's OIDC trust configuration, producing SLSA L3 keyless attestations automatically. This skill's responsibility is to ensure the resulting chain is recorded in the manifest entry and in `.cleo/releases.json`, not to re-implement the signing step. When CI has already produced an attestation, the skill MUST read its reference from the workflow output and record it verbatim.
|
|
87
|
+
|
|
88
|
+
## Integration
|
|
89
|
+
|
|
90
|
+
Invoke the parent pipeline via `cleo release ship`, then validate with `cleo check protocol`:
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Kick off the release pipeline.
|
|
94
|
+
cleo release ship v2026.4.5 \
|
|
95
|
+
--epic T260 \
|
|
96
|
+
--bump-version \
|
|
97
|
+
--create-tag \
|
|
98
|
+
--push
|
|
99
|
+
|
|
100
|
+
# Validate the parent protocol entry.
|
|
101
|
+
cleo check protocol \
|
|
102
|
+
--protocolType release \
|
|
103
|
+
--taskId T4900 \
|
|
104
|
+
--version v2026.4.5 \
|
|
105
|
+
--hasChangelog true
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Exit code 0 = release complete. Exit code 50 = release not found. Exit code 54 = validation gate failed. Exit code 55 = version bump failed. Exit code 56 = tag creation failed. Exit code 88 = artifact publish failed (bubbled from sub-protocol). Exit code 94 = attestation invalid (bubbled from provenance).
|
|
109
|
+
|
|
110
|
+
For source-only releases, pass `--no-artifacts` to skip the artifact-publish handoff. Every other release type leaves the default behavior alone.
|
|
111
|
+
|
|
112
|
+
## Anti-Patterns
|
|
113
|
+
|
|
114
|
+
| Pattern | Problem | Solution |
|
|
115
|
+
|---------|---------|----------|
|
|
116
|
+
| Publishing artifacts before running validation gates | Can't roll back a successful publish on a failed build | Follow the pipeline order: gates → commit → tag → publish |
|
|
117
|
+
| Pushing the git tag before publishing artifacts | Tag points to a commit whose packages never shipped | Push the tag after artifacts are live, or use the same job |
|
|
118
|
+
| Skipping the dry-run phase | Irreversible registry state on first real attempt | ARTP-002 requires dry-run; the parent skill refuses to skip it |
|
|
119
|
+
| Source-only releases triggering artifact-publish | Wasted CI time, false SLSA attestations | Check `release.artifacts` before handoff; skip if empty |
|
|
120
|
+
| Not recording the provenance chain in releases.json | Canon loses the commit → build → artifact → attestation link | Parent MUST record even when CI generated the attestation |
|
|
121
|
+
| Overusing `--force` to bypass epic completeness | Ships partial epics without review | Use the guard mode `warn` and address gaps explicitly |
|
|
122
|
+
| Mutating a `released` entry after the fact | Canon must be immutable once shipped | Create a new release entry for the hotfix |
|
|
123
|
+
| Running ship on a dirty worktree | Commits scoop up unrelated changes | Require a clean worktree before step 1 |
|
|
124
|
+
|
|
125
|
+
## Critical Rules Summary
|
|
126
|
+
|
|
127
|
+
1. Version MUST be valid semver; the parent skill refuses non-semver strings.
|
|
128
|
+
2. The changelog MUST be updated before the tag — no exceptions beyond explicit `--no-changelog`.
|
|
129
|
+
3. All validation gates MUST pass before the commit step.
|
|
130
|
+
4. The pipeline composes with artifact-publish and provenance only when the release config calls for it.
|
|
131
|
+
5. Exit codes bubble up unchanged: 88 from artifact-publish and 94 from provenance surface at the parent.
|
|
132
|
+
6. `released` entries are immutable; hotfixes go into new entries.
|
|
133
|
+
7. Manifest entry MUST set `agent_type: "documentation"` and record the full chain via `record_release()`.
|
|
134
|
+
8. Always validate via `cleo check protocol --protocolType release` before declaring the release done.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"_comment": "CLEO-only metadata -- add to packages/skills/skills/manifest.json",
|
|
3
|
+
"name": "ct-release-orchestrator",
|
|
4
|
+
"version": "1.0.0",
|
|
5
|
+
"tier": 2,
|
|
6
|
+
"token_budget": 8000,
|
|
7
|
+
"protocol": "release",
|
|
8
|
+
"capabilities": {
|
|
9
|
+
"inputs": ["version", "epic-id", "release-config"],
|
|
10
|
+
"outputs": ["release-commit", "git-tag", "changelog-entry", "manifest-entry"],
|
|
11
|
+
"dispatch_triggers": [
|
|
12
|
+
"ship release",
|
|
13
|
+
"cut release",
|
|
14
|
+
"publish version",
|
|
15
|
+
"cleo release ship",
|
|
16
|
+
"promote epic to released"
|
|
17
|
+
],
|
|
18
|
+
"compatible_subagent_types": ["general-purpose"],
|
|
19
|
+
"chains_to": ["ct-artifact-publisher", "ct-provenance-keeper"],
|
|
20
|
+
"dispatch_keywords": {
|
|
21
|
+
"primary": ["release", "ship", "version", "tag"],
|
|
22
|
+
"secondary": ["changelog", "semver", "bump", "publish"]
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"constraints": {
|
|
26
|
+
"max_context_tokens": 60000,
|
|
27
|
+
"requires_session": false,
|
|
28
|
+
"requires_epic": true
|
|
29
|
+
}
|
|
30
|
+
}
|