protocol

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Nov 12, 2026 License: Apache-2.0 Imports: 18 Imported by: 0

Documentation

Overview

Package protocol implements the Tessera protocol's value and metadata contracts. It is independent of HTTP handlers and storage engines.

DecodeRequest is the strict request boundary: it rejects duplicate members, unknown fields, malformed Unicode, trailing JSON, and oversized input. Value decoding is forward-compatible with additional response envelope members; DecodeRequest rejects those members when they occur in a request instead.

This package and its published vectors are provisional. They do not certify a server's conformance or replace the specification in docs/.

Index

Constants

View Source
const CollectionAccessCapability = "collection_access"
View Source
const CreatorAttributionCapability = "creator_attribution"

CreatorAttributionCapability enables server-populated creator fields.

View Source
const CreatorIDBytes = 32

CreatorIDBytes is the encoded size of a dataset-scoped creator ID.

View Source
const CreatorScopedMutationsCapability = "creator_scoped_mutations"
View Source
const DatasetCreationCapability = "dataset_creation"

DatasetCreationCapability says that the creation route exists on this server. A client MUST NOT read it as permission: it reports that the route is here, not that this caller may use it.

View Source
const DefaultAggregateGroups = 32

DefaultAggregateGroups is the group bound in force where a dataset declares none. It was a constant in the evaluator before it was a declared limit, and the value has not changed: moving it was about a client being able to read it before planning a query, not about what it should be.

View Source
const DefaultTransactionOperations = 256

DefaultTransactionOperations is the operation bound where a dataset declares none, and the value that was enforced before it was declared.

View Source
const MaxFixedScale = 32

MaxFixedScale bounds a declared scale. A field cannot usefully declare more fraction digits than a value may carry in total, and the two bounds are kept apart because they answer different questions: this one is what the schema may say, MaxFixedDigits is what a value may be.

View Source
const MinimumIdempotencyRetentionSeconds = 86400

DevelopmentLimits matches the discovery example. It is an implementation choice, not a protocol-wide minimum or default for other implementations. MinimumIdempotencyRetentionSeconds is the floor under any advertised idempotency retention window: twenty-four hours.

A bound that can be set low enough to void the guarantee it bounds is not a bound. Retention was required to be "at least the advertised value" and the advertised value was unconstrained, so a server could advertise sixty seconds and remain conformant while making reconciliation impossible — and a client meeting that has nothing to do but show somebody who lost their connection a dead end.

A day rather than an hour because the client that needs this most is on a phone, and the gap between losing a response and being able to ask about it is a night's sleep.

View Source
const SharedReadsCapability = "shared_reads"

SharedReadsCapability says that an authorization request here may ask to read everything shared with the account instead of naming a dataset.

Variables

View Source
var ErrNoCommonProtocolVersion = errors.New("no common protocol version")

ErrNoCommonProtocolVersion means negotiation cannot establish a connection.

View Source
var OptionalReadCapabilities = []string{}

OptionalReadCapabilities are the optional read features a composition may advertise. Required operations are never toggles: only these may be withheld, and withholding one means refusing it, never answering it approximately.

View Source
var RetiredReadCapabilities = []string{"aggregation", "distinct_references", "text_matching"}

RetiredReadCapabilities are names that were once advertised and are now required operations. A deployment naming one is refused rather than humoured: it is asking to switch off something this protocol no longer lets it decline, and starting anyway would leave the configuration saying one thing and the server doing another.

ServerCapabilities are the capability names that describe the server rather than a dataset. Each is advertised on its own condition, so this is the vocabulary rather than a list to emit: what it fixes is that a name exists in one place, and that the specification and the code can be compared.

Functions

func AtOperation

func AtOperation(err error, index int) error

func CanonicalFixed

func CanonicalFixed(literal string) string

CanonicalFixed reduces a decimal literal to the one spelling this protocol accepts: no trailing zero in the fraction, no empty fraction, and one zero for the value zero however it was signed or padded.

It exists for results the server computes rather than for values it is given. A value that arrives non-canonical is refused, because rewriting what a caller sent would make `expect` and record versions disagree with it; a sum this store computed has no sender to disagree with.

func Compare

