tmproto

package
v0.0.0-...-e7684d6 Latest Latest
Warning

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

Go to latest
Published: May 15, 2026 License: Apache-2.0 Imports: 29 Imported by: 0

Documentation

Overview

Package tmproto's signing.go implements the TMP request authentication envelope from docs/trusted-match/specification.mdx §"Request Authentication": Ed25519 signatures carried in X-AdCP-Signature / X-AdCP-Key-Id headers, per-provider binding via provider_endpoint_url, daily-epoch replay window.

Context match signs the newline-joined string:

type | property_rid | placement_id | sorted-comma-joined package_ids | provider_endpoint_url | daily_epoch

Identity match signs hex(SHA-256(JCS(canonical_object))) where the canonical object holds {type, request_id, seller_agent_url, identities_hash, consent, package_ids, provider_endpoint_url, daily_epoch}. JCS protects identity inputs against delimiter injection from arbitrary-byte fields like consent.gpp.

Package tmproto's tmpx.go implements TMPX exposure-token encoding per the TMP spec §"TMPX Exposure Tokens".

TMPX is an HPKE-encrypted opaque token that flows from the identity-match read replica → router → publisher → buyer's impression pixel. Only the buyer's cluster master holds the recipient private key. The wire format is `<kid>.<base64url_no_pad(enc || ciphertext_with_tag)>` and the cipher suite is fixed by the spec:

  • KEM: DHKEM(X25519, HKDF-SHA256) — RFC 9180 0x0020
  • KDF: HKDF-SHA256 — RFC 9180 0x0001
  • AEAD: ChaCha20-Poly1305 — RFC 9180 0x0003
  • Mode: mode_base (no PSK, no auth)

HPKE is implemented in this package with stdlib + chacha20poly1305 to keep adcp-go's dependency footprint minimal — protocol-layer code shouldn't pull in an HPKE framework for one cipher suite.

Package tmproto defines the Trusted Match Protocol message types.

TMP separates ad decisioning into two independent operations: Context Match (what content is this?) and Identity Match (is this user eligible?). The two operations never share data. The publisher joins them locally.

Index

Constants

View Source
const (
	HeaderTMPSignature = "X-AdCP-Signature"
	HeaderTMPKeyID     = "X-AdCP-Key-Id"
)

HTTP headers carrying the TMP signature envelope.

View Source
const (
	MaxPackagesPerRequest     = 500
	MaxArtifactRefsPerRequest = 20
	// MaxIdentitiesPerRequest mirrors the TMP schema's maxItems on identities
	// — matches the TMPX plaintext budget (~120 bytes after HPKE overhead).
	MaxIdentitiesPerRequest = 3
)

Maximum sizes for request arrays to prevent denial-of-service.

View Source
const (
	// ContextSignals.
	MaxTopics          = 50
	MaxKeywords        = 50
	MaxKeywordLength   = 100
	MaxContentPolicies = 20
	MaxSummaryLength   = 500
	MinEmbeddingDims   = 64
	MaxEmbeddingDims   = 2048

	// ArtifactRef.
	MaxArtifactRefs = 20

	// Artifact.
	MaxTextContentLength = 100000
	MaxTranscriptLength  = 200000
	MinHeadingLevel      = 1
	MaxHeadingLevel      = 6
)

Schema-driven limits used by the Validate methods below. These mirror the JSON Schema constraints in adcp/schemas/tmp/context-match-request.json and adcp/schemas/content-standards/artifact.json. Named constants so callers can mirror them in their own pre-checks.

View Source
const JWKSAlgEncryptionDHKEMX25519 = "HPKE-DHKEM-X25519-HKDF-SHA256"

JWKSAlgEncryptionDHKEMX25519 is the JWK `alg` value buyers publish for the TMPX HPKE recipient public key under the suite the spec fixes today.

View Source
const MaxAssets = 200

MaxAssets bounds the number of elements decoded from an Artifact's assets array. Matches the schema's maxItems: 200. Exposed so callers can mirror it in their HTTP body-size limits if they want symmetric defense.

View Source
const MaxIDLength = 256

MaxIDLength caps identifier fields to prevent oversized store keys.

View Source
const MaxSnapshotBytes = 1 * 1024 * 1024

MaxSnapshotBytes caps the registry snapshot the keystore will ingest. Sized for property catalogs in the thousands of entries; the spec caps individual property records at a few hundred bytes.

View Source
const TmpxFormatVersion uint8 = 0x01

TmpxFormatVersion is the TMPX binary plaintext format version per spec.

View Source
const TmpxHPKEOverheadBytes = 48

TmpxHPKEOverheadBytes is the post-seal HPKE overhead added on top of the plaintext: 32 bytes of encapsulated KEM key + 16 bytes of AEAD auth tag.

View Source
const TmpxHeaderBytes = 16

TmpxHeaderBytes is the size of the binary plaintext header (version, timestamp, country, nonce, count).

View Source
const TmpxMaxKidLen = 8

TmpxMaxKidLen is the spec-defined cap on the kid string prefixed to every TMPX wire token. Senders that size payloads against the wire budget should reserve this many bytes even when the currently advertised kid is shorter — JWKS rotations can change the kid length between seals.

View Source
const TmpxMaxWireBytes = 255

TmpxMaxWireBytes is the maximum size of a TMPX wire string after base64url encoding. 255 bytes is the GAM macro substitution limit — tokens above it cannot be inlined into creative tracking URLs without truncation.

Variables

View Source
var (
	ErrSignatureMissing    = errors.New("tmproto: signature headers missing")
	ErrSignatureMalformed  = errors.New("tmproto: signature header malformed")
	ErrSignatureKeyUnknown = errors.New("tmproto: signing key not in keystore")
	ErrSignatureKeyRevoked = errors.New("tmproto: signing key revoked")
	ErrSignatureInvalid    = errors.New("tmproto: ed25519 verification failed")
)

Sentinel errors returned by Verify*. Use errors.Is to discriminate.

Functions

func BuildContextMatchSigningInput

func BuildContextMatchSigningInput(req *ContextMatchRequest, providerEndpointURL string, epoch int64) []byte

BuildContextMatchSigningInput returns the bytes the signer feeds to Ed25519 for context match: newline-joined fields per the spec.

func BuildIdentityMatchSigningInput

func BuildIdentityMatchSigningInput(req *IdentityMatchRequest, providerEndpointURL string, epoch int64) ([]byte, error)

BuildIdentityMatchSigningInput returns the bytes the signer feeds to Ed25519 for identity match: hex(SHA-256(JCS(canonical_object))).

func CanonicalizeURL

func CanonicalizeURL(raw string) string

