dp1

package module
v0.6.2 Latest Latest
Warning

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

Go to latest
Published: Sep 15, 2026 License: MPL-2.0 Imports: 10 Imported by: 0

README

dp1-go

Test Lint codecov

Go SDK for the DP-1 protocol: playlists, playlist-groups (exhibitions), ref manifests, JCS signing payloads (RFC 8785), signature verification (Ed25519 + Ethereum EIP-191), and registered JSON Schema extensions.

Module: github.com/display-protocol/dp1-go

Requirements

  • Go 1.22+

Install

go get github.com/display-protocol/dp1-go

Usage

Parse and validate

All entrypoints validate raw JSON against embedded JSON Schema (draft 2020-12), then decode into typed structs.

Function Document
ParseAndValidatePlaylist Core playlist
ParseAndValidatePlaylistWithPlaylistsExtension Core playlist + playlists extension overlay
ParseAndValidatePlaylistGroup Playlist-group (exhibition)
ParseAndValidateRefManifest Ref manifest
ParseAndValidateChannel channels extension document
import "github.com/display-protocol/dp1-go"

p, err := dp1.ParseAndValidatePlaylist(playlistJSON)
if err != nil {
    return err
}

g, err := dp1.ParseAndValidatePlaylistGroup(groupJSON)
m, err := dp1.ParseAndValidateRefManifest(manifestJSON)
ch, err := dp1.ParseAndValidateChannel(channelJSON)

Playlist with the optional playlists extension overlay:

p, err := dp1.ParseAndValidatePlaylistWithPlaylistsExtension(data)

Dynamic playlist items (playlists extension dynamicQuery): hydrate {{placeholders}} with playlist.HydrationParams, fetch the indexer, map response rows, validate each item with core PlaylistItem plus the playlists-extension overlay (note / displayAt / inlineManifest), and append after static items via (*playlist.Playlist).ResolveDynamicQuery (pass *http.Client, or nil for http.DefaultClient, and *playlist.DynamicQueryFetchOptions or nil for HTTPS-only + SSRF-safe defaults). Set AllowInsecureHTTP on the options value to allow http:// and local addresses (for example httptest). The same fetch and decode path is available as playlist.PlaylistItemsFromDynamicQuery(ctx, dq, params, client, opts) when you only need []PlaylistItem. Use errors.Is(err, playlist.ErrDynamicQueryEndpointPolicy) when the outbound URL fails policy checks.

Errors
  • errors.Is(err, dp1.ErrValidation) — JSON Schema validation failed (after mapping, playlist failures still wrap ErrValidation).
  • errors.As into *dp1.CodedError — stable ErrorCode for UI/telemetry (e.g. dp1.CodePlaylistInvalid; dp1.CodeSigInvalid is used by the sign package). Validation failures use codes such as CodePlaylistInvalid, CodePlaylistGroupInvalid, CodeRefManifestInvalid, CodeChannelInvalid.
var coded *dp1.CodedError
if errors.As(err, &coded) {
    _ = coded.Code
}
dpVersion (DP-1 §12)
v, err := dp1.ParseDPVersion(p.DPVersion)
if err != nil { /* ... */ }
_ = dp1.WarnMajorMismatch(v, 1) // optional: warn if document major ≠ player major
Signing (github.com/display-protocol/dp1-go/sign)

The sign package implements DP-1 §7.1 signing: strip signature fields → JCS (RFC 8785) → append LF → SHA-256. All algorithms sign the same 32-byte digest. Supports Ed25519 (did:key) and Ethereum EIP-191 (did:pkh).

Common
  • sign.PayloadHashString / sign.VerifyPayloadHash — compute or verify sha256:<hex> for payload_hash field.
Ed25519 (algorithm: ed25519)
  • sign.SignMultiEd25519(raw, priv, role, ts) — create v1.1+ signature with did:key kid.
  • sign.VerifyMultiSignature(raw, sig) — verify any supported algorithm (ed25519, eip191).
  • sign.Ed25519DIDKey / sign.Ed25519PublicKeyFromDIDKey — encode/decode W3C did:key for Ed25519 public keys.
  • Legacy v1.0.x: sign.SignLegacyEd25519 / sign.VerifyLegacyEd25519 — single signature: ed25519:<hex> field.
