workrr trust Preview

Specification · draft

workrr Trust Certificate — Specification v0.1 (draft)

The exact format of a workrr certificate and how to verify one. Both the TypeScript and Go implementations are held to this document by shared test vectors.

predicate type https://trust.workrr.ai/certificate/v0.1 rendered from workrr-trust/certify v0.1.5 (5fb6a66) JSON Schema
Source of truth: the file in the GitHub repo at the tag above. This page is a rendering of it and changes only when a new certifier version is released.

A workrr certificate is an in-toto Statement v1 inside a DSSE envelope, signed keylessly through Sigstore and recorded in the public Rekor transparency log. This document is the contract every implementation (TypeScript, Go) must follow. Two implementations given the same artifact, policy, and ruleset must produce byte-identical checks.

1. Artifact digest

The subject digest is always sha256.

The subject name is the artifact's declared name (SKILL.md name, server.json name) or else the directory/file basename.

2. Artifact type

Detected in this order: SKILL.md present → skill; server.json present → mcp-server; AGENTS.md or CLAUDE.md present → agent-context; else other. A single file named SKILL.md, AGENTS.md, or CLAUDE.md maps the same way.

3. Checks

Every check records exactly what was used, so anyone can re-run it.

Check Result values Recorded inputs
provenance pass · fail source URI, commit, path; whether the artifact is unchanged from that commit
policy pass · fail policy digest (sha256 of canonical JSON), violations
scan pass · fail ruleset version + digest, findings (rule id, severity, file, line)
tests pass · fail · none command, exit code, duration

Scan wording. A pass means "no known-pattern findings of severity high or critical." It never means safe or injection-free. Findings of severity warn are recorded and don't fail the check.