func Compare(a, b Value) (int, error)

Compare returns -1, 0, or 1 in ascending protocol order. Null precedes all present values. JSON, blob and media have equality but no ordering in v1.

func DatasetCapabilities

func DatasetCapabilities() []string

DatasetCapabilities are every capability name a server may advertise for a dataset.

It exists so there is one list rather than one per surface. The administration page kept a copy of its own, the three retired names stayed in that copy when they became required operations, and the page went on telling an operator this deployment withheld three things the protocol no longer lets anyone decline.

func DecodeRequest

func DecodeRequest(data []byte, dst any, maxBytes int) error

DecodeRequest enforces the byte limit before parsing. It is for request bodies; json.Unmarshal should be used for forward-compatible response decoding. dst must be a non-nil pointer to a concrete wire type, not an interface{}.

func Equal

func Equal(a, b Value) (bool, error)

Equal implements exact predicate and expected-field equality. Null is equal only to null; comparing a present value with null is a defined false result.

func ErrorCode

func ErrorCode(err error) string

ErrorCode returns a stable error code, without exposing wrapped input errors.

func ErrorCodes

func ErrorCodes() []string

ErrorCodes returns every code this protocol defines, sorted. It exists so a specification can be compared against the code rather than proofread against it.

func ErrorStatus

func ErrorStatus(code string) int

func FixedDigits

func FixedDigits(canonical string) int

FixedDigits counts the significant digits of a canonical fixed value, sign and decimal point excluded. The canonical form has no leading zero in the integer part and no trailing zero in the fraction, so every digit it carries is one the value needs — except the single `0` of a value less than one, which is a placeholder rather than a digit and is not counted.

func FixedScale

func FixedScale(canonical string) int

FixedScale is the number of fraction digits a canonical fixed value carries.

func MarshalWire

func MarshalWire(value any) ([]byte, error)

MarshalWire encodes protocol envelopes without encoding/json's compaction of nested Marshaler results. That compaction would rewrite opaque annotation values (including whitespace, escapes, and number spellings). Use this at the HTTP boundary and for persisted metadata. It accepts acyclic protocol models.

func ParseContentReference

func ParseContentReference(value string) (storeID, itemID string, err error)

ParseContentReference accepts only a qualified store//item reference. A caller resolves the named store, never the dataset's current replacement.

func RequestDigest

func RequestDigest(method, target string, body []byte) [32]byte

RequestDigest uses SHA-256 over three uint64-big-endian length-prefixed byte strings: method, request target (path and query), and decoded body. This is an implementation framing choice, not a wire-visible canonicalization. Callers must remove transfer framing and decode any accepted content encoding first. Authorization is checked separately on every attempt, including a replay.

func ResolveObjectAliases

func ResolveObjectAliases(ids []string, aliases map[string]Reference) ([]string, error)

ResolveObjectAliases applies only to grammar positions holding object IDs.

func ResolvePredicateAliases

func ResolvePredicateAliases(raw json.RawMessage, aliases map[string]Reference) (json.RawMessage, error)

ResolvePredicateAliases only traverses predicate grammar. It never examines application JSON/text, annotations, or arbitrary values for '$' spellings.

func SelectProtocolVersion

func SelectProtocolVersion(advertised, supported []string) (string, error)

SelectProtocolVersion returns the numerically greatest common major, independent of array order and machine integer width. No common major is a connection refusal.

func ValidateDeprecatedVersions

func ValidateDeprecatedVersions(versions []string, deprecated []DeprecatedVersion) error

ValidateDeprecatedVersions checks advertised membership, uniqueness and timestamp syntax. Support periods and advance notice require independent operator evidence; neither this validator nor reaching Sunset authorizes retiring a version.

func ValidateIdentifier

func ValidateIdentifier(kind IdentifierKind, id string) error

func ValidateOperationKey

func ValidateOperationKey(key string) error

func ValidateProtocolVersions

func ValidateProtocolVersions(versions []string) error

ValidateProtocolVersions checks a nonempty set of canonical positive decimal majors.

Types

type Aggregate