Ethereum EIP-191 (algorithm: eip191)
  • sign.SignMultiEIP191(raw, priv, chainID, role, ts) — create signature using Ethereum personal_sign (EIP-191 version 0x45) with did:pkh kid.
  • sign.VerifyMultiSignature(raw, sig) — verify (same function as Ed25519, dispatches by sig.Alg).
  • sign.EthereumAddressToDIDPKH(addr, chainID) / sign.EthereumAddressFromDIDPKH(kid) — encode/decode did:pkh:eip155:{chainID}:{address} (CAIP-10).
  • Works with all EVM chains: Ethereum (1), Polygon (137), Arbitrum (42161), Base (8453), etc.

Example: Sign with Ethereum

import (
    "github.com/ethereum/go-ethereum/crypto"
    "github.com/display-protocol/dp1-go/sign"
    "github.com/display-protocol/dp1-go/playlist"
)

priv, _ := crypto.GenerateKey()  // or crypto.HexToECDSA(hexKey)
raw, _ := json.Marshal(playlist)

// Sign for Ethereum mainnet (chainID=1)
sig, err := sign.SignMultiEIP191(raw, priv, 1, playlist.RoleCurator, "2026-04-13T10:00:00Z")
// sig.Alg = "eip191"
// sig.Kid = "did:pkh:eip155:1:0xB9C5714089478a327F09197987f16f9E5d936E8a"

// Verify
err = sign.VerifyMultiSignature(raw, sig)
Multi-signature verification
  • sign.VerifyMultiSignaturesJSON(raw) — decode signatures[] array and verify all entries; returns (ok, failed, err).
  • sign.VerifyPlaylistSignatures / sign.VerifyPlaylistGroupSignatures / sign.VerifyChannelSignatures — equivalent wrappers for clarity.
  • Documents can mix Ed25519 and Ethereum signatures; each is verified independently.

Replay protections: Cross-document replay prevented by payload_hash. Cross-chain and temporal replay not enforced (see package docs).

Display merge (github.com/display-protocol/dp1-go/merge)

Resolution order: defaults → item inlineManifest controls → ref manifest controls → item override → item-local display fields.

import "github.com/display-protocol/dp1-go/merge"

prefs, err := merge.DisplayForItem(def, refManifest, item)

item.InlineManifest is applied automatically, so pass nil for refManifest when no manifest was fetched. merge.ManifestForItem(refManifest, item) returns the manifest a player should read for non-display fields (metadata, i18n): a fetched ref wins, the inline copy is the offline/degraded fallback. Both return an error if the item's inline manifest cannot be decoded — the normal case for playlists parsed on the core-only path, where nothing has checked the field. DisplayForItem returns it only when refManifest is nil: with an authoritative manifest in hand the inline fallback goes unread, so a malformed one does not block rendering.

