Documentation
¶
Overview ¶
Package signing signs and verifies the two signable op artifacts — graphs and execution traces — per the settled signing design (docs/plans/extract-starlark-from-op/phase-8/signing-options.md and graph-signing.md; phase-8 step 46).
The model is publisher identity with verifier-side trust: a raw ssh-ed25519 signature over the artifact's namespace-prefixed canonical bytes (NamespaceGraph / NamespaceTrace give domain separation), the publisher's key riding the document in OpenSSH wire format, and trust resolved against an OpenSSH `allowed_signers` file the verifier owns. No envelope, no hash options — the algorithm names the whole ciphersuite.
This package implements the DEFAULT custody tier: the developer's SSH key (`~/.ssh/id_ed25519`) with a generated local Ed25519 keyfile as the fallback. The ssh-agent, cloud-KMS, and keyless tiers are chartered opt-ins (the support matrix in signing-options.md) and are deliberately absent so their dependency weight stays out of the default build.
What a consumer does with a verification outcome is governed by Policy — the settled four-tier ladder — through one enforcement point, [Judge]. Like its prior art (PowerShell's ExecutionPolicy), the policy is a safety feature, not a security boundary.
Index ¶
Constants ¶
const ( // NamespaceGraph is the domain-separation prefix signed ahead of a graph's canonical bytes. NamespaceGraph = "devlore.graph.v1" // NamespaceTrace is the domain-separation prefix signed ahead of a trace's canonical bytes. NamespaceTrace = "devlore.trace.v1" // AlgorithmEd25519 is the default ciphersuite — Ed25519 under its OpenSSH key-type name. AlgorithmEd25519 = "ssh-ed25519" )
Variables ¶
This section is empty.
Functions ¶
func CanonicalDocument ¶
CanonicalDocument returns a serialized document's canonical bytes: the generically-decoded document with its integrity fields (`checksum`, `signature`) removed, re-marshaled.
This is the verify-side dual of the live artifacts' CanonicalContent for document-form canonicalization (traces): decoding into the typed struct can be lossy (custom unmarshalers), so verification canonicalizes the bytes it was handed. yaml key ordering is stable, so the result matches the sign-time canonical.
Parameters:
- `data`: the document bytes (YAML).
Returns:
- `[]byte`: the canonical bytes.
- `error`: non-nil when the document does not decode or re-marshal.
func External ¶
External reports whether a document path lies outside `storeHome` — the settled externality marker (a document under this machine's own store was produced by its own runs; anything else is external).
Parameters:
- `documentPath`: the document's path as given.
- `storeHome`: the machine's store root (the devlore state home).
Returns:
- `bool`: true when the document is external to the store.
Types ¶
type Outcome ¶
type Outcome int
Outcome classifies one artifact's verification result.
const ( // OutcomeValid means the signature verifies and the publisher key resolved to a trusted principal. OutcomeValid Outcome = iota // OutcomeUnsigned means the artifact carries no signature — a finding of absence, not a failure. OutcomeUnsigned // OutcomeInvalid means a signature is present but does not verify over the canonical bytes (the artifact // was altered after signing, or the signature is malformed). OutcomeInvalid // OutcomeUntrusted means the signature verifies but the publisher key resolved to no trusted principal in // the verifier's `allowed_signers`. OutcomeUntrusted )
type Policy ¶
type Policy int
Policy governs what a consumer does with a verification Verdict — the settled four-tier ladder (phase-8 step 46, question 4; prior art: PowerShell ExecutionPolicy, pacman SigLevel, Kubernetes admission enforce/warn/audit).
Like its prior art, the policy is a safety feature, not a security boundary: it prevents accidents and surfaces facts; it does not stop a local adversary who can change the policy.
const ( // PolicyReport verifies and reports findings — both unsigned (absence) and invalid/untrusted (failure) — // but refuses nothing. The floor: unsigned stores keep working while signature state becomes visible. PolicyReport Policy = iota // PolicyIgnore performs no verification at all. PolicyIgnore // PolicyRejectExternal rejects unsigned, invalid, or untrusted documents from OUTSIDE this machine's own // store; own-store documents behave as [PolicyReport]. The store boundary is the externality marker. PolicyRejectExternal // PolicyReject rejects every document that is not valid — unsigned, invalid, or untrusted alike. PolicyReject )
func ParsePolicy ¶
ParsePolicy parses a config/flag policy value.
Parameters:
- `value`: "ignore", "report", "reject_external", or "reject"; "" parses as the PolicyReport floor.
Returns:
- `Policy`: the parsed policy.
- `error`: non-nil for any other value.
func (Policy) Judge ¶
Judge applies the policy to a verdict: the one enforcement point every consumer shares.
Parameters:
- `verdict`: the artifact's verification result.
- `external`: whether the document came from outside this machine's own store (see External).
Returns:
- `error`: the rejection when the policy refuses this verdict; nil to proceed (the caller reports the verdict regardless — except under PolicyIgnore, where there is nothing to report).
type Signer ¶
type Signer interface {
// Sign produces the signature over `namespace ‖ canonical`.
//
// Parameters:
// - `namespace`: the artifact-kind domain separator ([NamespaceGraph] or [NamespaceTrace]).
// - `canonical`: the artifact's canonical bytes.
//
// Returns:
// - `*op.Signature`: the algorithm, the publisher key (OpenSSH wire format), and the raw signature.
// - `error`: non-nil when signing fails.
Sign(namespace string, canonical []byte) (*op.Signature, error)
}
Signer signs canonical artifact bytes under a namespace, producing the document's op.Signature.
func DefaultSigner ¶
DefaultSigner resolves the default-tier signer: the developer's SSH key, else the generated local key.
Resolution order (signing-options.md): `~/.ssh/id_ed25519` when present and parseable without a passphrase; otherwise the generated local Ed25519 keyfile under the user config directory (`<config>/devlore/signing/ed25519`, created on first use with its `.pub` in authorized_keys format for `allowed_signers` seeding). The generated path honors `XDG_CONFIG_HOME` on every platform (the devlore XDG convention).
Returns:
- `Signer`: the resolved signer.
- `error`: non-nil when no key can be loaded or generated.
type Verdict ¶
type Verdict struct {
// Outcome is the classification.
Outcome Outcome
// Principal is the trusted identity the publisher key resolved to; "" unless the outcome is valid.
Principal string
// Detail elaborates non-valid outcomes for human readers.
Detail string
}
Verdict is one artifact's verification result: the outcome, the trusted principal when valid, and the human-readable detail otherwise.
func Verify ¶
func Verify(signature *op.Signature, namespace string, canonical []byte, allowedSignersPath string) Verdict
Verify checks an artifact's signature over its namespace-prefixed canonical bytes and resolves the publisher against the trust list.
Parameters:
- `signature`: the artifact's signature, or nil for unsigned.
- `namespace`: the artifact-kind domain separator (NamespaceGraph or NamespaceTrace).
- `canonical`: the artifact's canonical bytes.
- `allowedSignersPath`: the verifier's trust list; "" resolves to the default (`<config>/devlore/allowed_signers`).
Returns:
- `Verdict`: the classification, with the trusted principal on validity.