tea

package
v3.84.7 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 2, 2026 License: AGPL-3.0 Imports: 14 Imported by: 0

Documentation

Overview

Package tea is a client for the OWASP Transparency Exchange API.

It speaks both halves of the same API: the consumption operations any conformant server serves, and the publication operations that create the objects those consumers read. That is deliberate — they are one API at one root, and a client that treated them as two would have to be told twice which server it is talking to.

The default server is Vulnetix, but nothing here assumes it. A --server pointing at another organisation's TEA endpoint works for every read; writes will simply be refused by a server that does not know the caller, which is the correct outcome rather than something to guard against locally.

Index

Constants

View Source
const DefaultServer = "https://www.vulnetix.com/tea/v1"

DefaultServer is Vulnetix's own TEA root. It is the `rootUrl` advertised in https://vulnetix.com/.well-known/tea.

Variables

This section is empty.

Functions

func ArtifactTypeForFile

func ArtifactTypeForFile(path string) string

ArtifactTypeForFile maps a document to the TEA artifact type enum.

Defaults to OTHER rather than BOM: claiming a file is a bill of materials when it might not be tells a consumer to parse it as one.

func ContentDigest

func ContentDigest(body []byte) string

ContentDigest renders the RFC 9530 header for a body.

The server verifies this before storing anything, which is what makes an upload safe to retry: a truncated transfer fails loudly instead of being published under a checksum asserting it arrived intact.

func MediaTypeForFile

func MediaTypeForFile(path string) string

MediaTypeForFile guesses the media type of a transparency document from its name.

Only the formats TEA actually carries are recognised. Anything else becomes application/octet-stream rather than a guess: an artifact served under the wrong media type is one a consumer's parser will reject, and being wrong is worse than being unspecific.

func NormaliseDomain

func NormaliseDomain(in string) (string, error)

NormaliseDomain reduces what a person pastes — a bare domain, a URL, or a TEI — to the domain-name component discovery starts from.

func RootFrom

func RootFrom(doc *WellKnown) string

RootFrom picks the endpoint and version to use from a discovery document.

Highest priority wins, and among an endpoint's versions the newest STABLE one is chosen. Plain SemVer ordering would rank 1.1.0-beta.1 above 1.0.0, and pointing a user's tooling at a provider's beta API is not a decision to make on their behalf.

Types

type AccessPolicy

type AccessPolicy struct {
	ObjectKind string       `json:"objectKind,omitempty"`
	ObjectUUID string       `json:"objectUuid,omitempty"`
	Visibility string       `json:"visibility"`
	SharedWith []ShareGrant `json:"sharedWith,omitempty"`
}

AccessPolicy is who may read an object.

type AccessStatus

type AccessStatus struct {
	UUID          string        `json:"uuid"`
	Declared      *AccessPolicy `json:"declared,omitempty"`
	Effective     AccessPolicy  `json:"effective"`
	InheritedFrom string        `json:"inheritedFrom,omitempty"`
}

AccessStatus reports what an object declares against what is enforced.

type Artifact

type Artifact struct {
	UUID        string   `json:"uuid"`
	Version     int      `json:"version,omitempty"`
	Name        string   `json:"name"`
	Type        string   `json:"type,omitempty"`
	CreatedDate string   `json:"createdDate,omitempty"`
	Formats     []Format `json:"formats,omitempty"`
}

Artifact is one published document.

type Checksum

type Checksum struct {
	AlgType  string `json:"algType"`
	AlgValue string `json:"algValue"`
}

Checksum is one digest over an artifact format's bytes.

type Client

type Client struct {
	BaseURL string
	Creds   *auth.Credentials
	HTTP    *http.Client
}

Client talks to one TEA server.

func New

func New(baseURL string, creds *auth.Credentials) *Client

New builds a client. Credentials may be nil: the consumption operations of a public catalogue need none, and requiring them would make the CLI useless for reading somebody else's open transparency log.

func (*Client) AccessPolicy

func (c *Client) AccessPolicy(ctx context.Context, uuid string) (*AccessStatus, error)

AccessPolicy reads the policy in force for an object.

func (*Client) AttachSignature

func (c *Client) AttachSignature(ctx context.Context, artifactUUID string, formatIndex int, sig map[string]any) error

AttachSignature records a detached signature and, crucially, the scheme it was produced with. The consumption API exposes only a URL, so a consumer without the scheme has to guess how to verify — which in practice means not verifying.

func (*Client) ComponentRelease added in v3.82.0

func (c *Client) ComponentRelease(ctx context.Context, uuid string) (*ComponentReleaseDetail, error)

ComponentRelease reads a component release with its distributions. The consumption API only carries distributions on the object read, never on a list page, so this is the one call that answers "where do I get it".

func (*Client) CreateArtifact

func (c *Client) CreateArtifact(ctx context.Context, collectionUUID, name, artifactType string, formats []Format, idempotencyKey string) (*Artifact, error)