Canonical JSON. Object keys sorted lexicographically, no whitespace, UTF-8. Precisely: keys sorted by UTF-8 byte order; strings and numbers serialized as ECMAScript JSON.stringify does (only ", \ and U+0000–U+001F are escaped; 1e+21, 1e-7, -0 → 0); absent optional fields are omitted, never null.

Determinism rules. Gold compares checks byte for byte, so these are part of the contract, not implementation details:

Cross-implementation test vectors live in test-vectors/; every implementation must pass them.

4. Level

Display names say how much scrutiny each level means: Scanned (scanned), Signed (banded), Tested (silver), Reproduced (gold). The wire values below are what certificates carry.

Level Rule
silver provenance, policy, scan, and tests all pass
banded provenance, policy, scan pass; tests none (not deterministically testable). Receives a deterministic A–E trust band (predicate.band).
scanned a public scan (docs/public-scans.md): an observer certifier run by workrr in its own repository read a public commit. Provenance, policy, scan pass; tests always none (a scan never runs the project's code). Not signed by the maintainer. Carries the same A–E band as banded. Ranks below banded.
(no certificate) any check fail — the certifier exits non-zero and nothing is signed as a certificate. A public scan that fails a security check signs a finding instead (§8).
gold Assigned only by verifiers, never by certifiers: two silver certificates for the same digest, signed by accepted certifier identities on two different forges, with identical checks.

Trust bands (A–E). When level is banded, the certifier computes a deterministic A–E grade over four intrinsic signals (1 point each; 4 → A, 3 → B, 2 → C, 1 → D, 0 → E), unless overridden by external outcome evidence:

  1. Zero scan warnings: scan.findings has no warn-severity findings.
  2. Scoped tools: SKILL.md declares allowed-tools or the policy pins allowedTools.
  3. Structured instructions: for a skill, description states when to trigger (use when) and the body has a Markdown heading or numbered step (1. ); for other types, a top-level README or README.md is present.
  4. Declared license: a top-level LICENSE/LICENCE/COPYING file or a non-blank license frontmatter field is present.

5. Signing and logging

6. Verification

A verifier must:

  1. Recompute the artifact digest (§1) and match it to the Statement subject.
  2. Check predicateType is a supported version.
  3. Verify the Sigstore bundle: certificate chain, signature, and Rekor inclusion.
  4. Match the signer identity (SAN, issuer, Build Signer Digest) against the trust policy. A SAN of refs/heads/main, an unlisted tag, or a digest other than the one pinned for that tag is rejected. The policy embedded in this build lists the certify.yml release tags and the scan.yml observer tag (src/trust-policy.ts).
  5. Cross-check source.uri and source.commit in the predicate against the signing certificate's Fulcio SourceRepositoryURI (1.3.6.1.4.1.57264.1.12) and SourceRepositoryDigest (1.3.6.1.4.1.57264.1.13) extensions when present, and derive the certified level (silver or banded) directly from the signed checks results rather than trusting predicate.level alone. Observer signers (trust policy "role": "observer", the public-scan workflow) are the exception: their Fulcio SourceRepositoryURI must be https://github.com/workrr-trust/certify, predicate.level must be scanned and checks.tests.result must be none. The predicate's source is then the observed repository and is not cross-checked against Fulcio. Such a bundle is reported as scanned only when no publisher bundle verifies, and it never counts toward gold. A publisher signer's certificate that claims scanned is rejected.
  6. Read checks.policy.policyName and checks.policy.policyDigest and report both. The name workrr-baseline-v0.1 is reserved: that name with any other digest is rejected. Any other digest is not a baseline result, and the verifier must not report silver, gold, or banded for it. A signed trust policy's baselinePolicyDigests map is an additional allow-list when that document is in use; it does not relax this rule.
  7. If the caller passes a source repository, it must match source.uri after normalizing .git, embedded credentials, and git@host:org/repo. owner/repo and host/owner/repo match that URL. A mismatch is a failed verification and is not reported at a baseline level.
  8. Check the certificate isn't revoked (§7). --offline skips this and must say so.
  9. For gold: repeat 1–7 for a second bundle from a different forge, require both policies to be the known baseline, and compare checks byte-for-byte as canonical JSON. The built-in policy has no second forge, so it does not award gold.

7. Signed trust policy and revocation

The trust policy is a versioned JSON document. Verifiers fetch it, check its signature, and cache it. The service that hosts it is not a trust anchor.

7.1 Policy document

Media type of the signed bytes: application/vnd.workrr.trust-policy+json. The bytes are canonical JSON (§3) with no trailing newline.

Field Rule
schema workrr-trust-policy-v1
sequence Integer ≥ 1. A verifier rejects a document whose sequence is lower than the sequence embedded in that build or lower than a sequence it has already verified.
issuedAt YYYY-MM-DDThh:mm:ssZ
signers Non-empty list of certifier identities: forge (github or gitlab), issuer, subjectAlternativeName (regular expression), buildSignerDigests (list of 40-hex commits; empty means any commit of that workflow).
baselinePolicyDigests Map of policy name → sha256: + 64 hex. When a signed policy is in use, a certificate whose checks.policy.policyDigest is outside this map fails.
revocationFeed https URL of the revocation collection, with no /since suffix.
revocationWatermark Integer ≥ 0. Entries 1..watermark existed when this policy was signed.
revocationHead 64-hex sha256 of the canonical entry at revocationWatermark. Sixty-four zeros when the watermark is 0.

sequence only goes up. An older signed document is a rollback:

ROLLBACK: trust policy sequence 1 is older than 3

The same rule applies to a revocation checkpoint sequence the client has already verified.

7.2 Signature

The on-wire envelope is application/vnd.workrr.signed-blob+json;version=1:

{
  "mediaType": "application/vnd.workrr.signed-blob+json;version=1",
  "payloadType": "application/vnd.workrr.trust-policy+json",
  "payload": "<standard base64 of the canonical document>",
  "signature": { "algorithm": "sigstore", "bundle": { } }
}

payload is standard base64 (with padding) of the exact bytes that were signed. Those bytes must be canonical JSON. The signature is over those bytes, not over the envelope.

Production signatures are Sigstore bundles from cosign sign-blob --bundle, made by a release-tag run of:

Both identities use issuer https://token.actions.githubusercontent.com and a SAN of

https://github.com/workrr-trust/certify/.github/workflows/<workflow>.yml@refs/tags/vX.Y.Z

refs/heads/main is not accepted. On a revocation entry or checkpoint the document's signer field must equal that certificate SAN.

The shared test vectors sign with ed25519 instead, so both implementations can check tamper and rollback without Fulcio. Production verifiers do not accept that key.

7.3 Revocation entry

Payload type application/vnd.workrr.revocation-entry+json. Schema workrr-revocation-v1.

Field Rule
sequence Integer ≥ 1, contiguous from the previous entry.
subject Exactly one of artifactDigest (64 lowercase hex) or logIndex (decimal Rekor index, no leading zeros).
reason One line, 1–200 characters, no ASCII controls.
timestamp YYYY-MM-DDThh:mm:ssZ
signer https URI of the identity that signed the entry.
prev 64-hex sha256 of the previous entry's canonical JSON, or 64 zeros for sequence 1.

A checkpoint (application/vnd.workrr.revocation-checkpoint+json, schema workrr-revocation-checkpoint-v1) has sequence, head (sha256 of the entry at that sequence), timestamp, and signer. It is signed the same way.

A client that loads a policy with watermark w and head h fetches <revocationFeed>/since/<w> and requires:

A match fails that bundle with REVOKED: <reason>. If every otherwise-valid bundle is revoked, the result status is REVOKED.

A feed that answers but does not verify fails closed. A feed that cannot be reached does not. The client adds revocation was not checked.

7.4 Fetch, cache, offline

offline: using the embedded trust policy; revocation was not checked

A revoked certificate can still verify under --offline. Passing --trust-policy a legacy {version, signers} file is a local override; the signature is not checked and the command says so. A signed blob passed that way is verified.

The host (https://trust-api.workrr.ai, or WORKRR_TRUST_API) is transport. Replacing it changes availability, not who can sign.

8. Findings

A public scan that fails a high or critical scan rule, or the policy's forbidden file type or escaping symlink rules, signs an in-toto Statement with predicate type https://trust.workrr.ai/finding/v0.1. Its predicate carries artifact, source and checks as in §3 (tests always none), plus kind (concealment when any failing rule is WT002, WT003, WT007 or WT008; otherwise check), findings (one item per failure: check, rule, severity, file, line where applicable, detail), certifier and scannedAt.

A finding is valid only from an observer signer whose Fulcio source repository is workrr-trust/certify. To accept one, a verifier checks the signature and Rekor inclusion as in §6, then reproduces it: it runs the same checks on the named commit and requires identical findings and kind under the same ruleset and policy digests. Certificate verifiers reject this predicate type. Format and policy: docs/findings.md.

Versioning

Breaking changes bump the predicate type URI. Verifiers must reject unknown predicate types.