Known gap: interaction settings resolve by Go zero value rather than field presence, so a higher-precedence layer can switch an interaction on but not off, and a manifest's mouse block replaces the lower layer's wholesale (#6).

Extension types (optional)

The draft content-rating extension uses playlist.PlaylistItem.ContentRating (*contentrating.Rating) and ContentReasons (*[]string). Nil rating means absent/unrated; the reasons pointer preserves absent versus a present empty array through decode/marshal. Use ParseAndValidatePlaylistWithContentRatingExtension for core playlists or ParseAndValidatePlaylistWithPlaylistsAndContentRatingExtensions when the playlists extension is also present; these reject present null, non-string ratings, and malformed reasons. The rating vocabulary is open (spec §3.3): any string is a valid rating, v0.1.0 defines only general and mature, and a value the consumer does not recognize means unrated — nothing is assumed from an unknown label, so only mature hides anything. Call Rating.Known() to branch on the values this SDK version defines; an unknown rating is a valid document, not an error, and it decodes and re-encodes unchanged. The parsers that do not implement the extension (ParseAndValidatePlaylist, ParseAndValidatePlaylistWithPlaylistsExtension) tolerate a rating they cannot type rather than failing the decode, leaving the field nil — core DP-1 permits item properties it does not describe, so a core-only consumer must still be able to read the playlist. Dynamic-query item resolution uses the combined item validator. Valid policy exclusions may use dp1.CodeContentBlocked; malformed metadata remains dp1.CodePlaylistInvalid.

Shared and extension-specific structs live under extension/ (for example extension/playlists for the playlists overlay—DynamicQuery, experimental Note on playlist.Playlist and playlist.PlaylistItem; extension/identity for Entity; extension/channels for the channel document type). Item-level displayAt is a *string on playlist.PlaylistItem (validated with the playlists-extension overlay). Item-level inlineManifest is a json.RawMessage on playlist.PlaylistItem (playlists extension §3.6): a complete ref manifest carried inside the playlist instead of behind ref, validated by the unmodified ref-manifest schema and already covered by the playlist signature (no refHash counterpart). Raw, like override, so the core-only parser stays tolerant of a document it does not implement and the signed bytes survive a round trip verbatim — decode with item.ParseInlineManifest(), or pass item.InlineManifest to dp1.ParseAndValidateRefManifest for decode plus schema validation. Prefer ParseAndValidate* at the root package for full schema validation.

refmanifest.Thumbnail.W / .H are *int: the ref-manifest schema requires only uri, so dimensions may be absent and consumers must handle nil.

Breaking changes

This module is v0.x; minor versions may break source compatibility. v0.6.0 changes refmanifest.Thumbnail.W/.H from int to *int so that an absent dimension is distinguishable from a zero one. CHANGELOG.md has the rationale and before/after.

Scheduling helpers for the playlists extension live in displayat (Parse, ComputeActiveSet, NextDisplayAt): resolve displayAt wire forms and compute eligible items when any item has displayAt. Per §3.5.6, the same rules apply whether items came from static items or dynamicQuery.

Schemas

Normative JSON Schemas are embedded from the spec repo under internal/schema/ (core v1.1.0 + extensions, including extensions/playlists/schema.json with the optional item-level overlay $defs/PlaylistItemExtension — note, displayAt, inlineManifest — and playlist_with_extension.json / playlist_item_with_extension.json, which both reference that single overlay so whole-playlist and single-item validation cannot drift).

extensions/content-rating/ is a draft overlay ($id … /v0.1.0/), embedded byte-for-byte from the spec repo's extensions/content-rating/ (merged in display-protocol/dp1#52); as a draft extension its shape may still change with a version bump. Its four composed files (playlist_with_extension.json, playlist_item_with_extension.json, and the _with_playlists_extension pair) resolve to the same $defs/PlaylistItemExtension — the item files $ref it directly, the playlist files reach it through schema.json's items array — on the same no-drift rule as playlists.

Testing

go test ./... -race -count=1
bash scripts/check-coverage.sh 80   # merged module coverage threshold (CI)

CI uploads the merged profile to Codecov after the threshold check. If uploads require authentication, add a CODECOV_TOKEN repository secret from codecov.io.

License

See LICENSE.

Documentation

Overview

Package dp1 implements parsing, validation, and helpers for the DP-1 protocol (playlists, playlist-groups, ref manifests, and registered extensions).

Entrypoints use JSON Schema (draft 2020-12) embedded from the specification: use ParseAndValidate* functions to obtain typed values that are known to match the schema.

Index

Constants

This section is empty.

Variables

View Source
var (
	ErrValidation     = validate.ErrValidation
	ErrSigInvalid     = sign.ErrSigInvalid
	ErrUnsupportedAlg = sign.ErrUnsupportedAlg
	ErrNoSignatures   = sign.ErrNoSignatures
)

Sentinel errors re-exported so applications can use a single import path with errors.Is / errors.As (e.g. signature verification vs JSON Schema validation).

View Source
var (
	PlaylistCoreSchemaValidate                                    = validate.Playlist
	PlaylistWithPlaylistsExtensionSchemaValidate                  = validate.PlaylistWithPlaylistsExtension
	PlaylistWithContentRatingExtensionSchemaValidate              = validate.PlaylistWithContentRatingExtension
	PlaylistWithPlaylistsAndContentRatingExtensionsSchemaValidate = validate.PlaylistWithPlaylistsAndContentRatingExtensions
	PlaylistGroupSchemaValidate                                   = validate.PlaylistGroup
	RefManifestSchemaValidate                                     = validate.RefManifest
	ChannelExtensionSchemaValidate                                = validate.ChannelsExtension
)

The following hooks default to the real JSON Schema validators. Tests may replace them briefly to exercise JSON decode error paths. Do not reassign concurrently in production.

Functions

func CodeFromChannelValidation

func CodeFromChannelValidation(err error) error

CodeFromChannelValidation maps JSON Schema validation failures to CodeChannelInvalid.

func CodeFromPlaylistGroupValidation

func CodeFromPlaylistGroupValidation(err error) error

CodeFromPlaylistGroupValidation maps JSON Schema validation failures to CodePlaylistGroupInvalid.

func CodeFromPlaylistValidation

func CodeFromPlaylistValidation(err error) error

CodeFromPlaylistValidation maps JSON Schema validation failures to CodePlaylistInvalid (playlist core or playlist + playlists extension schemas). The wrapped error still satisfies errors.Is(err, ErrValidation) for the underlying failure.

func CodeFromRefManifestValidation

func CodeFromRefManifestValidation(err error) error

CodeFromRefManifestValidation maps JSON Schema validation failures to CodeRefManifestInvalid.

func ParseAndValidateChannel

func ParseAndValidateChannel(data []byte) (*channels.Channel, error)

ParseAndValidateChannel validates the channels extension document.

func ParseAndValidatePlaylist

func ParseAndValidatePlaylist(data []byte) (*playlist.Playlist, error)

ParseAndValidatePlaylist validates against the core playlist schema and decodes into playlist.Playlist.

Only core fields are guaranteed schema-valid: the core schema describes no registry-extension field, so an inlineManifest present in the document decodes without ever being checked, and the merge package feeds its controls straight into display preferences. Callers that act on it should parse with ParseAndValidatePlaylistWithPlaylistsExtension instead.

func ParseAndValidatePlaylistGroup

func ParseAndValidatePlaylistGroup(data []byte) (*playlistgroup.Group, error)

ParseAndValidatePlaylistGroup validates a playlist-group (exhibition) document.

func ParseAndValidatePlaylistWithContentRatingExtension added in v0.6.2

func ParseAndValidatePlaylistWithContentRatingExtension(data []byte) (*playlist.Playlist, error)

ParseAndValidatePlaylistWithContentRatingExtension validates core plus the draft content-rating extension.

func ParseAndValidatePlaylistWithPlaylistsAndContentRatingExtensions added in v0.6.2

func ParseAndValidatePlaylistWithPlaylistsAndContentRatingExtensions(data []byte) (*playlist.Playlist, error)

ParseAndValidatePlaylistWithPlaylistsAndContentRatingExtensions validates both draft overlays.

func ParseAndValidatePlaylistWithPlaylistsExtension

func ParseAndValidatePlaylistWithPlaylistsExtension(data []byte) (*playlist.Playlist, error)

ParseAndValidatePlaylistWithPlaylistsExtension validates against the composed playlists extension schema (core bundle + extension fragment).

func ParseAndValidateRefManifest

func ParseAndValidateRefManifest(data []byte) (*refmanifest.Manifest, error)

ParseAndValidateRefManifest validates a ref manifest document.

func ParseDPVersion

func ParseDPVersion(s string) (*semver.Version, error)

ParseDPVersion parses a document dpVersion field (SemVer), per DP-1 §12. Typical use: after decoding a playlist, compare or log against the player’s supported spec version.

func ValidateContentRatingExtension added in v0.6.2

func ValidateContentRatingExtension(data []byte) error

ValidateContentRatingExtension validates only the draft content-rating overlay. It accepts a partial/full playlist without requiring core fields or signatures, making it suitable for legacy ingestion boundaries that cannot yet require a signed DP-1 document.

func WarnMajorMismatch

func WarnMajorMismatch(document *semver.Version, wantMajor uint64) error

WarnMajorMismatch is an optional policy helper for §12 (“players warn on major mismatch”). Pass the parsed dpVersion and the major version your player implements (e.g. 1 for DP-1 v1.x). Nil document is a no-op so callers can skip parsing when the field is absent.

func WithCode

func WithCode(code ErrorCode, err error) error

WithCode wraps err when err is non-nil.

Types

type CodedError

type CodedError struct {
	Code ErrorCode
	Err  error
}

CodedError wraps an error with ErrorCode for UI or telemetry.

func (*CodedError) Error

func (e *CodedError) Error() string

func (*CodedError) Unwrap

func (e *CodedError) Unwrap() error

type ErrorCode

type ErrorCode string

ErrorCode is a stable string for DP-1 §14 (player → UI) mapping where applicable, plus SDK-specific codes for document types not named in §14.

const (
	// CodePlaylistInvalid is the §14 code for playlist schema / structural failure (“Playlist malformed.”).
	// Use with ParseAndValidatePlaylist and ParseAndValidatePlaylistWithPlaylistsExtension only.
	CodePlaylistInvalid ErrorCode = "playlistInvalid"
	// CodePlaylistGroupInvalid is used when a playlist-group (exhibition) document fails JSON Schema validation or decode.
	CodePlaylistGroupInvalid ErrorCode = "playlistGroupInvalid"
	// CodeRefManifestInvalid is used when a ref manifest document fails JSON Schema validation or decode.
	CodeRefManifestInvalid ErrorCode = "refManifestInvalid"
	// CodeChannelInvalid is used when a channel extension document fails JSON Schema validation or decode.
	CodeChannelInvalid ErrorCode = "channelInvalid"
	// CodeSigInvalid is the §14 code for signature verification failure (“Invalid feed signature.”).
	// Returned by the sign package; not emitted from ParseAndValidate* helpers.
	CodeSigInvalid ErrorCode = "sigInvalid"
	// The following §14 codes are reserved for player/runtime behavior (auth, hash drift, network).
	// This parsing/signing SDK does not emit them; clients may use them when implementing a full player.
	CodeLicenseDenied     ErrorCode = "licenseDenied"
	CodeReproMismatch     ErrorCode = "reproMismatch"
	CodeSourceUnreachable ErrorCode = "sourceUnreachable"
	// CodeContentBlocked reports that a structurally valid item was excluded by consumer policy.
	// Invalid content-rating metadata remains CodePlaylistInvalid.
	CodeContentBlocked ErrorCode = "contentBlocked"
)

Directories

Path Synopsis
Package displayat provides parsing and scheduling helpers for the DP-1 Playlist Extension displayAt field.
Package displayat provides parsing and scheduling helpers for the DP-1 Playlist Extension displayAt field.
extension
channels
Package channels contains types for the DP-1 "channels" extension (playlist-group evolution).
Package channels contains types for the DP-1 "channels" extension (playlist-group evolution).
contentrating
Package contentrating defines the draft DP-1 content-rating extension types.
Package contentrating defines the draft DP-1 content-rating extension types.
identity
Package identity holds shared types used by multiple DP-1 registry extensions.
Package identity holds shared types used by multiple DP-1 registry extensions.
playlists
Package playlists contains types for the DP-1 "playlists" extension (draft): optional fields on a playlist document.
Package playlists contains types for the DP-1 "playlists" extension (draft): optional fields on a playlist document.
internal
validate
Package validate loads embedded JSON Schema (draft 2020-12) documents and validates raw JSON payloads.
Package validate loads embedded JSON Schema (draft 2020-12) documents and validates raw JSON payloads.
Package jcs exposes RFC 8785 JSON Canonicalization for DP-1 signing payloads.
Package jcs exposes RFC 8785 JSON Canonicalization for DP-1 signing payloads.
Package merge applies DP-1 resolution order for display (and related) fields on an item: defaults → item inlineManifest → ref manifest → item override (JSON) → item-local fields (last wins for the same path).
Package merge applies DP-1 resolution order for display (and related) fields on an item: defaults → item inlineManifest → ref manifest → item override (JSON) → item-local fields (last wins for the same path).
Package playlist defines DP-1 major-1 playlist document types (core wire format).
Package playlist defines DP-1 major-1 playlist document types (core wire format).
Package playlistgroup defines DP-1 playlist-group (exhibition) documents.
Package playlistgroup defines DP-1 playlist-group (exhibition) documents.
Package refmanifest defines the optional ref manifest envelope (metadata + controls).
Package refmanifest defines the optional ref manifest envelope (metadata + controls).
Package sign implements DP-1 signing payload construction (JCS, UTF-8, LF-terminated octets) and signature verification.
Package sign implements DP-1 signing payload construction (JCS, UTF-8, LF-terminated octets) and signature verification.

Jump to

Keyboard shortcuts

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