type Aggregate struct {
	// Grouped distinguishes an ungrouped answer, which omits the member
	// entirely, from the group of rows whose field is null, which reports it
	// as null and counts toward the group limit.
	Grouped bool
	Group   *string
	// GroupReference carries the group of a reference-valued grouping, as the
	// bare qualified identity distinct references already returns. At most one
	// of Group and GroupReference is set; both nil in a grouped answer is the
	// null group, which is a group like any other.
	//
	// One representation of a reference rather than two, because a client that
	// has to decode an identity differently depending on which read produced
	// it is a client with two decoders and one of them under-tested.
	GroupReference *Reference
	RowCount       int
	ValueCount     int
	Sum            json.RawMessage
	Minimum        json.RawMessage
	Maximum        json.RawMessage
}

Aggregate is one row of an aggregation answer.

Sum, Minimum and Maximum are raw JSON numbers rather than float64 so that an integer statistic keeps the spelling the engine computed. A nil member is the specified null, which is not the same answer as zero: it means no row carried a value, and a client that reads it as zero reports an average of nothing.

func (Aggregate) MarshalJSON

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

type AttachmentTarget added in v0.3.0

type AttachmentTarget struct {
	TypeID       string `json:"type_id"`
	CollectionID string `json:"collection_id"`
	ObjectID     string `json:"object_id,omitempty"`
}

AttachmentTarget binds an import to an exact collection insert or one existing record. An empty ObjectID names a collection insert.

func (AttachmentTarget) Validate added in v0.3.0

func (t AttachmentTarget) Validate() error

type DeprecatedVersion

type DeprecatedVersion struct {
	Version string `json:"version"`
	Sunset  string `json:"sunset"`
}

DeprecatedVersion announces a planned retirement, not permission to stop serving. Sunset is a UTC timestamp with seconds, for example 2030-01-01T00:00:00Z.

type Error

type Error struct {
	OperationIndex *int           `json:"operation_index,omitempty"`
	Code           string         `json:"code"`
	Message        string         `json:"message"`
	Details        map[string]any `json:"details,omitempty"`
}

Error contains only protocol-owned diagnostic text. Validation errors never interpolate input values, identifiers, JSON, SQL, or credentials.

func (*Error) Error

func (e *Error) Error() string

type Field

type Field struct {
	ID     string `json:"id"`
	MetaID string `json:"meta_id"`
	Name   string `json:"name"`
	Type   Type   `json:"type"`
	// Scale is the greatest number of fraction digits a value of this field
	// may carry. It is required on a `fixed` field and MUST be absent on every
	// other type, so that a reader never has to decide what scale means for a
	// type that has no fraction.
	//
	// It bounds rather than pads. A canonical fixed value carries no trailing
	// zero, so a scale-2 field stores `1`, `1.5` and `1.25`, and refuses
	// `1.255` — which would have to be rounded, and rounding is the caller's
	// decision rather than the store's.
	Scale *int `json:"scale,omitempty"`
	// Immutable fixes a value once it is written: an update may set the field
	// while it holds nothing, and may restate what it holds, and may do nothing
	// else to it. It constrains update, never deletion, and it is a rule the
	// server applies mechanically — the store still does not know what the
	// field means. `false` and absent are the same, and only `true` is returned.
	Immutable bool `json:"immutable,omitempty"`
	// Creator marks a text field populated by the server from the authenticated
	// principal on insert. A caller cannot supply or change its value.
	Collection  bool                       `json:"collection,omitempty"`
	Creator     bool                       `json:"creator,omitempty"`
	Roles       []string                   `json:"roles,omitempty"`
	Annotations map[string]json.RawMessage `json:"annotations,omitempty"`
}

func (Field) FixedScaleOf

func (f Field) FixedScaleOf() (int, bool)

FixedScaleOf reports the declared scale of a field, and whether it has one.

type IdentifierKind

type IdentifierKind string
const (
	DatasetIdentifier IdentifierKind = "dataset"
	MetaIdentifier    IdentifierKind = "meta"
	FieldIdentifier   IdentifierKind = "field"
	ObjectIdentifier  IdentifierKind = "object"
	ContentIdentifier IdentifierKind = "content"
	AliasIdentifier   IdentifierKind = "alias"
)

type Identity