CreateArtifact registers an artifact within a collection. It does not carry the bytes: those go up per format afterwards, so correcting a metadata field does not mean resending a multi-megabyte document.

func (*Client) CreateComponent added in v3.82.0

func (c *Client) CreateComponent(ctx context.Context, name string, identifiers []Identifier, idempotencyKey string) (*Product, error)

CreateComponent registers a component.

A product is what somebody buys; a component is a thing that ships. They are separate objects in TEA because a release of one is not a release of the other, and only a component release can carry distributions.

func (*Client) CreateComponentRelease added in v3.82.0

func (c *Client) CreateComponentRelease(ctx context.Context, componentUUID, version, releaseDate string, preRelease bool, idempotencyKey string) (*Release, error)

CreateComponentRelease publishes a release of a component.

func (*Client) CreateDistribution added in v3.82.0

func (c *Client) CreateDistribution(ctx context.Context, componentReleaseUUID string, d Distribution, idempotencyKey string) (*Distribution, error)

CreateDistribution declares one delivered form of a component release.

Idempotent on the download URL, or on the description where a channel has no single file to fetch — so republishing a release updates its download links rather than listing each of them twice.

func (*Client) CreateProduct

func (c *Client) CreateProduct(ctx context.Context, name string, identifiers []Identifier, idempotencyKey string) (*Product, error)

CreateProduct registers a product. The server assigns the UUID and the TEI — a TEI names the server's authority over the object, which a publisher cannot assert on its behalf.

func (*Client) CreateProductRelease

func (c *Client) CreateProductRelease(ctx context.Context, productUUID, version, releaseDate string, preRelease bool, idempotencyKey string) (*Release, error)

CreateProductRelease publishes a release of a product.

func (*Client) DeleteDistribution added in v3.82.0

func (c *Client) DeleteDistribution(ctx context.Context, uuid string) error

DeleteDistribution withdraws a download link. It does not make anything already downloaded go away.

func (*Client) DeleteObject added in v3.83.0

func (c *Client) DeleteObject(ctx context.Context, segment, uuid string) error

DeleteObject removes any published object by its kind's path segment.

Deleting a product does not reach the component published alongside it: they are separate objects with separate lineages, which is what lets a component outlive the product that first shipped it. Withdrawing everything therefore means naming both, and a CLI that could create a component but never remove one would leave that impossible from the terminal.

func (*Client) DeleteProduct

func (c *Client) DeleteProduct(ctx context.Context, uuid string) error

DeleteProduct removes a product and everything beneath it.

func (*Client) Do

func (c *Client) Do(ctx context.Context, method, path string, body any, out any, headers map[string]string) error

Do performs one request and decodes the result into out.

headers are applied last so a caller can set Idempotency-Key and Content-Digest, which are the two the publication API acts on and which a generic client would otherwise have no way to send.

func (*Client) LatestCollection

func (c *Client) LatestCollection(ctx context.Context, releaseUUID string) (*Collection, error)

LatestCollection reads the current collection for a product release.

func (*Client) Product

func (c *Client) Product(ctx context.Context, uuid string) (*Product, error)

Product reads one product.

func (*Client) ProductReleases

func (c *Client) ProductReleases(ctx context.Context, uuid string) (*Page[Release], error)

ProductReleases lists a product's releases.

func (*Client) Products

func (c *Client) Products(ctx context.Context, pageSize int, pageToken string) (*Page[Product], error)

Products lists products.

func (*Client) Publications

func (c *Client) Publications(ctx context.Context) ([]map[string]any, error)

Publications lists what this organisation has published, with the visibility of each — the half a publisher cannot see through the consumption API, because that API will not show anyone anything private.

func (*Client) PublishCollection

func (c *Client) PublishCollection(ctx context.Context, releaseUUID string, reason UpdateReason) (*Collection, error)

PublishCollection publishes the next version of a release's collection.

func (*Client) ResolveTEI

func (c *Client) ResolveTEI(ctx context.Context, tei string) (map[string]any, error)

ResolveTEI asks the server to resolve an identifier to the object it names.

func (*Client) SetAccessPolicy

func (c *Client) SetAccessPolicy(ctx context.Context, uuid string, policy AccessPolicy) (*AccessStatus, error)

SetAccessPolicy replaces an object's own policy.

Going public requires the server's confirmation parameter. That is not ceremony: no later request undoes it, because whatever was fetched while an object was public stays fetched.

func (*Client) UploadArtifactFile

func (c *Client) UploadArtifactFile(ctx context.Context, artifactUUID string, formatIndex int, path, mediaType string) (map[string]any, error)

UploadArtifactFile sends one format's bytes with the digest the server verifies against them.

type Collection

type Collection struct {
	UUID         string        `json:"uuid"`
	Version      int           `json:"version"`
	Date         string        `json:"date,omitempty"`
	BelongsTo    string        `json:"belongsTo,omitempty"`
	UpdateReason *UpdateReason `json:"updateReason,omitempty"`
	Artifacts    []Artifact    `json:"artifacts,omitempty"`
}