CanonicalizeURL normalizes a URL for consistent hashing:

  • Strip scheme (http://, https://)
  • Strip www., m., amp. prefixes from hostname
  • Lowercase everything
  • Strip trailing slash
  • Strip query params and fragments

func CurrentEpoch

func CurrentEpoch() int64

CurrentEpoch returns floor(unix_timestamp / 86400). Signatures bind to this value; verifiers accept current and previous epoch.

func EncodeTmpxPlaintext

func EncodeTmpxPlaintext(country string, entries []TmpxEntry, ts time.Time) ([]byte, error)

EncodeTmpxPlaintext builds the binary plaintext per spec §"Binary format": 16-byte header (version, ts, country, nonce, count) followed by entries. Country is exactly 2 ASCII bytes (ISO 3166-1 alpha-2). The nonce is randomly drawn — replay deduplication at the master uses it.

func EpochAt

func EpochAt(t time.Time) int64

EpochAt returns the daily epoch for a given timestamp.

func ExtractSignatureHeaders

func ExtractSignatureHeaders(h http.Header) (sig, kid string, err error)

ExtractSignatureHeaders pulls the X-AdCP-Signature and X-AdCP-Key-Id values from a header map. Empty values map to ErrSignatureMissing.

func HashCanonical

func HashCanonical(canonical string) uint64

HashCanonical returns a uint64 FNV-1a hash of an already-canonicalized string.

func HashURL

func HashURL(raw string) uint64

HashURL returns a uint64 FNV-1a hash of the canonicalized URL.

func LoadEd25519PrivateKeyPEM

func LoadEd25519PrivateKeyPEM(pemBytes []byte) (ed25519.PrivateKey, error)

LoadEd25519PrivateKeyPEM parses a PKCS#8-encoded Ed25519 private key from PEM bytes. Used by cmd/router to load the signing key configured on disk.

func LoadX25519PublicKey

func LoadX25519PublicKey(b []byte) (*ecdh.PublicKey, error)

LoadX25519PublicKey parses 32 raw bytes into an ecdh.PublicKey. Used by reference agents that load buyer-published TMPX recipient keys from disk.

func MarshalJSON

func MarshalJSON(v any) ([]byte, error)

MarshalJSON marshals any TMP message type to JSON.

func NormalizeProviderEndpointURL

func NormalizeProviderEndpointURL(s string) string

NormalizeProviderEndpointURL returns the canonical form used in signing. The spec mandates exact string match with the provider's registered endpoint and forbids trailing slashes — we strip them so callers don't have to.

func SealTmpx

func SealTmpx(recipient TmpxRecipient, info, plaintext []byte) (string, error)

SealTmpx HPKE-encrypts plaintext under recipient's X25519 public key and returns the wire-format string `kid.b64url(enc||ct)` per spec.

info is bound into the HPKE key schedule and is left empty in the spec — callers should pass nil unless the buyer profile defines a value.

func TmpxTokenSize

func TmpxTokenSize(typeID TmpxTypeID) (int, bool)

TmpxTokenSize returns the spec-defined binary size for a Type ID. Returns (0, false) when typeID is unknown — parsers MUST stop on unknown IDs and treat the remaining entries as absent.

func TmpxWireSize

func TmpxWireSize(kidLen, entriesBytes int) int

TmpxWireSize returns the wire-string length a TMPX token will have after HPKE sealing and base64url encoding, given a recipient kid of length kidLen and entriesBytes worth of plaintext entry payload (sum of 1 + tokenSize over the entries). Callers use this to keep emitted tokens under TmpxMaxWireBytes before paying for the seal.

func UnmarshalJSON

func UnmarshalJSON(data []byte, v any) error

UnmarshalJSON unmarshals JSON into any TMP message type.

func ValidateContextRequest

func ValidateContextRequest(req *ContextMatchRequest) error

ValidateContextRequest checks that required fields are present on a context match request.

func ValidateIdentityRequest

func ValidateIdentityRequest(req *IdentityMatchRequest) error

ValidateIdentityRequest checks that required fields are present on an identity match request.

func VerifyContextMatch

func VerifyContextMatch(req *ContextMatchRequest, ownEndpointURL, sig, kid string, ks KeyStore, now time.Time) error

VerifyContextMatch verifies the signature on a context-match request using the verifier's own registered endpoint URL. now should be the wall clock for the request — current+previous epoch are accepted.

func VerifyContextMatchHandler

func VerifyContextMatchHandler(next http.Handler, opts VerifyOptions) http.Handler

VerifyContextMatchHandler wraps an HTTP handler with TMP context-match signature verification. The handler is invoked with the original request body re-attached so it can decode normally; the parsed request is also exposed via VerifiedContextMatchFromContext.

func VerifyIdentityMatch

func VerifyIdentityMatch(req *IdentityMatchRequest, ownEndpointURL, sig, kid string, ks KeyStore, now time.Time) error

VerifyIdentityMatch verifies the signature on an identity-match request.

func VerifyIdentityMatchHandler

func VerifyIdentityMatchHandler(next http.Handler, opts VerifyOptions) http.Handler

VerifyIdentityMatchHandler wraps an HTTP handler with TMP identity-match signature verification.

Types

type Artifact

type Artifact struct {
	PropertyRID    string `json:"property_rid"`
	ArtifactID     string `json:"artifact_id"`
	VariantID      string `json:"variant_id,omitempty"`
	URL            string `json:"url,omitempty"`
	PublishedTime  string `json:"published_time,omitempty"`   // ISO 8601
	LastUpdateTime string `json:"last_update_time,omitempty"` // ISO 8601
	Assets         Assets `json:"assets"`

	// FormatID optionally references a format definition from the format registry.
	// Shape: /schemas/latest/core/format-id.json. Left as json.RawMessage until
	// the core types package is introduced.
	FormatID json.RawMessage `json:"format_id,omitempty"`

	// Provenance declares how the content was produced (AI involvement,
	// declaring party, etc.). Shape: /schemas/latest/core/provenance.json.
	// Left as json.RawMessage until the core types package is introduced.
	Provenance json.RawMessage `json:"provenance,omitempty"`

	// Metadata carries rich extracted metadata. Known keys: canonical, author,
	// keywords, open_graph, twitter_card, json_ld. Schema allows additional
	// properties — stored as an untyped map for lossless round-trip.
	Metadata map[string]any `json:"metadata,omitempty"`

	// Identifiers carries platform-specific handles. Known keys:
	// apple_podcast_id, spotify_collection_id, podcast_guid, youtube_video_id,
	// rss_url. Schema allows additional properties — untyped for lossless round-trip.
	Identifiers map[string]any `json:"identifiers,omitempty"`
}

Artifact is content adjacent to an ad placement (article, podcast segment, video chapter, social post). An Artifact is a collection of assets plus metadata and signals. Used in ContextMatchRequest as the highest-disclosure rung of the TMP content ladder.

Publishers MUST NOT include asset access credentials the buyer could use outside this request flow — for secured assets, use signed URLs with short expiry. Routers MUST call StripAccess before forwarding to multiple buyers.

func (*Artifact) StripAccess

func (a *Artifact) StripAccess()

StripAccess zeros the Access field on every asset in the artifact. Routers MUST call this (or equivalent) before fanning out a ContextMatchRequest to multiple buyer agents, per the AdCP spec — otherwise credentials leak to every buyer.

Safe to call on an Artifact whose assets have no Access set; it's a no-op.

func (*Artifact) Validate

func (a *Artifact) Validate() error

Validate checks Artifact-level constraints and recurses into each asset. Unknown assets (from forward-compat passthrough) are skipped.

type ArtifactRef

type ArtifactRef struct {
	Type  ArtifactRefType `json:"type"`
	Value string          `json:"value"`
}

ArtifactRef is a public identifier for content adjacent to an ad opportunity. Each ref identifies content via a public scheme the buyer can resolve without private registry sync. Both fields are required by the schema.

func (*ArtifactRef) Validate

func (r *ArtifactRef) Validate() error

Validate checks field-level constraints on an ArtifactRef.

type ArtifactRefType

type ArtifactRefType string

ArtifactRefType identifies the kind of public content identifier carried in an ArtifactRef. The rung on the disclosure ladder between full-content (Artifact) and classifier-only (ContextSignals): the publisher shares a public handle the buyer can resolve independently.

const (
	// ArtifactRefTypeURL is the canonical content URL. MUST NOT contain
	// user-specific path segments, query parameters, or fragments.
	ArtifactRefTypeURL ArtifactRefType = "url"
	// ArtifactRefTypeURLHash is a Blake3 hash of the canonicalized URL,
	// base64-encoded. Canonicalization: strip scheme, strip www./m./amp.
	// prefixes, lowercase, strip trailing slash, strip query + fragment.
	ArtifactRefTypeURLHash   ArtifactRefType = "url_hash"
	ArtifactRefTypeEIDR      ArtifactRefType = "eidr"      // EIDR DOI (film/TV)
	ArtifactRefTypeGracenote ArtifactRefType = "gracenote" // Gracenote TMS ID
	ArtifactRefTypeISRC      ArtifactRefType = "isrc"      // music recordings
	ArtifactRefTypeGTIN      ArtifactRefType = "gtin"      // UPC/EAN/ISBN-13
	ArtifactRefTypeRSSGUID   ArtifactRefType = "rss_guid"  // podcast episode GUID
	ArtifactRefTypeISBN      ArtifactRefType = "isbn"
	ArtifactRefTypeCustom    ArtifactRefType = "custom"
)

type Asset

type Asset interface {
	AssetTag() AssetType
}

Asset is one element of Artifact.Assets. Concrete implementations: *TextAsset, *ImageAsset, *VideoAsset, *AudioAsset, *UnknownAsset. The discriminator on the wire comes from the implementer's AssetTag method, never from a user-settable field.

type AssetAccess

type AssetAccess struct {
	Method AssetAccessMethod `json:"-"`

	// Token is emitted only when Method == bearer_token.
	Token string `json:"-"`

	// Provider and Credentials are emitted only when Method == service_account.
	// Provider is "gcp" or "aws".
	Provider    string         `json:"-"`
	Credentials map[string]any `json:"-"`
}

AssetAccess carries authentication for accessing secured asset URLs.

Sensitive: Token and Credentials hold secrets. String() and GoString() are overridden to redact so standard %v/%+v logging cannot leak them. MarshalJSON emits only the fields that belong to the current Method, so a stray Token set on a signed_url variant cannot reach the wire.

Routers MUST strip this field (see Artifact.StripAccess) before fanning out a ContextMatchRequest to multiple buyer agents, per the AdCP spec.

Prefer the NewBearerTokenAccess / NewServiceAccountAccess / NewSignedURLAccess constructors over literal struct construction.

func NewBearerTokenAccess

func NewBearerTokenAccess(token string) AssetAccess

NewBearerTokenAccess constructs an AssetAccess for a bearer token.

func NewServiceAccountAccess

func NewServiceAccountAccess(provider string, credentials map[string]any) AssetAccess

NewServiceAccountAccess constructs an AssetAccess for a cloud service account. Provider is "gcp" or "aws"; credentials shape is provider-specific.

func NewSignedURLAccess

func NewSignedURLAccess() AssetAccess

NewSignedURLAccess constructs an AssetAccess for a signed URL — credentials are embedded in the URL itself, so no additional fields are carried.

func (AssetAccess) GoString

func (a AssetAccess) GoString() string

GoString returns a redacted form. %+v / %#v use this path too.

func (AssetAccess) MarshalJSON

func (a AssetAccess) MarshalJSON() ([]byte, error)

MarshalJSON emits only the fields that belong to the current Method. Fields set on the wrong variant (e.g., Token when Method=signed_url) are silently dropped — preferable to leaking credentials onto the wire.

func (AssetAccess) String

func (a AssetAccess) String() string

String returns a redacted form. Tokens and credentials are never included so %v / %s logging cannot leak secrets.

func (*AssetAccess) UnmarshalJSON

func (a *AssetAccess) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes the method and only the fields appropriate for it. Fields belonging to other variants are ignored even if present on the wire.

func (*AssetAccess) Validate

func (a *AssetAccess) Validate() error

Validate checks field-level and cross-field constraints on an AssetAccess. Returns an error if the method is unknown, missing, or if required fields for the method are absent.

type AssetAccessMethod

type AssetAccessMethod string

AssetAccessMethod identifies the credential scheme for a secured asset URL.

const (
	AssetAccessMethodBearerToken    AssetAccessMethod = "bearer_token"
	AssetAccessMethodServiceAccount AssetAccessMethod = "service_account"
	AssetAccessMethodSignedURL      AssetAccessMethod = "signed_url"
)

type AssetType

type AssetType string

AssetType discriminates entries in Artifact.Assets.

const (
	AssetTypeText  AssetType = "text"
	AssetTypeImage AssetType = "image"
	AssetTypeVideo AssetType = "video"
	AssetTypeAudio AssetType = "audio"
)

type Assets

type Assets []Asset

Assets is a discriminated-union slice backing Artifact.Assets. Marshaling is standard (each concrete type writes its own "type" field); unmarshaling dispatches on the "type" discriminator and falls back to UnknownAsset for forward compatibility.

func (*Assets) UnmarshalJSON

func (a *Assets) UnmarshalJSON(data []byte) error

UnmarshalJSON decodes each element by reading the "type" discriminator and routing to the matching concrete asset struct. Enforces MaxAssets to bound allocation. Returns an error on missing discriminators — passthrough would mask malformed payloads. Unknown discriminators fall back to UnknownAsset so forward-compatible payloads round-trip cleanly.

type AudioAsset

type AudioAsset struct {
	URL              string          `json:"url"`
	Access           *AssetAccess    `json:"access,omitempty"`
	DurationMs       int             `json:"duration_ms,omitempty"`
	Transcript       string          `json:"transcript,omitempty"`
	TranscriptFormat string          `json:"transcript_format,omitempty"` // text/plain (default), text/markdown, application/json
	TranscriptSource string          `json:"transcript_source,omitempty"` // original_script, closed_captions, generated
	Provenance       json.RawMessage `json:"provenance,omitempty"`
}

AudioAsset is an audio asset with its URL and optional transcript/metadata. Transcript is publisher-supplied and MUST be treated as untrusted input.

func (*AudioAsset) AssetTag

func (*AudioAsset) AssetTag() AssetType

AssetTag implements Asset.

func (*AudioAsset) MarshalJSON

func (a *AudioAsset) MarshalJSON() ([]byte, error)

MarshalJSON writes the "type" discriminator from AssetTag.

func (*AudioAsset) Validate

func (a *AudioAsset) Validate() error

Validate checks AudioAsset field-level constraints.

type AvailablePackage

type AvailablePackage struct {
	PackageID   string            `json:"package_id"`           // Unique identifier for the package
	MediaBuyID  string            `json:"media_buy_id"`         // Media buy that this package belongs to
	SellerAgent json.RawMessage   `json:"seller_agent"`         // The seller agent that owns this package. `agent_url` MUST match one of `authorized_agents[].url` in the publisher's adagents.json authoritative for every property this package may serve. Providers SHOULD validate at sync time and reject mismatches with `seller_not_authorized`. Cached alongside the package and used for offer attribution, per-seller observability, and dispute resolution — not for request-time filtering.
	FormatIDs   []json.RawMessage `json:"format_ids,omitempty"` // Creative format identifiers eligible for this package. Uses the standard AdCP format-id object with agent_url and id for unambiguous format resolution across namespaces.
	Catalogs    []json.RawMessage `json:"catalogs,omitempty"`   // The buyer's catalogs attached to this package, with selectors (ids, gtins, tags, category, query) scoping which items are in play. References synced catalogs by catalog_id. The provider resolves items from its cached copy.
}

A package available for contextual matching. Synced to providers at media buy creation time — not sent per request. Providers cache this metadata and use it when evaluating context match requests for the placement. The `seller_agent` field binds the package to the originating seller agent; providers MUST use this binding rather than re-deriving seller identity from media_buy_id.

type ContextMatchRequest

type ContextMatchRequest struct {
	AdcpMajorVersion int             `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against their supported major_versions and return VERSION_UNSUPPORTED if unsupported. When omitted, the seller assumes its highest supported version.
	Type             string          `json:"type"`                         // Message type discriminator for deserialization.
	ProtocolVersion  string          `json:"protocol_version,omitempty"`   // TMP protocol version. Allows receivers to handle semantic differences across versions.
	RequestID        string          `json:"request_id"`                   // Unique request identifier. MUST NOT correlate with any identity match request_id.
	PropertyRID      string          `json:"property_rid"`                 // Property catalog UUID (UUID v7). Globally unique, stable identifier assigned by the property catalog. The primary key for TMP matching and property list targeting.
	PropertyID       string          `json:"property_id,omitempty"`        // Publisher's human-readable property slug (e.g., 'cnn_homepage'). Optional when property_rid is present. Useful for logging and debugging.
	PropertyType     PropertyType    `json:"property_type"`                // Type of the publisher property
	PlacementID      string          `json:"placement_id"`                 // Placement identifier from the publisher's placement registry in adagents.json. Identifies where on the property this ad opportunity exists. One placement per request.
	Artifact         *Artifact       `json:"artifact,omitempty"`           // Full content artifact adjacent to this ad opportunity. Same schema used for content standards evaluation. The publisher sends the artifact when they want the buyer to evaluate the full content. Contractual protections govern buyer use. TEE deployment upgrades contractual trust to cryptographic verification. Publishers MUST NOT include asset access credentials (bearer tokens, service accounts) — the router fans out to multiple buyer agents. For secured assets, use signed URLs with short expiry. Routers MUST strip access fields from artifacts before forwarding.
	ArtifactRefs     []ArtifactRef   `json:"artifact_refs,omitempty"`      // Public content references adjacent to this ad opportunity. Each artifact identifies content via a public identifier the buyer can resolve independently — no private registry sync required.
	Geo              map[string]any  `json:"geo,omitempty"`                // Coarse geographic location of the viewer. Publisher controls granularity — country is sufficient for regulatory compliance and volume filtering, region or metro helps with campaign targeting and valuation. Coarsened to prevent user identification: no postcode, no coordinates. All fields optional.
	ContextSignals   *ContextSignals `json:"context_signals,omitempty"`    // Pre-computed classifier outputs for the content environment. Use when the publisher wants to provide classified context without sharing content or public references. Can supplement artifact_refs (e.g., URL + pre-classified topics) or replace them entirely (e.g., ephemeral conversation turns). Raw content MUST NOT be included — only classified outputs. The publisher is the classifier boundary.
	PackageIDs       []string        `json:"package_ids,omitempty"`        // Restrict evaluation to specific packages. When omitted, the provider evaluates all eligible packages for this placement (the common case). MUST NOT vary by user — the same package_ids must be sent for every user on a given placement. User-dependent filtering leaks identity into the context path.
}

Sent by publisher to router or provider to evaluate packages against contextual signals. The provider uses its synced package set for the placement. MUST NOT contain user identity. The request_id MUST NOT correlate with any identity match request_id. Extension fields (ext, context) are intentionally omitted — extension data in the context path could inadvertently carry or correlate user identity signals.

type ContextMatchResponse

type ContextMatchResponse struct {
	Type      string         `json:"type"`                // Message type discriminator for deserialization.
	RequestID string         `json:"request_id"`          // Echoed request identifier from the context match request
	Offers    []Offer        `json:"offers"`              // Offers from the buyer, one per activated package. An empty array means no packages matched. For simple activation, each offer has just package_id. For richer responses, offers include brand, price, summary, and creative manifest.
	CacheTTL  int            `json:"cache_ttl,omitempty"` // Optional override for the default 5-minute cache TTL, in seconds. When present, the router MUST use this value instead of its default. Set to 0 to disable caching (e.g., when targeting configuration has just changed).
	Signals   map[string]any `json:"signals,omitempty"`   // Response-level targeting signals for ad server pass-through. In the GAM case, these carry the key-value pairs that trigger line items. Not per-offer — applies to the response as a whole.
}

Response from router or provider with offers for matched packages. An empty offers array means no packages matched. For simple GAM integration, package_id flows as a macro via signals. For rich integrations, the offer includes brand, price, summary, and optionally an inline creative manifest. Extension fields (ext, context) are intentionally omitted — extension data in the context path could inadvertently carry or correlate user identity signals.

type ContextProvider

type ContextProvider interface {
	ContextMatch(ctx context.Context, req *ContextMatchRequest) (*ContextMatchResponse, error)
}

ContextProvider evaluates packages against content context.

type ContextSignals

type ContextSignals struct {
	// Topics carries content topic identifiers. With IAB Content Taxonomy 3.0
	// (TaxonomyID=7) these are numeric IDs as strings (e.g., "632" for Food &
	// Drink). For custom taxonomies, use human-readable strings.
	Topics []string `json:"topics,omitempty"`

	// TaxonomySource is the organization that defines the topic taxonomy.
	// "iab" for IAB Content Taxonomy; publishers may use other values.
	TaxonomySource string `json:"taxonomy_source,omitempty"`

	// TaxonomyID is the taxonomy version within the source. For IAB, follows
	// the AdCOM cattax enum: 7 = Content Taxonomy 3.0.
	TaxonomyID int `json:"taxonomy_id,omitempty"`

	// Sentiment is the content sentiment classification. One of
	// "positive", "negative", "neutral", "mixed".
	Sentiment string `json:"sentiment,omitempty"`

	// Keywords are content keywords extracted by the publisher's classifier.
	Keywords []string `json:"keywords,omitempty"`

	// Language is the content language in ISO 639-1 format (e.g., "en", "ja").
	Language string `json:"language,omitempty"`

	// ContentPolicies are policy IDs from the AdCP policy registry that this
	// content satisfies (e.g., "csbs" for Common Sense Brand Standards).
	// An empty slice means no policies have been evaluated.
	ContentPolicies []string `json:"content_policies,omitempty"`

	// Summary is a publisher-generated natural-language summary of the content
	// for relevance judgment. Useful for LLM-native buyers. Untrusted input.
	Summary string `json:"summary,omitempty"`

	// Embedding is the content embedding as a base64-encoded int8 vector.
	// Captures semantic content beyond topics and keywords. EmbeddingModel and
	// EmbeddingDims MUST be set when Embedding is present.
	Embedding string `json:"embedding,omitempty"`

	// EmbeddingModel identifies the embedding model (e.g., "nomic-embed-text-v1.5").
	EmbeddingModel string `json:"embedding_model,omitempty"`

	// EmbeddingDims is the number of dimensions in the embedding vector.
	EmbeddingDims int `json:"embedding_dims,omitempty"`
}

ContextSignals carries pre-computed classifier outputs for the content environment. It is the baseline content disclosure level of TMP: the publisher runs classifiers locally and shares only the outputs, never raw content. Can supplement artifact_refs or replace them entirely (e.g., ephemeral conversation turns that have no public URL).

Buyers MUST treat Summary as untrusted publisher-generated content.

func (*ContextSignals) Validate

func (c *ContextSignals) Validate() error

Validate checks field-level and cross-field schema constraints on a ContextSignals. Returns the first violation found. Callers running an untrusted payload through the SDK SHOULD call Validate before using it — schema-invalid data is not rejected at unmarshal time.

type ErrorCode

type ErrorCode string

Machine-readable TMP error code.

const (
	ErrorCodeInvalidRequest      ErrorCode = "invalid_request"
	ErrorCodeUnknownPackage      ErrorCode = "unknown_package"
	ErrorCodeRateLimited         ErrorCode = "rate_limited"
	ErrorCodeTimeout             ErrorCode = "timeout"
	ErrorCodeInternalError       ErrorCode = "internal_error"
	ErrorCodeProviderUnavailable ErrorCode = "provider_unavailable"
)

type ErrorResponse

type ErrorResponse struct {
	Type      string    `json:"type"`              // Message type discriminator for deserialization.
	RequestID string    `json:"request_id"`        // Echoed request identifier from the original request
	Code      ErrorCode `json:"code"`              // Machine-readable error code. `seller_not_authorized` is returned by providers at sync time when an AvailablePackage declares a `seller_agent.agent_url` that is not present in the `authorized_agents` list of the publisher's adagents.json for a property the package claims to serve.
	Message   string    `json:"message,omitempty"` // Human-readable error description for debugging
}

Error response from a TMP provider or router. Returned instead of a normal response when the provider cannot process the request. Distinguishes errors from empty results (an empty offers array is a valid response meaning no matches, not an error).

type IdentityMatchRequest

type IdentityMatchRequest struct {
	AdcpMajorVersion int             `json:"adcp_major_version,omitempty"` // The AdCP major version the buyer's payloads conform to. Sellers validate against their supported major_versions and return VERSION_UNSUPPORTED if unsupported. When omitted, the seller assumes its highest supported version.
	Type             string          `json:"type"`                         // Message type discriminator for deserialization.
	ProtocolVersion  string          `json:"protocol_version,omitempty"`   // TMP protocol version. Allows receivers to handle semantic differences across versions.
	RequestID        string          `json:"request_id"`                   // Unique request identifier. MUST NOT correlate with any context match request_id.
	SellerAgentURL   string          `json:"seller_agent_url"`             // API endpoint URL of the seller agent issuing this request. The buyer's identity-match service uses this to resolve the active package set it has registered for this seller; when `package_ids` is omitted, evaluation occurs against that full set. If `seller_agent_url` does not match any seller for which the buyer has registered active packages, the buyer MUST return an empty `eligible_package_ids` set — it MUST NOT fall back to evaluating against another seller's active set. Compared using the AdCP URL canonicalization rules, not byte-equality — see docs/reference/url-canonicalization. Consistent with `seller_agent.agent_url` on `AvailablePackage` and `agent_url` in `adagents.json`.
	Identities       []IdentityToken `json:"identities"`                   // Identity tokens for the user, each tagged with its type. Publishers SHOULD include every token they have available — the buyer resolves on whichever graph matches. Entry order is not semantically significant; buyers use their own preference order when multiple entries resolve. Duplicate `(uid_type, user_token)` pairs MUST NOT appear; routers MAY reject or dedupe. `maxItems: 3` matches the TMPX plaintext budget (~120 bytes after HPKE overhead fits three 32-byte tokens); exceeding it forces buyer-side truncation.
	Consent          map[string]any  `json:"consent,omitempty"`            // Privacy consent signals. Buyers in regulated jurisdictions MUST NOT process the user token without consent information.
	PackageIDs       []string        `json:"package_ids,omitempty"`        // Optional. When omitted, the buyer evaluates eligibility against the full set of active packages it has registered for `seller_agent_url`. When provided, the composition of `package_ids` MUST be statistically independent of the current placement — sending only the page-specific subset would let the buyer correlate Identity Match with Context Match by comparing package sets. Two acceptable modes: (a) **all-active** — include every active package this buyer has at this publisher; (b) **fuzzed** — include a random sample of active packages, optionally padded with synthetic non-existent IDs, drawn from a distribution that does not depend on the current placement. The buyer's silent-drop behavior on unknown IDs (specified below) is what makes synthetic-ID padding safe — they do not affect the response shape and cannot leak registry membership. When both `seller_agent_url` and `package_ids` are present, the buyer evaluates against the intersection of its registered active set and `package_ids`; IDs in `package_ids` that the buyer has not registered for this seller MUST be silently ignored (not surfaced as errors) to avoid leaking registry membership back to the publisher.
	Country          string          `json:"country,omitempty"`            // ISO 3166-1 alpha-2 country code. Routing directive for the TMP Router — used to select the correct regional provider. The router MUST strip this field before forwarding the request to the buyer agent. Not an identity signal.
}

Sent by publisher to evaluate user eligibility for packages using an opaque identity token. MUST NOT contain page context. The request_id MUST NOT correlate with any context match request_id. The buyer resolves the active package set for this seller from `seller_agent_url`; if `package_ids` is provided, its composition MUST be independent of the current placement (e.g., all-active or fuzzed; see field description) to prevent set-correlation attacks. Extension fields (ext, context) are intentionally omitted to prevent data leakage across the identity privacy boundary.

type IdentityMatchResponse

type IdentityMatchResponse struct {
	Type               string   `json:"type"`                 // Message type discriminator for deserialization.
	RequestID          string   `json:"request_id"`           // Echoed request identifier from the identity match request
	EligiblePackageIDs []string `json:"eligible_package_ids"` // Package IDs the user is eligible for. Packages not listed are ineligible.
	TTLSec             int      `json:"ttl_sec"`              // How long the router should cache this response, in seconds. The router returns cached eligibility without re-querying the buyer during this window. A value of 0 means do not cache.
	Tmpx               string   `json:"tmpx,omitempty"`       // HPKE-encrypted exposure token containing the resolved user identity tokens. The publisher substitutes this into creative tracking URLs as {TMPX}. The buyer's impression pixel receives the token at serve time, enabling real-time per-user frequency state updates. Wire format: kid.base64url_nopad(ciphertext) — unpadded base64url per RFC 4648 section 5 (no = characters). Publishers MUST treat this value as opaque pass-through data.
}

Response indicating which packages the user is eligible for. The ttl_sec field defines a caching contract: the router caches this response and returns cached eligibility without re-querying the buyer during the TTL window. Extension fields (ext, context) are intentionally omitted to prevent data leakage across the identity privacy boundary.

type IdentityProvider

type IdentityProvider interface {
	IdentityMatch(ctx context.Context, req *IdentityMatchRequest) (*IdentityMatchResponse, error)
}

IdentityProvider evaluates user eligibility for packages.

type IdentityToken

type IdentityToken struct {
	UserToken string  `json:"user_token"`
	UIDType   UIDType `json:"uid_type"`
}

IdentityToken carries one opaque user identifier and the type of identity graph it came from. Publishers include one entry per token they have; the buyer resolves on whichever graph matches. Used by IdentityMatchRequest.

type ImageAsset

type ImageAsset struct {
	URL        string          `json:"url"`
	Access     *AssetAccess    `json:"access,omitempty"`
	AltText    string          `json:"alt_text,omitempty"`
	Caption    string          `json:"caption,omitempty"`
	Width      int             `json:"width,omitempty"`
	Height     int             `json:"height,omitempty"`
	Provenance json.RawMessage `json:"provenance,omitempty"`
}

ImageAsset is an image asset with its URL and optional display metadata.

func (*ImageAsset) AssetTag

func (*ImageAsset) AssetTag() AssetType

AssetTag implements Asset.

func (*ImageAsset) MarshalJSON

func (a *ImageAsset) MarshalJSON() ([]byte, error)

MarshalJSON writes the "type" discriminator from AssetTag.

func (*ImageAsset) Validate

func (a *ImageAsset) Validate() error

Validate checks ImageAsset field-level constraints.

type JWKSStore

type JWKSStore struct {
	// contains filtered or unexported fields
}

JWKSStore polls a JWKS endpoint and indexes the keys by purpose:

  • Signing keys (`adcp_use=request-signing`) accessible via LookupKey(kid) for verifier middleware.
  • The current TMPX encryption key (`adcp_use=tmpx-encrypt`, newest `iat`) accessible via CurrentEncryptionRecipient() for token sealers.

Buyers publish both on the same `/.well-known/jwks.json` endpoint; the store handles both purposes in one Refresh.

func NewJWKSStore

func NewJWKSStore(opts JWKSStoreOptions) (*JWKSStore, error)

NewJWKSStore builds a JWKSStore. Call Refresh once for an initial fetch, then Run for background polling.

func (*JWKSStore) CurrentEncryptionRecipient

func (s *JWKSStore) CurrentEncryptionRecipient() (TmpxRecipient, bool)

CurrentEncryptionRecipient returns the active TMPX recipient, picked as the adcp_use=tmpx-encrypt entry with the newest iat. Returns (zero, false) when no encryption key is currently advertised.

func (*JWKSStore) LookupKey

func (s *JWKSStore) LookupKey(kid string) (*SigningKey, bool)

LookupKey implements KeyStore over the JWKS-published signing keys.

func (*JWKSStore) Refresh

func (s *JWKSStore) Refresh(ctx context.Context) error

Refresh fetches the JWKS once and rebuilds both indexes. Transient empty snapshots retain cached state.

func (*JWKSStore) Run

func (s *JWKSStore) Run(ctx context.Context) error

Run runs an initial Refresh, then loops on the refresh interval until ctx is canceled. Returns ctx.Err() when the loop exits.

type JWKSStoreOptions

type JWKSStoreOptions struct {
	// URL of the JWKS endpoint (typically `/.well-known/jwks.json`).
	// Must be https:// unless AllowInsecureScheme is true.
	URL string

	// AllowInsecureScheme permits http:// URLs for local development only.
	AllowInsecureScheme bool

	// HTTPClient overrides the default 10-second client with cross-origin
	// redirect denial.
	HTTPClient *http.Client

	// RefreshInterval defaults to 5 minutes (spec-recommended cache TTL).
	RefreshInterval time.Duration

	// Logger receives refresh outcomes.
	Logger *slog.Logger
}

JWKSStoreOptions configures a JWKSStore.

type KeyStore

type KeyStore interface {
	LookupKey(kid string) (*SigningKey, bool)
}

KeyStore resolves a kid to its SigningKey. Verifiers query this on every request — implementations MUST be safe for concurrent reads.

type MetroAreaSystem

type MetroAreaSystem string

Metro area classification systems for geographic targeting

const (
	MetroSystemNielsenDMA    MetroAreaSystem = "nielsen_dma"
	MetroSystemUKITL1        MetroAreaSystem = "uk_itl1"
	MetroSystemUKITL2        MetroAreaSystem = "uk_itl2"
	MetroSystemEurostatNUTS2 MetroAreaSystem = "eurostat_nuts2"
	MetroSystemCustom        MetroAreaSystem = "custom"
)

type Offer

type Offer struct {
	PackageID        string            `json:"package_id"`                  // Package identifier from the media buy.
	SellerAgent      json.RawMessage   `json:"seller_agent,omitempty"`      // Optional echo of the package's seller agent from sync-time metadata. Provided for publisher-side observability so log pipelines can attribute offers to sellers without round-tripping to the media-buy store. Non-authoritative: the binding on the cached AvailablePackage is source of truth. When omitted, the router MAY stamp this field from its cached package→seller map.
	Brand            json.RawMessage   `json:"brand,omitempty"`             // Brand for this offer. Required when the product allows dynamic brands (brand selected at match time rather than fixed on the package). For single-brand packages, the brand is already known from the media buy.
	Price            OfferPrice        `json:"price,omitempty"`             // Price for this offer. Only present when the product supports variable pricing. For fixed-price packages, price is already set on the media buy.
	Summary          string            `json:"summary,omitempty"`           // Buyer-generated description of the offer, for the publisher to judge relevance. E.g., '50% off Goldenfield mayo — recipe integration'. The publisher (or their AI assistant) uses this to decide whether the offer fits the context.
	CreativeManifest *json.RawMessage  `json:"creative_manifest,omitempty"` // Full creative details, inline. When present, the publisher has everything needed to render. Inline for small creatives (markdown, product card). For large creatives (VAST, video), the manifest references external assets via URLs.
	Macros           map[string]string `json:"macros,omitempty"`            // Key-value pairs the buyer passes for dynamic creative rendering or attribution tracking. In the GAM case, these flow as macro values. Not tied to user identity — attribution reconciliation happens via delivery reporting or clean room.
}

A buyer's response to a context match request. Generalizes package activation — a simple activation is an offer with just package_id. A rich response includes brand, price, summary, and optionally an inline creative manifest.

type OfferPrice

type OfferPrice struct {
	Amount   float64 `json:"amount"`             // Price amount in the specified currency
	Currency string  `json:"currency,omitempty"` // ISO 4217 currency code
	Model    string  `json:"model"`              // Pricing model for this offer
}

Lightweight price for a TMP offer. Used when the product supports variable pricing and the buyer specifies a price at match time.

type PackageEligibility

type PackageEligibility struct {
	PackageID string `json:"package_id"`
	Eligible  bool   `json:"eligible"`
}

PackageEligibility is an internal identity evaluation result. The targeting engine produces these; they are converted to EligiblePackageIDs (the spec wire format) at the boundary.

type PricingModel

type PricingModel string

Supported pricing models for advertising products

const (
	PriceModelCPM      PricingModel = "cpm"
	PriceModelVCPM     PricingModel = "vcpm"
	PriceModelCPC      PricingModel = "cpc"
	PriceModelCPCV     PricingModel = "cpcv"
	PriceModelCPV      PricingModel = "cpv"
	PriceModelCPP      PricingModel = "cpp"
	PriceModelCPA      PricingModel = "cpa"
	PriceModelFlatRate PricingModel = "flat_rate"
	PriceModelTime     PricingModel = "time"
)

type PropertyType

type PropertyType string

Types of addressable advertising properties with verifiable ownership. Property types are a subset of media channels - they represent inventory surfaces that can be validated via adagents.json.

const (
	PropertyTypeWebsite        PropertyType = "website"
	PropertyTypeMobileApp      PropertyType = "mobile_app"
	PropertyTypeCTVApp         PropertyType = "ctv_app"
	PropertyTypeDesktopApp     PropertyType = "desktop_app"
	PropertyTypeDOOH           PropertyType = "dooh"
	PropertyTypePodcast        PropertyType = "podcast"
	PropertyTypeRadio          PropertyType = "radio"
	PropertyTypeLinearTV       PropertyType = "linear_tv"
	PropertyTypeStreamingAudio PropertyType = "streaming_audio"
	PropertyTypeAIAssistant    PropertyType = "ai_assistant"
)

type ProviderRegistration

type ProviderRegistration struct {
	ProviderID    string         `json:"provider_id"`              // Stable identifier for this provider registration. Used in logs, metrics, and cache keys. Publishers assign this — it is not the provider's agent_url.
	Endpoint      string         `json:"endpoint"`                 // Base URL the router calls. The router appends /context for Context Match and /identity for Identity Match. MUST be HTTPS in production, validated against the canonical reserved IPv4 and IPv6 ranges, with the TCP connection pinned to the validated IP (DNS re-resolution alone is insufficient against rebinding). Publishers comparing two provider registrations for the same `endpoint` MUST canonicalize both per the AdCP URL canonicalization rules; two registrations differing only in case, default port, or path-slash collapsing are the same provider. See docs/trusted-match/specification#provider-registration-security, docs/building/implementation/security#webhook-url-validation-ssrf, and docs/reference/url-canonicalization.
	ContextMatch  bool           `json:"context_match,omitempty"`  // Provider handles Context Match requests (POST /context).
	IdentityMatch bool           `json:"identity_match,omitempty"` // Provider handles Identity Match requests (POST /identity).
	Countries     []string       `json:"countries,omitempty"`      // ISO 3166-1 alpha-2 country codes this provider serves. The router filters Identity Match providers by the request's country field. MUST be present and non-empty when identity_match is true.
	UIDTypes      []UIDType      `json:"uid_types,omitempty"`      // Identity types this provider can resolve. The router selects Identity Match providers whose uid_types overlaps with any uid_type in the request's identities array. MUST be present and non-empty when identity_match is true.
	Properties    []string       `json:"properties,omitempty"`     // Property RIDs (UUID v7) this provider serves. When present, the router only sends requests from these properties to this provider. When absent, the provider serves all properties.
	TimeoutMs     int            `json:"timeout_ms,omitempty"`     // Per-provider timeout in milliseconds. The router skips this provider if it does not respond within this budget. Must be less than or equal to the router's overall latency_budget_ms. The router may further reduce this based on adaptive timeout allocation.
	Priority      int            `json:"priority,omitempty"`       // Provider ordering for merge conflict resolution. Lower values have higher priority. When two providers return offers for the same package_id (a configuration error), the router keeps the offer from the higher-priority provider. Also used for adaptive timeout allocation — higher-priority providers receive a larger share of the latency budget.
	Status        ProviderStatus `json:"status,omitempty"`         // Provider lifecycle status. Active providers receive requests. Inactive providers are skipped entirely. Draining providers stop receiving new requests but in-flight requests complete normally.
}

Declares a TMP provider's endpoint, capabilities, and operational parameters. Used in router configuration (static YAML or dynamic API) and referenced by product-level provider entries. The publisher controls which providers participate in their ad decisioning. Endpoint URLs MUST be validated against SSRF, and dynamic registration endpoints MUST authenticate callers — see docs/trusted-match/specification#provider-registration-security.

type ProviderStatus

type ProviderStatus string

Provider lifecycle status.

const (
	ProviderStatusActive   ProviderStatus = "active"
	ProviderStatusInactive ProviderStatus = "inactive"
	ProviderStatusDraining ProviderStatus = "draining"
)

type RemoteKeyStore

type RemoteKeyStore struct {
	// contains filtered or unexported fields
}

RemoteKeyStore is a tmproto.KeyStore backed by a polled JSON snapshot (typically the router's GET /registry/snapshot endpoint). Reference providers use this to discover the router's signing keys without coupling to the router package's full Registry implementation.

The snapshot is parsed into a kid-indexed map. Run() schedules background refreshes; LookupKey serves from the most recent successful refresh.

func NewRemoteKeyStore

func NewRemoteKeyStore(opts RemoteKeyStoreOptions) (*RemoteKeyStore, error)

NewRemoteKeyStore builds a RemoteKeyStore. Call Refresh for an initial synchronous fetch and Run to begin background polling.

func (*RemoteKeyStore) LookupKey

func (s *RemoteKeyStore) LookupKey(kid string) (*SigningKey, bool)

LookupKey implements tmproto.KeyStore.

func (*RemoteKeyStore) Refresh

func (s *RemoteKeyStore) Refresh(ctx context.Context) (int, error)

Refresh fetches the snapshot once and replaces the in-memory keystore. Returns the number of keys observed. An empty snapshot is treated as a transient registry condition — the previous keys are retained and a warning is logged so the agent doesn't 401 every request during a publisher's mid-deploy snapshot churn.

func (*RemoteKeyStore) Run

func (s *RemoteKeyStore) Run(ctx context.Context) error

Run blocks on an initial synchronous fetch so the keystore is non-empty before the caller serves traffic, then schedules background refreshes driven by the supplied context. Returns when ctx is canceled.

type RemoteKeyStoreOptions

type RemoteKeyStoreOptions struct {
	// URL of the JSON snapshot endpoint that returns property records with
	// signing_keys arrays. Must use https:// unless AllowInsecureScheme is true.
	URL string

	// AllowInsecureScheme permits http:// URLs. For local development only —
	// a plain-HTTP keystore lets a network attacker swap signing keys.
	AllowInsecureScheme bool

	// HTTPClient is the client used for snapshot fetches. When nil, a 10-second
	// client is constructed with redirects denied (HPKE / signing-key material
	// must not follow registry redirects to arbitrary destinations).
	HTTPClient *http.Client

	// RefreshInterval between background refreshes. Defaults to 5 minutes
	// (the spec's recommended cache TTL).
	RefreshInterval time.Duration

	// Logger receives refresh outcomes.
	Logger *slog.Logger
}

RemoteKeyStoreOptions configures a RemoteKeyStore.

type Signer

type Signer struct {
	KeyID string
	// contains filtered or unexported fields
}

Signer signs context-match and identity-match requests.

func NewSigner

func NewSigner(keyID string, priv ed25519.PrivateKey) (*Signer, error)

NewSigner constructs a Signer. Returns an error if the private key is not Ed25519-shaped.

func (*Signer) PublicJWK

func (s *Signer) PublicJWK() SigningKey

PublicJWK returns the SigningKey JWK that verifiers need.

func (*Signer) SignContextMatch

func (s *Signer) SignContextMatch(req *ContextMatchRequest, providerEndpointURL string, epoch int64) string

SignContextMatch signs a context-match request bound to the given provider endpoint URL and epoch. Returns the base64url-no-pad signature for use in the X-AdCP-Signature header.

func (*Signer) SignIdentityMatch

func (s *Signer) SignIdentityMatch(req *IdentityMatchRequest, providerEndpointURL string, epoch int64) (string, error)

SignIdentityMatch signs an identity-match request bound to the given provider endpoint URL and epoch. The request's Country field is not part of the signing input — callers should strip it before signing per the spec.

type SigningKey

type SigningKey struct {
	Kid       string     `json:"kid"`
	Kty       string     `json:"kty"`
	Alg       string     `json:"alg,omitempty"`
	Crv       string     `json:"crv,omitempty"`
	X         string     `json:"x,omitempty"`
	Use       string     `json:"use,omitempty"`
	AdcpUse   string     `json:"adcp_use,omitempty"` // "request-signing" or "tmpx-encrypt"
	IssuedAt  int64      `json:"iat,omitempty"`      // Unix seconds; higher = newer when picking the current key
	RevokedAt *time.Time `json:"revoked_at,omitempty"`
}

SigningKey is a publisher-attested signing key, shaped to match the agent-signing-key.json schema. Verifiers maintain a keystore of these keyed by Kid.

func PublicSigningKey

func PublicSigningKey(kid string, pub ed25519.PublicKey) SigningKey

PublicSigningKey builds a SigningKey JWK for an Ed25519 public key. Used by router config wiring to publish keys to the registry.

func (*SigningKey) PublicKey

func (k *SigningKey) PublicKey() (ed25519.PublicKey, error)

PublicKey extracts the Ed25519 public key from the JWK fields. Returns an error if the key is not Ed25519/OKP.

type StaticKeyStore

type StaticKeyStore struct {
	// contains filtered or unexported fields
}

StaticKeyStore is a concurrent-safe map-backed KeyStore for tests and for wrapping a pre-built snapshot of the registry.

func NewStaticKeyStore

func NewStaticKeyStore(keys []SigningKey) *StaticKeyStore

NewStaticKeyStore builds a keystore from a slice of keys. Keys with empty Kid are dropped.

func (*StaticKeyStore) LookupKey

func (s *StaticKeyStore) LookupKey(kid string) (*SigningKey, bool)

LookupKey returns the key with the given kid.

type TextAsset

type TextAsset struct {
	Role          string          `json:"role,omitempty"`
	Content       string          `json:"content"`
	ContentFormat string          `json:"content_format,omitempty"` // text/plain (default), text/markdown, text/html, application/json
	Language      string          `json:"language,omitempty"`       // BCP 47 language tag
	HeadingLevel  int             `json:"heading_level,omitempty"`  // only for role=heading
	Provenance    json.RawMessage `json:"provenance,omitempty"`
}

TextAsset is a text block (paragraph, heading, caption, etc.). Content is publisher-supplied and MUST be treated as untrusted input when passed to LLM-based evaluation.

func (*TextAsset) AssetTag

func (*TextAsset) AssetTag() AssetType

AssetTag implements Asset.

func (*TextAsset) MarshalJSON

func (a *TextAsset) MarshalJSON() ([]byte, error)

MarshalJSON writes the "type" discriminator from AssetTag so it can't be forged via a user-set struct field.

func (*TextAsset) Validate

func (a *TextAsset) Validate() error

Validate checks TextAsset field-level constraints.

type TmpResponseType

type TmpResponseType string

What the publisher wants back from a TMP context match. Determines the richness level of the buyer's offer response.

const (
	ResponseTypeActivation   TmpResponseType = "activation"
	ResponseTypeCatalogItems TmpResponseType = "catalog_items"
	ResponseTypeCreative     TmpResponseType = "creative"
	ResponseTypeDeal         TmpResponseType = "deal"
)

type TmpxEntry

type TmpxEntry struct {
	TypeID TmpxTypeID
	Token  []byte // exactly TmpxTokenSize(TypeID) bytes
}

TmpxEntry is one identity token packed into a TMPX plaintext.

type TmpxRecipient

type TmpxRecipient struct {
	Kid       string
	PublicKey *ecdh.PublicKey // X25519
}

TmpxRecipient is a buyer-cluster public key the token is sealed to. Kid is max 8 chars, opaque, MUST NOT encode geographic or deployment information.

type TmpxTypeID

type TmpxTypeID uint8

TmpxTypeID is one entry in the TMPX type registry. Type IDs are stable — new types append, existing IDs never change. Tokens are stored in binary; callers convert source string identifiers to binary before encoding.

const (
	TmpxTypeUID2                TmpxTypeID = 1
	TmpxTypeEUID                TmpxTypeID = 2
	TmpxTypeID5                 TmpxTypeID = 3
	TmpxTypeRampID              TmpxTypeID = 4
	TmpxTypeRampIDDerived       TmpxTypeID = 5
	TmpxTypeMAID                TmpxTypeID = 6
	TmpxTypePairID              TmpxTypeID = 7
	TmpxTypeHashedEmail         TmpxTypeID = 8
	TmpxTypePublisherFirstParty TmpxTypeID = 9
)

type UIDType

type UIDType string

Type of user identifier. Used in audience sync, event logging, and TMP identity match requests to tell the receiver which identity graph to resolve against.

const (
	UIDTypeRampID              UIDType = "rampid"
	UIDTypeRampIDDerived       UIDType = "rampid_derived"
	UIDTypeID5                 UIDType = "id5"
	UIDTypeUID2                UIDType = "uid2"
	UIDTypeEUID                UIDType = "euid"
	UIDTypePairID              UIDType = "pairid"
	UIDTypeMAID                UIDType = "maid"
	UIDTypeHashedEmail         UIDType = "hashed_email"
	UIDTypePublisherFirstParty UIDType = "publisher_first_party"
	UIDTypeOther               UIDType = "other"
)

type UnknownAsset

type UnknownAsset struct {
	Type AssetType       // the unrecognized discriminator value
	Raw  json.RawMessage // the original bytes, re-emitted verbatim on marshal
}

UnknownAsset preserves an asset entry whose type the SDK does not recognize, so older SDKs can round-trip newer publisher payloads. Emitted by Assets.UnmarshalJSON when the discriminator is non-empty but unknown.

func (*UnknownAsset) AssetTag

func (u *UnknownAsset) AssetTag() AssetType

AssetTag implements Asset.

func (*UnknownAsset) MarshalJSON

func (u *UnknownAsset) MarshalJSON() ([]byte, error)

MarshalJSON returns the original bytes unchanged so routers pass through unknown asset types without corrupting them.

type VerifyOptions

type VerifyOptions struct {
	// KeyStore resolves kids carried by incoming requests. Required.
	KeyStore KeyStore

	// OwnEndpointURL is this provider's registered endpoint URL — verifier
	// rejects signatures that don't bind to it.
	OwnEndpointURL string

	// RequireSignature, when true, rejects requests that arrive without a
	// signature. When false, unsigned requests pass through to the inner
	// handler with a warning log line — useful only for migration windows.
	RequireSignature bool

	// Logger receives verification outcomes. Defaults to slog.Default().
	Logger *slog.Logger

	// Now optionally returns the wall-clock time the verifier compares against
	// the daily epoch. Defaults to time.Now.
	Now func() time.Time
}

VerifyOptions configures a TMP-signature verifier middleware.

type VideoAsset

type VideoAsset struct {
	URL              string          `json:"url"`
	Access           *AssetAccess    `json:"access,omitempty"`
	DurationMs       int             `json:"duration_ms,omitempty"`
	Transcript       string          `json:"transcript,omitempty"`
	TranscriptFormat string          `json:"transcript_format,omitempty"` // text/plain (default), text/markdown, application/json
	TranscriptSource string          `json:"transcript_source,omitempty"` // original_script, subtitles, closed_captions, dub, generated
	ThumbnailURL     string          `json:"thumbnail_url,omitempty"`
	Provenance       json.RawMessage `json:"provenance,omitempty"`
}

VideoAsset is a video asset with its URL and optional transcript/metadata. Transcript is publisher-supplied and MUST be treated as untrusted input.

func (*VideoAsset) AssetTag

func (*VideoAsset) AssetTag() AssetType

AssetTag implements Asset.

func (*VideoAsset) MarshalJSON

func (a *VideoAsset) MarshalJSON() ([]byte, error)

MarshalJSON writes the "type" discriminator from AssetTag.

func (*VideoAsset) Validate

func (a *VideoAsset) Validate() error

Validate checks VideoAsset field-level constraints.

Jump to

Keyboard shortcuts

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