type Identity struct {
	ID     string `json:"id"`
	MetaID string `json:"meta_id"`
}

type Limits

type Limits struct {
	MaxMaterializedBytes int `json:"max_materialized_bytes,omitempty"`
	// MaxStoredBytes is the dataset's storage quota: the stored bytes past
	// which a write is refused 507 dataset_full. Zero, and absent on the
	// wire, is no quota. It is not the materialization bound, which is how
	// much one operation may hold in memory and is always declared.
	MaxStoredBytes              int `json:"max_stored_bytes,omitempty"`
	MaxAllowedConcreteTypes     int `json:"max_allowed_concrete_types"`
	MaxSelectionPerConcreteType int `json:"max_selection_per_concrete_type"`
	MaxSortTerms                int `json:"max_sort_terms"`
	MaxProjectedFields          int `json:"max_projected_fields"`
	MaxOverallLimit             int `json:"max_overall_limit"`
	MaxPageSize                 int `json:"max_page_size"`
	MaxDistinctReferences       int `json:"max_distinct_references"`
	// MaxAggregateGroups bounds the distinct groups one aggregation may
	// return. Exceeding it refuses the whole request; a prefix returned as if
	// complete is the answer this bound exists to prevent.
	MaxAggregateGroups int `json:"max_aggregate_groups"`
	// MaxTransactionOperations bounds the operations in one transaction. It
	// was enforced before it was declared, and a caller exceeding it got a
	// bare invalid_field naming neither the bound nor the number — a limit a
	// client could only find by being refused.
	MaxTransactionOperations int `json:"max_transaction_operations"`
	MaxPredicateDepth        int `json:"max_predicate_depth"`
	MaxPredicateWidth        int `json:"max_predicate_width"`
	// MaxFixedDigits bounds the significant digits of a `fixed` value, sign
	// and decimal point excluded. It is a value bound rather than a field
	// bound: a field's own `scale` bounds its fraction digits, and this bounds
	// how large the whole number may be whatever the field declares.
	MaxFixedDigits     int `json:"max_fixed_digits"`
	MaxTextBytes       int `json:"max_text_bytes"`
	MaxInlineBlobBytes int `json:"max_inline_blob_bytes"`
	MaxRequestBytes    int `json:"max_request_bytes"`
	MaxScannedRows     int `json:"max_scanned_rows"`
	MaxRoles           int `json:"max_roles"`
	MaxRoleLength      int `json:"max_role_length"`
	// IdempotencyRetentionSeconds is how long a claimed key stays answerable,
	// measured from the claim. It MUST be at least
	// MinimumIdempotencyRetentionSeconds: a window shorter than that leaves a
	// client unable to reconcile a lost response, which is the one thing the
	// key exists to make possible.
	IdempotencyRetentionSeconds int `json:"idempotency_retention_seconds"`
}

Limits are advertised by a server, not inferred by clients from its engine.

func DevelopmentLimits

func DevelopmentLimits() Limits

func MergeLimits

func MergeLimits(base Limits, raw json.RawMessage) (Limits, error)

MergeLimits applies a partial limits object over base: members present in raw replace base's, absent ones keep it, and an unknown member is refused.

Decoding raw straight into a Limits would not do this. Its decoder starts from zero, so every member the override left out would become a bound of nothing — a configuration raising one limit would silently zero the rest.

func (*Limits) UnmarshalJSON

func (l *Limits) UnmarshalJSON(data []byte) error

UnmarshalJSON applies the group bound's default when it is absent.

A dataset stored before `max_aggregate_groups` had a name decodes it as zero, and zero is not a bound of nothing — it is a row written before the bound existed. Reading it as a bound would refuse every grouped aggregation on every dataset this deployment already holds.

It lives here rather than at each decode site because there are two of those today and the next one will not remember.

func (Limits) Validate

func (l Limits) Validate() error

Validate refuses limits a server could not honestly advertise: a bound of zero or less means nothing can be done, and a retention window under the floor voids the reconciliation it exists for.

type Meta

type Meta struct {
	SchemaVersion string                     `json:"schema_version,omitempty"`
	ID            string                     `json:"id"`
	ParentID      *string                    `json:"parent_id"`
	Name          string                     `json:"name"`
	Fields        []Field                    `json:"fields"`
	Roles         []string                   `json:"roles,omitempty"`
	Annotations   map[string]json.RawMessage `json:"annotations,omitempty"`
}

func EffectiveMetadata

func EffectiveMetadata(types map[string]Meta, id string) (Meta, error)

EffectiveMetadata resolves a declared schema without mutating caller-owned slices. The reference implementation's stable order is ancestor-first, then declaration order. The protocol permits other stable field orders.

func (Meta) MarshalJSON

func (m Meta) MarshalJSON() ([]byte, error)

func (Meta) ValidateDeclared

func (m Meta) ValidateDeclared() error

ValidateDeclared validates one type's own fields. Inherited collisions and graph constraints are checked by EffectiveMetadata before publishing a schema.

func (Meta) ValidateSemantics

func (m Meta) ValidateSemantics(limits Limits) error

ValidateSemantics enforces structure only, never any application vocabulary.

type ObjectID

type ObjectID struct {
	ID        string `json:"id"`
	DatasetID string `json:"dataset_id"`
}

type Operation

type Operation struct {
	SchemaVersion string                     `json:"schema_version,omitempty"`
	Op            string                     `json:"op"`
	Type          string                     `json:"type,omitempty"`
	Target        *Target                    `json:"target,omitempty"`
	Fields        map[string]json.RawMessage `json:"fields,omitempty"`
	As            string                     `json:"as,omitempty"`
	Meta          *Meta                      `json:"meta,omitempty"`
	Filter        json.RawMessage            `json:"filter,omitempty"`
	ObjectIDs     []string                   `json:"object_ids,omitempty"`
	Version       *string                    `json:"version,omitempty"`
	Guards        []string                   `json:"guards,omitempty"`
	Cascade       bool                       `json:"cascade,omitempty"`
	Confirm       string                     `json:"confirm,omitempty"`
}

func (Operation) MarshalJSON

func (op Operation) MarshalJSON() ([]byte, error)

func (Operation) ResolveTarget

func (op Operation) ResolveTarget(aliases map[string]Reference) (Target, error)

func (Operation) Values

func (op Operation) Values(aliases map[string]Reference, limits Limits) (map[string]Value, error)

type OperationResult

type OperationResult struct {
	Objects []Identity `json:"objects,omitempty"`
	// RecordSet is the stored row, present only where the caller asked for it
	// with `Prefer: return=representation` and may read what it wrote. It is
	// what a read of the new object would return, produced by the same path,
	// so a client cannot be handed a representation that a subsequent read
	// would contradict.
	//
	// Objects stays whatever this carries. The identity is what every caller
	// of an insert needs and the representation is what some do, and the two
	// are built from one object inside one transaction, so they cannot
	// disagree — which is the objection that would otherwise apply to saying
	// the same thing twice.
	RecordSet      *RecordSet `json:"record_set,omitempty"`
	Affected       *int       `json:"affected,omitempty"`
	Type           *Meta      `json:"type,omitempty"`
	DeletedTypes   []string   `json:"deleted_types,omitempty"`
	DeletedObjects *int       `json:"deleted_objects,omitempty"`
}

type Predicate

type Predicate struct {
	Kind         string
	Field        string
	Exact        *Value
	Lower, Upper *Value
	Text         string
	IDs          []string
	After        string
	Children     []*Predicate
}

Predicate is validated before any row is evaluated, even for empty datasets.

func ParsePredicate

func ParsePredicate(raw []byte, l Limits) (*Predicate, error)

func (*Predicate) Match

func (p *Predicate) Match(id string, fields map[string]Value) (bool, error)

func (*Predicate) Validate

func (p *Predicate) Validate(meta Meta, textMatching bool) error

type Query

type Query struct {
	TypeScope         *TypeScope      `json:"type_scope,omitempty"`
	Filter            json.RawMessage `json:"filter,omitempty"`
	Selection         *Selection      `json:"selection,omitempty"`
	ResultSort        []SortTerm      `json:"result_sort,omitempty"`
	Fields            []string        `json:"fields"`
	MaximumTextLength *int            `json:"maximum_text_length,omitempty"`
	IncludeVersions   bool            `json:"include_versions,omitempty"`
}

type Record

type Record struct {
	ID      ObjectID `json:"id"`
	MetaID  string   `json:"meta_id"`
	Values  []Value  `json:"values"`
	Version string   `json:"version,omitempty"`
}

func (Record) MarshalJSON

func (r Record) MarshalJSON() ([]byte, error)

type RecordSet

type RecordSet struct {
	Meta      Meta     `json:"meta"`
	Records   []Record `json:"records"`
	Watermark string   `json:"watermark"`
}

func (RecordSet) MarshalJSON

func (r RecordSet) MarshalJSON() ([]byte, error)

func (RecordSet) Validate

func (r RecordSet) Validate(dataset string, limits Limits) error

Validate checks the record-set envelope against its projection. Validating concrete-type ancestry additionally requires the server's schema catalogue.

type Reference

type Reference struct {
	DatasetID string `json:"dataset_id"`
	ObjectID  string `json:"object_id"`
	MetaID    string `json:"meta_id"`
}

func (Reference) Validate

func (r Reference) Validate() error

type Selection

type Selection struct {
	Kind      string     `json:"kind"`
	Count     int        `json:"count,omitempty"`
	OrderedBy []SortTerm `json:"ordered_by,omitempty"`
}

type SortTerm

type SortTerm struct {
	FieldID   string `json:"field_id"`
	Ascending bool   `json:"ascending"`
}

type Target

type Target struct {
	Type string `json:"type"`
	ID   string `json:"id"`
}

type Transaction

type Transaction struct {
	Operations []Operation `json:"operations"`
}

func DecodeTransaction

func DecodeTransaction(data []byte, limits Limits) (Transaction, error)

DecodeTransaction validates the entire grammar before execution or a key claim. Field conversion and checks against transaction-local schema happen in order.

type TransactionResult

type TransactionResult struct {
	Results   []OperationResult `json:"results"`
	Watermark string            `json:"watermark"`
}

type Type

type Type string
const (
	NullType      Type = "null"
	IntegerType   Type = "integer"
	FloatType     Type = "float"
	TextType      Type = "text"
	BooleanType   Type = "boolean"
	DateTimeType  Type = "date_time"
	MediaType     Type = "media"
	JSONType      Type = "json"
	BlobType      Type = "blob"
	ReferenceType Type = "reference"
	// FixedType is an exact decimal, carried as a canonical string so it never
	// passes through a binary floating-point reader.
	//
	// It is not spelled `decimal` because a frozen vector case uses that name
	// as its example of a type no server knows, and keeping the name would
	// leave the case passing for a different reason than it was written for.
	// `fixed` names the representation rather than a use: fixed-point against
	// float's floating point. `money` would be a vocabulary, which this
	// protocol does not carry.
	FixedType Type = "fixed"
)

func (Type) ValidFieldType

func (t Type) ValidFieldType() bool

type TypeScope

type TypeScope struct {
	Kind                   string   `json:"kind"`
	AllowedConcreteTypeIDs []string `json:"allowed_concrete_type_ids,omitempty"`
}

type Value

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

Value is an immutable tagged value. Its zero value is null. Numbers are stored in their actual integer/float representation, never in a generic JSON double.

func NewValue

func NewValue(kind Type, payload any) (Value, error)

NewValue validates a payload through the wire contract. Prefer int64 for integer payloads. It never applies conversion to a field's declared type.

func (Value) Convert

func (v Value) Convert(to Type, limits Limits) (Value, error)

Convert implements only the specification's declared-field conversion table.

func (Value) MarshalJSON

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

func (Value) Payload

func (v Value) Payload() any

Payload returns int64, float64, string, bool, Reference, or nil. Blob payloads are canonical base64 strings; JSON payloads are the original JSON text.

func (Value) Type

func (v Value) Type() Type

func (*Value) UnmarshalJSON

func (v *Value) UnmarshalJSON(data []byte) error

func (Value) Validate

func (v Value) Validate(limits Limits) error

Validate enforces deployment-specific payload bounds after syntax decoding.

Jump to

Keyboard shortcuts

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