Documentation
¶
Overview ¶
Package store defines Tessera's authorized transaction boundary. Authentication belongs to the host; current authorization belongs beneath every operation.
A Store serves datasets, plural: every operation names the one it acts on, and how many a given implementation holds is the implementation's business. The durable engines behind pkg/store/sqlite and pkg/store/postgres hold many in one file or database; pkg/store/memory holds one.
Index ¶
- Constants
- func AuthorizeTransaction(tx Tx, t p.Transaction) error
- func Execute(tx Tx, request p.Transaction, dataset string, limits p.Limits) (p.TransactionResult, error)
- func ExecuteWith(tx Tx, request p.Transaction, dataset string, limits p.Limits, ...) (p.TransactionResult, error)
- func Refuse(code string) error
- func RefuseWith(code, reason string, details map[string]any) error
- type Access
- type Authorization
- type Content
- type ContentRead
- type Created
- type Dataset
- type ErasePreview
- type EraseResult
- type Grants
- type Info
- type Lifecycle
- type Mutation
- type ObjectKey
- type PageStart
- type Paging
- type Provisioning
- type ReadOnly
- func (ReadOnly) CreateMetadata(p.Meta) (p.Meta, error)
- func (ReadOnly) Delete(string, string) error
- func (ReadOnly) DeleteMetadata(string, bool) ([]string, int, error)
- func (ReadOnly) Insert(string, map[string]p.Value) (p.RecordSet, error)
- func (ReadOnly) Update(string, string, map[string]p.Value) error
- func (ReadOnly) UpdateMetadata(p.Meta, string) (p.Meta, error)
- type Reader
- type Receipt
- type RecordOnly
- func (RecordOnly) Aggregate(string, string, string, *p.Predicate) ([]p.Aggregate, error)
- func (RecordOnly) DistinctReferences(string, string, int, *p.Predicate) ([]p.Reference, error)
- func (RecordOnly) Matches(string, string, string, *p.Predicate) (bool, error)
- func (RecordOnly) Matching(string, *p.Predicate) ([]ObjectKey, error)
- func (RecordOnly) Query(string, p.Query, int) (p.RecordSet, error)
- type Recovery
- type Reply
- type Represent
- type SchemaOnly
- func (SchemaOnly) Aggregate(string, string, string, *p.Predicate) ([]p.Aggregate, error)
- func (SchemaOnly) DistinctReferences(string, string, int, *p.Predicate) ([]p.Reference, error)
- func (SchemaOnly) Matches(string, string, string, *p.Predicate) (bool, error)
- func (SchemaOnly) Matching(string, *p.Predicate) ([]ObjectKey, error)
- func (SchemaOnly) Object(string, string, []string) (p.RecordSet, error)
- func (SchemaOnly) Query(string, p.Query, int) (p.RecordSet, error)
- type ServerScoped
- type Store
- type Tx
- type Usage
- type UsageReader
- type View
- func (v View) Allows(k ObjectKey) bool
- func (v View) AllowsInsert(id string) bool
- func (v View) AllowsMetadata(id string) bool
- func (v View) AllowsSequence(k ObjectKey, sequence uint64) bool
- func (v View) AllowsType(id string) bool
- func (v View) BeforeSequence(cut uint64) View
- func (v View) Full() bool
- func (v View) MarshalJSON() ([]byte, error)
- func (v View) SequenceCut() uint64
- func (v *View) UnmarshalJSON(raw []byte) error
- func (v View) Valid() bool
- func (v View) WithInserts(types []string) View
Constants ¶
const DefaultServerRetentionSeconds = 604800
DefaultServerRetentionSeconds is the floor this implementation advertises for server-scoped receipts: one week. It is deliberately not a dataset's window. A creation's population is one claim per account plus retries, so storage is not the constraint; what a short window costs is every correct action, since past it :status answers unknown, the contract forbids inferring an outcome from elapsed time, and retrying the key makes a second dataset.
const ServerScope = "*server"
ServerScope is the receipt scope for an operation that belongs to the server rather than to any dataset. Creation is the only one: it has no dataset to be keyed by, and without a scope of its own a caller whose response was lost could not discover whether it made a dataset — which is the one thing this contract never asks of a caller.
It is deliberately not a dataset identity and can never become one: the dataset grammar admits no leading "*", so no minted identity collides with it and no caller can address it as a dataset.
Variables ¶
This section is empty.
Functions ¶
func AuthorizeTransaction ¶
func AuthorizeTransaction(tx Tx, t p.Transaction) error
func Execute ¶
func Execute(tx Tx, request p.Transaction, dataset string, limits p.Limits) (p.TransactionResult, error)
Execute runs inside the caller-owned unit of work. A returned error must abort that unit. The same executor serves HTTP transactions and REST conveniences.
func ExecuteWith ¶
func ExecuteWith(tx Tx, request p.Transaction, dataset string, limits p.Limits, represent Represent) (p.TransactionResult, error)
ExecuteWith is Execute, told whether the caller asked for representations.
Types ¶
type Access ¶
type Access struct{ Principal, Authorization string }
Access identifies both the principal and its particular application grant. It is supplied by trusted authentication code, never decoded from a request.
type Authorization ¶
Grants must be the current intersection of account and application privileges. The host updates or revokes this entry when either privilege set changes.
type ContentRead ¶
type ContentRead struct {
Body io.ReadCloser
Size int64
ContentType string
}
type Created ¶
Created is everything a creation reports: the identity of the dataset that now exists, and whether this answer came from a retained outcome rather than from work done now.
It is deliberately not Info. A replay must be answerable to an owner with no grant on what it made — after the grant was withdrawn, after the dataset was erased — and building an Info would both require reading the dataset and disclose its current bounds, capabilities and versions to a caller whose access has ended. Current information comes from discovery instead.
type Dataset ¶
Dataset is a dataset as the composition that owns the storage sees it, with no caller in the picture. It carries no grants, because there is no caller to hold them.
type ErasePreview ¶
type EraseResult ¶
type Grants ¶
type Grants struct {
ExpiresAt *time.Time `json:"changes_at"`
Read bool `json:"read"`
Write bool `json:"write"`
Schema bool `json:"schema"`
Erase bool `json:"erase"`
// Insert adds records of the types the view names as insertable, and
// nothing else: no read, no update, no delete, and no continuing authority
// over what was added, which belongs to the dataset from the moment it
// commits. It is separate from Write because it is the whole of what a
// contributor holds, and a contributor is not an owner.
Insert bool `json:"insert"`
}
func OperationGrant ¶
type Info ¶
type Info struct {
ID string
// DisplayName is for a person choosing between datasets. It is host-supplied
// and never a record value, so it carries nothing a caller put in a record.
DisplayName string
ReplacedDatasetID string
Capabilities []string
ContentStoreID string
Access Grants
Watermark string
ViewVersion string
SchemaVersion string
Limits p.Limits
}
type Mutation ¶
type Mutation struct {
Dataset, Key string
Digest [32]byte
Transaction p.Transaction
Single bool
SuccessStatus int
// Represent records that the caller asked for the stored row back. It is
// part of the mutation rather than of the request that carried it, because
// a replay must reproduce the original body exactly — a receipt answering
// with less than the caller was given the first time would be a different
// response to the same key.
Represent bool
}
Mutation is a parsed write plus the identity of the original HTTP request. Digest must use the original method, request target and received body bytes.
type PageStart ¶
Paging preserves bounded results and checks the current authorization before returning any page, including a replay.
type Provisioning ¶
type Provisioning interface {
// types, when given, are installed in the same transaction that creates
// the dataset, so a dataset that exists is one that already has them.
CreateDataset(ctx context.Context, a Access, displayName string, limits p.Limits, types []p.Meta, key string, digest [32]byte) (Created, error)
// RecoverCreation answers from a retained claim without making one. It
// exists so a caller can be answered before capability and permission are
// consulted: a receipt's owner outlives its access, and a repeat is a
// question about what already happened rather than a request to do more.
// False means no claim, at which point admitting new work is the question.
RecoverCreation(ctx context.Context, a Access, key string, digest [32]byte) (Created, bool, error)
}
Provisioning is implemented by an engine that can make a dataset that was not there before. It is not part of Store: the contract describes datasets a server already holds and says nothing about how one comes to exist, so an engine may reasonably have no answer. The caller becomes the new dataset's administrator. Passing zero limits takes the storage's.
A non-empty key makes the creation durably idempotent under ServerScope, and binds it to the digest so a replay of a different request is refused rather than answered. An empty key is a creation that cannot be recovered, which is only safe for composition, where there is no response to lose.
type ReadOnly ¶
type ReadOnly struct{ Tx }
ReadOnly wraps a transaction so that every operation which would change the dataset is refused. It is the backstop for the boundary above: the caller decides whether a request is a mutation, and this makes a wrong decision an error the caller sees rather than a write that silently disappears when the read evaluation declines to save its state.
func (ReadOnly) DeleteMetadata ¶
type Reader ¶
type Reader interface {
Read(context.Context, Access, string, func(Tx) error) error
// ReadSchema evaluates a request that reads the schema and no records. It
// is a different question from Read and not a hint about the same one: an
// engine that keeps records in storage need not fetch any of them to
// answer it, and answering it through Read meant decoding every record in
// the dataset to describe a type.
ReadSchema(context.Context, Access, string, func(Tx) error) error
// ReadRecord evaluates a request that reads records by identity and does
// not scan for them. An engine that keeps records in storage fetches the
// ones asked for and no others, so reading one record costs one record.
ReadRecord(context.Context, Access, string, func(Tx) error) error
}
Reader is an engine that can evaluate a request which changes nothing without taking a writer's turn. It is optional: a composition that does not implement it is correct, only slower, and callers fall back to Transact.
The separation is worth stating because it is not an optimisation detail. A read evaluated as a write takes the dataset's write lock, so ordinary reads queue behind each other and behind every writer; and it reads the state in order to hand back a copy nothing will look at again.
type Receipt ¶
type Receipt struct {
Watermark string `json:"watermark,omitempty"`
State string `json:"state"`
Result json.RawMessage `json:"result,omitempty"`
Error json.RawMessage `json:"error,omitempty"`
Objects []p.Reference `json:"objects,omitempty"`
ResultWithheld bool `json:"result_withheld,omitempty"`
}
type RecordOnly ¶
type RecordOnly struct{ ReadOnly }
RecordOnly wraps a transaction that may read records by identity and may not scan for them. The records it was not asked for are not there, so a scan is refused for the reason a schema evaluation refuses a record read: answering "none" would be wrong rather than unavailable.
func (RecordOnly) DistinctReferences ¶
type Recovery ¶
type Recovery interface {
RecoveryLimits() p.Limits
Replay(context.Context, Access, string, string, [32]byte) (Reply, bool, error)
Status(context.Context, Access, string, string) (Receipt, error)
Mutate(context.Context, Access, Mutation) (Reply, error)
}
Recovery is provided by durable engines. Receipt lookups authenticate their owner independently of current dataset grants. False means no retained claim.
type Reply ¶
type Reply struct {
Status int
Body json.RawMessage
Replayed bool
}
func FailureReply ¶
func SuccessReply ¶
type Represent ¶
type Represent bool
Execute runs inside the caller-owned unit of work. A returned error must abort that unit. The same executor serves HTTP transactions and REST conveniences. Represent asks Execute to carry the stored row back with each insert, which a caller requests with `Prefer: return=representation`. It is a preference rather than an instruction: a caller that may write and not read gets the identities alone, because handing a representation to somebody who may not read it would be a disclosure rather than a convenience.
type SchemaOnly ¶
type SchemaOnly struct{ ReadOnly }
SchemaOnly wraps a transaction that may read the schema and nothing else. The records are not there to be read: a schema evaluation need not have fetched any. Refusing is the point — a record read that quietly answered "none" would be a wrong answer rather than an error, and the caller that routed it here is the one that has to hear about it.
func (SchemaOnly) DistinctReferences ¶
type ServerScoped ¶
type ServerScoped interface {
ServerRetentionSeconds() int
}
ServerScoped is implemented by an engine that retains outcomes belonging to no dataset. The window is advertised as a floor and owed on every claim already made, so it is read from the engine rather than configured at the edge.
type Store ¶
type Store interface {
Datasets(context.Context, Access) ([]Info, error)
Info(context.Context, Access, string) (Info, error)
Transact(context.Context, Access, string, func(Tx) error) error
}
Store owns transaction commit and rollback. A callback may compose multiple operations; any error or cancellation aborts all of them. Callbacks must not perform network I/O or reenter Store. Tx must not escape or be used concurrently. Durable engines must reserve receipts before entering this write boundary.
Every operation names the dataset it is for. A server may hold more than one, and an identity naming none that this store serves is absence: not_found, never a report of what else the store holds.
Datasets answers what the caller may use. A dataset the caller holds no grant on is absent from the listing rather than refused within it, so the listing reveals nothing about datasets that are not theirs. An empty listing is not an error.
type Tx ¶
type Tx interface {
DatasetID() string
Metadata(string) (p.Meta, error)
ListMetadata() ([]p.Meta, error)
CreateMetadata(p.Meta) (p.Meta, error)
Query(string, p.Query, int) (p.RecordSet, error)
Object(typeID, objectID string, fields []string) (p.RecordSet, error)
Insert(typeID string, fields map[string]p.Value) (p.RecordSet, error)
Update(typeID, objectID string, fields map[string]p.Value) error
ExpectType(string, string) error
Expect(typeID, objectID string, fields map[string]p.Value, version *string) error
UpdateMetadata(p.Meta, string) (p.Meta, error)
DeleteMetadata(string, bool) ([]string, int, error)
Matching(string, *p.Predicate) ([]ObjectKey, error)
// Aggregate and DistinctReferences are exact-type only and are refused with
// not_supported unless the composition advertises their capability.
Aggregate(typeID, field, groupBy string, filter *p.Predicate) ([]p.Aggregate, error)
DistinctReferences(typeID, field string, maximum int, filter *p.Predicate) ([]p.Reference, error)
Matches(string, string, string, *p.Predicate) (bool, error)
Authorize(Grants) error
ValidateFields(string, map[string]p.Value) error
Watermark() string
Delete(typeID, objectID string) error
}
type Usage ¶
type Usage struct {
Records int64 `json:"records"`
Bytes int64 `json:"bytes"`
// MaxBytes is the dataset's storage quota, absent when it has none: a
// client shows usage alone then, never a dataset nearly full.
MaxBytes *int64 `json:"max_bytes,omitempty"`
}
Usage is how much a dataset holds against the bound that stops it accepting writes, so an administrator can see a dataset filling before it is full rather than learning from a refused write.
type UsageReader ¶
UsageReader measures a dataset for its administrators. Composition only: it takes no access, and the surface calling it decides who may see the answer.
type View ¶
type View struct {
// contains filtered or unexported fields
}
View is a constrained identity set for this first slice. It cannot carry SQL or executable host policy. The zero value is invalid, not unrestricted. Row predicates and policy hooks require a later, explicitly tested extension.
func ObjectView ¶
func SharedView ¶
SharedView grants exact concrete types for queries and explicit identities for direct reads. It never conveys mutation rights or follows object references.
func (View) AllowsInsert ¶
AllowsInsert reports whether an Insert grant reaches this concrete type. A full view does not imply it: insertion is named type by type, and an owner adds records through Write.
func (View) AllowsMetadata ¶
AllowsMetadata includes the types a caller may insert into: writing a record of a type needs its schema, and a schema discloses no record.
func (View) AllowsSequence ¶
AllowsSequence checks a trusted engine's minted sequence, never an identity interpreted by the transport. A cut intersects the existing authorized view.
func (View) AllowsType ¶
func (View) BeforeSequence ¶
BeforeSequence narrows records to those minted before cut. Zero removes the cut; schema and insert scope do not change.
func (View) MarshalJSON ¶
func (View) SequenceCut ¶
func (*View) UnmarshalJSON ¶
func (View) WithInserts ¶
WithInserts is this view, able also to add records of exactly the named concrete types under an Insert grant. It shows nothing more.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package memory provides a synthetic, non-durable evaluator holding exactly one dataset.
|
Package memory provides a synthetic, non-durable evaluator holding exactly one dataset. |
|
Package postgres provides durable storage in a dedicated PostgreSQL database.
|
Package postgres provides durable storage in a dedicated PostgreSQL database. |
|
Package sqlite provides durable SQLite storage.
|
Package sqlite provides durable SQLite storage. |