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
- Variables
- func AtOperation(err error, index int) error
- func CanonicalFixed(literal string) string
- func Compare(a, b Value) (int, error)
- func DatasetCapabilities() []string
- func DecodeRequest(data []byte, dst any, maxBytes int) error
- func Equal(a, b Value) (bool, error)
- func ErrorCode(err error) string
- func ErrorCodes() []string
- func ErrorStatus(code string) int
- func FixedDigits(canonical string) int
- func FixedScale(canonical string) int
- func MarshalWire(value any) ([]byte, error)
- func ParseContentReference(value string) (storeID, itemID string, err error)
- func RequestDigest(method, target string, body []byte) [32]byte
- func ResolveObjectAliases(ids []string, aliases map[string]Reference) ([]string, error)
- func ResolvePredicateAliases(raw json.RawMessage, aliases map[string]Reference) (json.RawMessage, error)
- func SelectProtocolVersion(advertised, supported []string) (string, error)
- func ValidateDeprecatedVersions(versions []string, deprecated []DeprecatedVersion) error
- func ValidateIdentifier(kind IdentifierKind, id string) error
- func ValidateOperationKey(key string) error
- func ValidateProtocolVersions(versions []string) error
- type Aggregate
- type AttachmentTarget
- type DeprecatedVersion
- type Error
- type Field
- type IdentifierKind
- type Identity
- type Limits
- type Meta
- type ObjectID
- type Operation
- type OperationResult
- type Predicate
- type Query
- type Record
- type RecordSet
- type Reference
- type Selection
- type SortTerm
- type Target
- type Transaction
- type TransactionResult
- type Type
- type TypeScope
- type Value
Constants ¶
const CollectionAccessCapability = "collection_access"
const CreatorAttributionCapability = "creator_attribution"
CreatorAttributionCapability enables server-populated creator fields.
const CreatorIDBytes = 32
CreatorIDBytes is the encoded size of a dataset-scoped creator ID.
const CreatorScopedMutationsCapability = "creator_scoped_mutations"
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.
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.
const DefaultTransactionOperations = 256
DefaultTransactionOperations is the operation bound where a dataset declares none, and the value that was enforced before it was declared.
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.
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.
SharedReadsCapability says that an authorization request here may ask to read everything shared with the account instead of naming a dataset.
Variables ¶
var ErrNoCommonProtocolVersion = errors.New("no common protocol version")
ErrNoCommonProtocolVersion means negotiation cannot establish a connection.
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.
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.
var ServerCapabilities = []string{DatasetCreationCapability, SharedReadsCapability}
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 CanonicalFixed ¶
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 ¶
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 ¶
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 ¶
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 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 FixedDigits ¶
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 ¶
FixedScale is the number of fraction digits a canonical fixed value carries.
func MarshalWire ¶
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 ¶
ParseContentReference accepts only a qualified store//item reference. A caller resolves the named store, never the dataset's current replacement.
func RequestDigest ¶
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 ¶
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 ¶
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 ValidateProtocolVersions ¶
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 ¶
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 ¶
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.
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 ¶
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 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 ¶
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.
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 ¶
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 (Meta) ValidateDeclared ¶
ValidateDeclared validates one type's own fields. Inherited collisions and graph constraints are checked by EffectiveMetadata before publishing a schema.
func (Meta) ValidateSemantics ¶
ValidateSemantics enforces structure only, never any application vocabulary.
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 (Operation) ResolveTarget ¶
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.
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 ¶
type RecordSet ¶
type RecordSet struct {
Meta Meta `json:"meta"`
Records []Record `json:"records"`
Watermark string `json:"watermark"`
}
func (RecordSet) MarshalJSON ¶
type Reference ¶
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 ¶
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 ¶
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) MarshalJSON ¶
func (Value) Payload ¶
Payload returns int64, float64, string, bool, Reference, or nil. Blob payloads are canonical base64 strings; JSON payloads are the original JSON text.