LEVIATHAN v962456e · 962456eee1

trident

Publishing, yanking, and auditing packages

Releasing a version of a package, withdrawing it without breaking existing users, and requiring human review and signed provenance for the dependencies a project uses.

since 0.1.0-alpha.1linux

Description

This page covers the two ends of the package life cycle: the author, who publishes and withdraws versions, and the consumer, who decides which packages to trust. The commands are publish, yank, attest, audit and audit-record; their full argument lists are in trident.commands.

Publishing a version

A package is a git repository with a trident.toml that has a name, a version and the sources that make up the package. trident publish turns the current commit into a release:

$ trident publish . --path github.com/acme/json
published github.com/acme/json@1.2.0
  tag: v1.2.0 (3d897718e9af7d68553d49745db79182deefc4cb)
  hash: sha256:ab66a23e591b15c87616eb388c9f7351a4b4291b35da98d119acf2502f9e4696

It refuses unless the package's working tree is clean, so everything that is published is committed. It then creates the git tag v<version>, taking the version from the manifest, or from --tag vX.Y.Z when you give one, and records the package's hash in the checksum record. A published tag is immutable: publishing again from the same commit changes nothing and succeeds, and publishing a tag that already names a different commit is an error. --path is the repository path that consumers will write in their [[dep]] tables. trident publish creates the tag in your local repository; pushing it to the remote is a separate git push.

If TRIDENT_INDEX is set, publish also registers the package's name as a short name for that path (see trident.versions-and-integrity). A name that is already registered to another repository cannot be re-registered.

Yanking a version

trident yank <path>@<version> withdraws a version without deleting it. A fresh resolution can no longer choose a yanked version, and says so, while a project that already has the version in its trident.lock keeps building and auditing with it:

$ trident yank github.com/acme/json@1.2.0
yanked github.com/acme/json@1.2.0 (existing locks remain valid)

$ trident fetch
error: github.com/acme/json@1.2.0 is yanked and cannot be selected by a new resolution; an existing consistent trident.lock may continue to use it (run `trident why github.com/acme/json` to inspect the chain)

The yank is recorded in the checksum record on the machine where you ran the command.

Attesting where a package came from

An attestation is a signed statement that says "this identity produced this source". It names the module, the version, the source hash and the commit, and may also name a built file and its hash. It is signed with a private key by the external openssl program, which must be installed. You can create one while publishing with --sign-key private.pem --identity <name> (and optionally --artifact <file> and --attestation-out <file>), or on its own with trident attest --key private.pem --identity <name>. Without --attestation-out or --out the file is placed under $TRIDENT_HOME/attestations/.

Reviewing and enforcing a policy

trident audit-record <path>@<version> --auditor <name> writes a review record: "this auditor reviewed this module at this version and this source hash". It appends to trident.audits.toml beside the manifest, or to the file named by --file, and the module must be in the project's lock:

$ trident audit-record github.com/acme/json@1.2.0 --auditor security-team
recorded audit by security-team for github.com/acme/json@1.2.0 in trident.audits.toml

trident audit always verifies that every locked module's source matches the checksum record and the lock (see trident.versions-and-integrity). If a file named trident.audit.toml sits beside the manifest, or you pass --policy <file>, it also enforces that policy for every locked module:

version = 1
trusted_auditors = ["security-team"]
audit_files = ["trident.audits.toml"]
require_attestations = true
attestation_dirs = ["attestations"]

[[key]]
identity = "release-ci"
public_key = "keys/release-ci.pem"
Key Meaning
trusted_auditors Every locked module needs a review record from one of these auditors.
audit_files The files that hold the review records written by audit-record.
require_attestations When true, every locked module needs a valid signed attestation.
attestation_dirs Folders searched for attestation files.
[[key]] One per signer: the identity an attestation claims and the public_key that must verify it. Put these last.

Relative paths are resolved beside the policy file. An audit record or attestation pins the module, the version and the source hash, so it stops applying if the source changes. A review record from an auditor who is not trusted, or an attestation whose signature does not verify, makes the audit fail. When the policy passes, the report lists the evidence that was accepted:

$ trident audit
OK   github.com/acme/json@1.2.0  sha256:ab66a23e591b15c87616eb388c9f7351a4b4291b35da98d119acf2502f9e4696
AUDIT github.com/acme/json@1.2.0 by security-team
ATTEST github.com/acme/json@1.2.0 by acme-ci (./attestations/json.attestation)
policy passed: ./trident.audit.toml
audit passed: 1 module(s) verified

The package that these commands act on is ordinary Leviathan source. The one published above holds a namespace of public functions:

A package's source, with a caller

namespace Json {
    public string encode(int n) => "{\"value\":" + n + "}";
    public string tag(string s) => "<" + s + ">";
}
uses Json;
console.writeln(encode(42));
console.writeln(tag("hi"));
{"value":42}
<hi>

Rules

  • publish needs a clean git working tree and a version of the form vMAJOR.MINOR.PATCH.
  • A tag is immutable: the same commit may be published again, a different commit may not.
  • A yanked version is never chosen by a new resolution and never removed from a lock that already uses it.
  • Without a policy file trident audit only verifies hashes. With one, it also enforces trusted auditors and signed provenance.
  • Signing and verifying use the openssl program and are only needed when provenance is requested.
  • [[key]] tables come last in a policy file.

Examples

A release with a signed attestation:

trident publish . --path github.com/acme/json \
    --sign-key private.pem --identity acme-ci --attestation-out attestations/json.attestation
git push origin v1.2.0

A consumer recording a review and then requiring it:

trident audit-record github.com/acme/json@1.2.0 --auditor security-team
trident audit

Notes

  • trident audit needs an existing lock; run trident lock first if there is none.
  • There is no command to delete a published version; yanking is the supported way to stop new projects from selecting it.

See also

  • Versions, the lock file, and integrity — How trident picks versions of repository dependencies, what trident.lock and the checksum record guarantee, and how to build without the network.
  • The trident commands — Every trident subcommand with its arguments, grouped into building, managing dependencies, and publishing and auditing.
  • The manifest: trident.toml — Every key of trident.toml, how sources and assets are listed, and the three ways a project can choose its entry point.