Collection is a versioned set of artifacts for one release.

type ComponentReleaseDetail added in v3.82.0

type ComponentReleaseDetail struct {
	Release struct {
		Release
		Distributions []Distribution `json:"distributions,omitempty"`
	} `json:"release"`
	LatestCollection Collection `json:"latestCollection"`
}

ComponentReleaseDetail is what GET /componentRelease/{uuid} returns: the release and the collection currently published for it.

type Distribution added in v3.82.0

type Distribution struct {
	DistributionID string       `json:"distributionId,omitempty"`
	Description    string       `json:"description,omitempty"`
	Identifiers    []Identifier `json:"identifiers,omitempty"`
	URL            string       `json:"url,omitempty"`
	SignatureURL   string       `json:"signatureUrl,omitempty"`
	Checksums      []Checksum   `json:"checksums,omitempty"`
}

Distribution is one delivered form of a component release — `release-distribution` in the consumption schema.

Not to be confused with an artifact. An artifact is evidence ABOUT a release: an SBOM, a VEX document, an attestation. A distribution is the release as somebody installs it: a binary on a releases page, a Homebrew formula, a Scoop manifest. A consumer asking "how do I get 3.81.0 on macOS" wants the second, and an SBOM is not an answer to it.

type Error

type Error struct {
	Status  int    `json:"-"`
	Code    string `json:"error"`
	Message string `json:"message,omitempty"`
	Field   string `json:"field,omitempty"`
}

Error is a TEA error response, in either the consumption or publication envelope. Both carry a machine-readable code; only the publication one carries a message and field.

func (*Error) Error

func (e *Error) Error() string

func (*Error) IsNotFound

func (e *Error) IsNotFound() bool

IsNotFound reports whether the server said the object does not exist, under either envelope's spelling.

type Format

type Format struct {
	MediaType    string     `json:"mediaType"`
	Description  string     `json:"description,omitempty"`
	URL          string     `json:"url,omitempty"`
	SignatureURL string     `json:"signatureUrl,omitempty"`
	Checksums    []Checksum `json:"checksums,omitempty"`
}

Format is one representation of an artifact.

type Identifier

type Identifier struct {
	IDType  string `json:"idType"`
	IDValue string `json:"idValue"`
}

Identifier is a TEA identifier: a type and its value.

type Page

type Page[T any] struct {
	Results       []T    `json:"results"`
	HasNext       bool   `json:"hasNext"`
	NextPageToken string `json:"nextPageToken"`
}

Page is the consumption API's pagination envelope.

type Product

type Product struct {
	UUID        string       `json:"uuid"`
	Name        string       `json:"name"`
	Identifiers []Identifier `json:"identifiers,omitempty"`
}

Product is a lineage of releases.

type Release

type Release struct {
	UUID          string       `json:"uuid"`
	Product       string       `json:"product,omitempty"`
	ProductName   string       `json:"productName,omitempty"`
	Component     string       `json:"component,omitempty"`
	ComponentName string       `json:"componentName,omitempty"`
	Version       string       `json:"version"`
	CreatedDate   string       `json:"createdDate,omitempty"`
	ReleaseDate   string       `json:"releaseDate,omitempty"`
	PreRelease    bool         `json:"preRelease,omitempty"`
	Identifiers   []Identifier `json:"identifiers,omitempty"`
}

Release covers both flavours. The consumption API distinguishes a product release from a component release; the fields that differ are the parent and its name, so one struct carries both rather than duplicating the rest.

type ShareGrant

type ShareGrant struct {
	OrganizationUUID string `json:"organizationUuid"`
	OrganizationName string `json:"organizationName,omitempty"`
	ExpiresAt        int64  `json:"expiresAt,omitempty"`
}

ShareGrant entitles one organisation to read one object.

type UpdateReason

type UpdateReason struct {
	Type    string `json:"type"`
	Comment string `json:"comment,omitempty"`
}

UpdateReason says why a collection changed. Required on publication: a collection whose contents change without saying why is indistinguishable, to a consumer, from one that was tampered with.

type WellKnown

type WellKnown struct {
	SchemaVersion int `json:"schemaVersion"`
	Endpoints     []struct {
		URL      string   `json:"url"`
		Versions []string `json:"versions"`
		Priority *float64 `json:"priority,omitempty"`
	} `json:"endpoints"`
}

WellKnown is the discovery document at /.well-known/tea.

func Discover

func Discover(ctx context.Context, domainOrTEI string) (*WellKnown, string, error)

Discover resolves a domain to a TEA root, following the specification: query DNS for the domain, fetch /.well-known/tea over HTTPS, then pick an endpoint and append its version.

The domain may be given as a bare name, a URL, or a TEI — those are the three forms a person actually has to hand, and requiring them to convert between them would be busywork.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL