Documentation
¶
Overview ¶
Package integrity provides typed storage with built-in data integrity protection. It automatically computes and verifies hashes and signatures for stored values.
See TypedBuilder for configuration options and Typed for available operations.
Index ¶
- Constants
- Variables
- func IgnoreMoreThanOneResult() options.OptionCallback[getOptions]
- func IgnoreVerificationError() options.OptionCallback[getOptions]
- func WithDeletePredicates(predicates ...Predicate) options.OptionCallback[deleteOptions]
- func WithPrefix() options.OptionCallback[deleteOptions]
- func WithPutPredicates(predicates ...Predicate) options.OptionCallback[putOptions]
- type Branch
- type Branchable
- type Codec
- func (c *Codec[T]) Bind(s storage.Storage) *Store[T]
- func (c *Codec[T]) BindPredicate(name string, pred Predicate) (predicate.Predicate, error)
- func (c *Codec[T]) BindSingleton(s storage.Storage, name string) (*SingletonStore[T], error)
- func (c *Codec[T]) FullKeys(name string) ([]string, error)
- func (c *Codec[T]) TxDelete(b Branchable, name string, opts ...options.OptionCallback[deleteOptions]) error
- func (c *Codec[T]) TxGet(b Branchable, name string, opts ...options.OptionCallback[getOptions]) *GetFuture[T]
- func (c *Codec[T]) TxPut(b Branchable, name string, value T) error
- func (c *Codec[T]) TxRange(b Branchable, name string, opts ...options.OptionCallback[getOptions]) *RangeFuture[T]
- func (c *Codec[T]) ValueEqual(value T) (Predicate, error)
- func (c *Codec[T]) ValueKey(name string) (string, error)
- func (c *Codec[T]) ValueNotEqual(value T) (Predicate, error)
- func (c *Codec[T]) VersionEqual(v int64) Predicate
- func (c *Codec[T]) VersionGreater(v int64) Predicate
- func (c *Codec[T]) VersionLess(v int64) Predicate
- func (c *Codec[T]) VersionNotEqual(v int64) Predicate
- type CodecBuilder
- func (b CodecBuilder[T]) Build() (*Codec[T], error)
- func (b CodecBuilder[T]) WithHashLocation(hasherName, loc string) CodecBuilder[T]
- func (b CodecBuilder[T]) WithHasher(h hasher.Hasher) CodecBuilder[T]
- func (b CodecBuilder[T]) WithKeyPrefix(prefix string) CodecBuilder[T]
- func (b CodecBuilder[T]) WithMarshaller(m marshaller.TypedMarshaller[T]) CodecBuilder[T]
- func (b CodecBuilder[T]) WithNamer(f CodecNamerConstructor) CodecBuilder[T]
- func (b CodecBuilder[T]) WithObjectLocation(loc string) CodecBuilder[T]
- func (b CodecBuilder[T]) WithSignatureLocation(signerName, loc string) CodecBuilder[T]
- func (b CodecBuilder[T]) WithSigner(s crypto.Signer) CodecBuilder[T]
- func (b CodecBuilder[T]) WithSignerVerifier(sv crypto.SignerVerifier) CodecBuilder[T]
- func (b CodecBuilder[T]) WithSingleHashCompact() CodecBuilder[T]
- func (b CodecBuilder[T]) WithSingleSigCompact() CodecBuilder[T]
- func (b CodecBuilder[T]) WithVerifier(v crypto.Verifier) CodecBuilder[T]
- type CodecNamerConstructor
- type FailedToComputeHashError
- type FailedToGenerateKeysError
- type FailedToGenerateSignatureError
- type FailedToMarshalValueError
- type FailedToValidateAggregatedError
- type Generator
- type GetFuture
- type ImpossibleError
- type InvalidNameError
- type NamerConstructor
- type Predicate
- type RangeFuture
- type Response
- type SingletonStore
- func (s *SingletonStore[T]) BindPredicate(pred Predicate) (predicate.Predicate, error)
- func (s *SingletonStore[T]) Delete(ctx context.Context, opts ...options.OptionCallback[deleteOptions]) error
- func (s *SingletonStore[T]) FullKeys() ([]string, error)
- func (s *SingletonStore[T]) Get(ctx context.Context, opts ...options.OptionCallback[getOptions]) (ValidatedResult[T], error)
- func (s *SingletonStore[T]) Put(ctx context.Context, value T, opts ...options.OptionCallback[putOptions]) error
- func (s *SingletonStore[T]) TxDelete(b Branchable, opts ...options.OptionCallback[deleteOptions]) error
- func (s *SingletonStore[T]) TxGet(b Branchable, opts ...options.OptionCallback[getOptions]) *GetFuture[T]
- func (s *SingletonStore[T]) TxPut(b Branchable, value T) error
- func (s *SingletonStore[T]) ValueKey() (string, error)
- func (s *SingletonStore[T]) Watch(ctx context.Context) (<-chan watch.Event, error)
- type Store
- func (s *Store[T]) Delete(ctx context.Context, name string, ...) error
- func (s *Store[T]) FullKeys(name string) ([]string, error)
- func (s *Store[T]) Get(ctx context.Context, name string, opts ...options.OptionCallback[getOptions]) (ValidatedResult[T], error)
- func (s *Store[T]) Put(ctx context.Context, name string, value T, ...) error
- func (s *Store[T]) Range(ctx context.Context, name string, opts ...options.OptionCallback[getOptions]) ([]ValidatedResult[T], error)
- func (s *Store[T]) ValueKey(name string) (string, error)
- func (s *Store[T]) Watch(ctx context.Context, name string) (<-chan watch.Event, error)
- type Tx
- type Typed
- func (t *Typed[T]) Delete(ctx context.Context, name string, ...) error
- func (t *Typed[T]) Get(ctx context.Context, name string, vOpts ...options.OptionCallback[getOptions]) (ValidatedResult[T], error)
- func (t *Typed[T]) Put(ctx context.Context, name string, val T, ...) error
- func (t *Typed[T]) Range(ctx context.Context, name string, vOpts ...options.OptionCallback[getOptions]) ([]ValidatedResult[T], error)
- func (t *Typed[T]) ValueEqual(value T) (Predicate, error)
- func (t *Typed[T]) ValueNotEqual(value T) (Predicate, error)
- func (t *Typed[T]) VersionEqual(value int64) Predicate
- func (t *Typed[T]) VersionGreater(value int64) Predicate
- func (t *Typed[T]) VersionLess(value int64) Predicate
- func (t *Typed[T]) VersionNotEqual(value int64) Predicate
- func (t *Typed[T]) Watch(ctx context.Context, name string) (<-chan watch.Event, error)
- type TypedBuilder
- func (s TypedBuilder[T]) Build() *Typed[T]
- func (s TypedBuilder[T]) WithHasher(h hasher.Hasher) TypedBuilder[T]
- func (s TypedBuilder[T]) WithMarshaller(marshaller marshaller.TypedMarshaller[T]) TypedBuilder[T]
- func (s TypedBuilder[T]) WithNamer(namerFunc NamerConstructor) TypedBuilder[T]
- func (s TypedBuilder[T]) WithPrefix(prefix string) TypedBuilder[T]
- func (s TypedBuilder[T]) WithSigner(signer crypto.Signer) TypedBuilder[T]
- func (s TypedBuilder[T]) WithSignerVerifier(sv crypto.SignerVerifier) TypedBuilder[T]
- func (s TypedBuilder[T]) WithVerifier(verifier crypto.Verifier) TypedBuilder[T]
- type ValidatedResult
- type ValidationError
- type Validator
Examples ¶
Constants ¶
const ModRevisionEmpty = 0
ModRevisionEmpty is used to initialize the ModRevision field by default.
Variables ¶
var ( ErrBranchNotFired = errors.New("integrity: transaction branch did not fire") ErrTxNotCommitted = errors.New("integrity: transaction has not been committed") ErrTxAlreadyCommitted = errors.New("integrity: transaction has already been committed") )
Sentinel errors for Tx operations.
var ( ErrNotFound = errors.New("not found") ErrMoreThanOneResult = errors.New("more than one result was returned") ErrInvalidPredicateValueType = errors.New("invalid predicate value type") ErrNoValueKey = errors.New("no value key found in generated keys") // ErrPredicateFailed is returned by Put or Delete when predicates are specified // but the transaction predicate check fails (i.e., the conditions are not met). // Use [WithPutPredicates] or [WithDeletePredicates] to specify predicates. ErrPredicateFailed = errors.New("predicate check failed") )
var ErrInvalidName = InvalidNameError{/* contains filtered or unexported fields */}
ErrInvalidName is a sentinel error for invalid names.
var ErrSingleHashCompactCardinality = errors.New(
"codec: WithSingleHashCompact requires exactly one hasher")
ErrSingleHashCompactCardinality is returned when WithSingleHashCompact is set but the codec is not configured with exactly one hasher.
var ErrSingleSigCompactCardinality = errors.New(
"codec: WithSingleSigCompact requires exactly one unique signer/verifier")
ErrSingleSigCompactCardinality is returned when WithSingleSigCompact is set but the union of signers and verifiers (deduped by name) is not of size one.
var ErrUnknownHasherLocation = errors.New("codec: WithHashLocation key does not match any configured hasher")
ErrUnknownHasherLocation is returned when a WithHashLocation key does not match any configured hasher.
var ErrUnknownSignerLocation = errors.New(
"codec: WithSignatureLocation key does not match any configured signer or verifier")
ErrUnknownSignerLocation is returned when a WithSignatureLocation key does not match any configured signer or verifier.
Functions ¶
func IgnoreMoreThanOneResult ¶
func IgnoreMoreThanOneResult() options.OptionCallback[getOptions]
IgnoreMoreThanOneResult returns an option that allows Get operation to succeed when multiple results are returned for a single name. By default, Get returns ErrMoreThanOneResult in such cases.
func IgnoreVerificationError ¶
func IgnoreVerificationError() options.OptionCallback[getOptions]
IgnoreVerificationError returns an option that allows Get and Range operations to return results even if hash or signature verification fails. The returned result will still contain the Error field with verification details.
func WithDeletePredicates ¶ added in v1.1.0
func WithDeletePredicates(predicates ...Predicate) options.OptionCallback[deleteOptions]
WithDeletePredicates configures predicates for conditional Delete operations. The Delete operation will only succeed if all predicates evaluate to true. If predicates are specified but fail, ErrPredicateFailed is returned.
func WithPrefix ¶ added in v1.1.0
func WithPrefix() options.OptionCallback[deleteOptions]
WithPrefix configures the ability to delete keys by a prefix.
func WithPutPredicates ¶ added in v1.1.0
func WithPutPredicates(predicates ...Predicate) options.OptionCallback[putOptions]
WithPutPredicates configures predicates for conditional Put operations. The Put operation will only succeed if all predicates evaluate to true. If predicates are specified but fail, ErrPredicateFailed is returned.
Types ¶
type Branch ¶ added in v1.3.0
type Branch struct {
// contains filtered or unexported fields
}
Branch holds a list of operations and spans for one side of an If/Then/Else. Branch is not goroutine-safe.
type Branchable ¶ added in v1.3.0
type Branchable interface {
// contains filtered or unexported methods
}
Branchable is satisfied by both *Tx (which routes to thenBranch) and *Branch (which routes to itself). The method is unexported so only types in this package can satisfy the interface.
type Codec ¶ added in v1.3.0
type Codec[T any] struct { // contains filtered or unexported fields }
Codec holds the integrity schema for type T: marshaller, hashers, signers/verifiers, and three namer instances (generator, validator, and a top-level union). It carries no storage handle and no namespace prefix — namespace scoping is the storage layer's concern.
func (*Codec[T]) Bind ¶ added in v1.3.0
Bind creates a new Store[T] by binding c to the given storage. Bind is a cheap struct literal — no caching, no validation.
func (*Codec[T]) BindPredicate ¶ added in v1.3.0
BindPredicate resolves the value-layer key for name and calls p with it, returning the concrete predicate.Predicate ready for use in Tx.If.
Returns ErrInvalidName for empty, leading-slash, or trailing-slash names — the same rule applied by Put/Delete/Get/Watch — and ErrNoValueKey if the namer emits no value-layer key.
func (*Codec[T]) BindSingleton ¶ added in v1.4.0
BindSingleton binds c to s and bakes name into every operation of the returned SingletonStore[T]. The name is validated eagerly with the same rules as Store[T] operations; an invalid name returns ErrInvalidName.
Example ¶
ExampleCodec_BindSingleton shows the canonical fixed-key configuration pattern: a single object at "/settings/auth" instead of a directory of "/settings/auth/<id>" items. The bound name is supplied once; operations take no name parameter.
package main
import (
"context"
"fmt"
"log"
storage "github.com/tarantool/go-storage"
"github.com/tarantool/go-storage/driver/dummy"
"github.com/tarantool/go-storage/hasher"
"github.com/tarantool/go-storage/integrity"
)
type authConfig struct {
Issuer string `yaml:"issuer"`
}
// ExampleCodec_BindSingleton shows the canonical fixed-key configuration
// pattern: a single object at "/settings/auth" instead of a directory of
// "/settings/auth/<id>" items. The bound name is supplied once; operations
// take no name parameter.
func main() {
ctx := context.Background()
codec, err := integrity.NewCodecBuilder[authConfig]().
WithObjectLocation("settings").
WithHasher(hasher.NewSHA256Hasher()).
Build()
if err != nil {
log.Fatalf("build codec: %v", err)
}
auth, err := codec.BindSingleton(storage.NewStorage(dummy.New()), "auth")
if err != nil {
log.Fatalf("bind singleton: %v", err)
}
err = auth.Put(ctx, authConfig{Issuer: "tarantool"})
if err != nil {
log.Fatalf("put: %v", err)
}
res, err := auth.Get(ctx)
if err != nil {
log.Fatalf("get: %v", err)
}
fmt.Println("issuer:", res.Value.Unwrap().Issuer)
}
Output: issuer: tarantool
func (*Codec[T]) FullKeys ¶ added in v1.6.0
FullKeys returns every key the codec's namer would emit for name — the value-layer key plus one key per configured hash and signature layer. The order matches namer.Namer.GenerateNames: value first, then hashes, then signatures.
Returned keys are namer-relative — use Store.FullKeys for keys that include any storage.Prefixed wrapper's prefix.
Returns ErrInvalidName for empty, leading-slash, or trailing-slash names.
func (*Codec[T]) TxDelete ¶ added in v1.3.0
func (c *Codec[T]) TxDelete( b Branchable, name string, opts ...options.OptionCallback[deleteOptions], ) error
TxDelete enqueues Delete operations for name onto branch b.
Example ¶
ExampleCodec_TxDelete enqueues a Delete onto the Then branch and removes every key that belongs to the named value (value plus all hash/sig keys).
package main
import (
"context"
"errors"
"fmt"
"log"
storage "github.com/tarantool/go-storage"
"github.com/tarantool/go-storage/driver/dummy"
"github.com/tarantool/go-storage/hasher"
"github.com/tarantool/go-storage/integrity"
)
type txExampleValue struct {
Field string `yaml:"field"`
}
func newTxExampleCodec() *integrity.Codec[txExampleValue] {
codec, err := integrity.NewCodecBuilder[txExampleValue]().WithObjectLocation("objects").
WithHasher(hasher.NewSHA256Hasher()).
Build()
if err != nil {
log.Fatalf("build codec: %v", err)
}
return codec
}
func main() {
ctx := context.Background()
store := storage.NewStorage(dummy.New())
codec := newTxExampleCodec()
seedTx := integrity.NewTx(store)
err := codec.TxPut(seedTx, "victim", txExampleValue{Field: "x"})
if err != nil {
log.Fatalf("seed: %v", err)
}
_, err = seedTx.Commit(ctx)
if err != nil {
log.Fatalf("seed commit: %v", err)
}
delTx := integrity.NewTx(store)
err = codec.TxDelete(delTx, "victim")
if err != nil {
log.Fatalf("delete: %v", err)
}
_, err = delTx.Commit(ctx)
if err != nil {
log.Fatalf("delete commit: %v", err)
}
getTx := integrity.NewTx(store)
fut := codec.TxGet(getTx, "victim")
_, err = getTx.Commit(ctx)
if err != nil {
log.Fatalf("get commit: %v", err)
}
_, err = fut.Result()
fmt.Println("not found:", errors.Is(err, integrity.ErrNotFound))
}
Output: not found: true
func (*Codec[T]) TxGet ¶ added in v1.3.0
func (c *Codec[T]) TxGet( b Branchable, name string, opts ...options.OptionCallback[getOptions], ) *GetFuture[T]
TxGet enqueues a Get operation for name onto branch b. Returns a *GetFuture[T] whose Result() is populated after Commit.
func (*Codec[T]) TxPut ¶ added in v1.3.0
func (c *Codec[T]) TxPut(b Branchable, name string, value T) error
TxPut enqueues Put operations for name/value onto branch b. Returns the first error encountered during build (if any); subsequent enqueue calls after an error are no-ops returning nil.
func (*Codec[T]) TxRange ¶ added in v1.3.0
func (c *Codec[T]) TxRange( b Branchable, name string, opts ...options.OptionCallback[getOptions], ) *RangeFuture[T]
TxRange enqueues a range-Get operation for name onto branch b. name == "" fetches everything under the object location.
func (*Codec[T]) ValueEqual ¶ added in v1.3.0
ValueEqual creates a predicate that checks if a key's value equals the specified value.
Example ¶
ExampleCodec_ValueEqual produces a Codec-bound predicate that can be passed to Tx.If via Codec.BindPredicate, or used through Store helpers like WithPutPredicates.
package main
import (
"fmt"
"log"
"github.com/tarantool/go-storage/integrity"
)
type codecExampleConfig struct {
Name string `yaml:"name"`
N int `yaml:"n"`
}
func main() {
codec, err := integrity.NewCodecBuilder[codecExampleConfig]().WithObjectLocation("objects").Build()
if err != nil {
log.Fatalf("build: %v", err)
}
pred, err := codec.ValueEqual(codecExampleConfig{Name: "alice", N: 1})
if err != nil {
log.Fatalf("predicate: %v", err)
}
bound, err := codec.BindPredicate("alice", pred)
if err != nil {
log.Fatalf("bind: %v", err)
}
// The bound predicate targets the value-layer key for "alice".
fmt.Printf("predicate key: %s\n", bound.Key())
}
Output: predicate key: /objects/alice
func (*Codec[T]) ValueKey ¶ added in v1.6.0
ValueKey returns the value-layer key for name as produced by the codec's namer — e.g. "/objects/<name>" for a codec built with WithObjectLocation("objects"), or "/<name>" for an unnamed codec.
The returned key is namer-relative: it does NOT include any prefix added by a storage.Prefixed wrapper — for the on-disk key, use Store.ValueKey. Hash and signature keys are not included; use Codec.FullKeys for those.
Returns ErrInvalidName for empty, leading-slash, or trailing-slash names, and ErrNoValueKey if the namer emits no value-layer key.
func (*Codec[T]) ValueNotEqual ¶ added in v1.3.0
ValueNotEqual creates a predicate that checks if a key's value is not equal to the specified value.
func (*Codec[T]) VersionEqual ¶ added in v1.3.0
VersionEqual creates a predicate that checks if a key's version equals the specified version.
func (*Codec[T]) VersionGreater ¶ added in v1.3.0
VersionGreater creates a predicate that checks if a key's version is greater than the specified version.
func (*Codec[T]) VersionLess ¶ added in v1.3.0
VersionLess creates a predicate that checks if a key's version is less than the specified version.
func (*Codec[T]) VersionNotEqual ¶ added in v1.3.0
VersionNotEqual creates a predicate that checks if a key's version is not equal to the specified version.
type CodecBuilder ¶ added in v1.3.0
type CodecBuilder[T any] struct { // contains filtered or unexported fields }
CodecBuilder[T] is a fluent, value-receiver builder for Codec[T]. Each setter returns a copy of the builder (copy-on-write), so the original builder is not mutated.
func NewCodecBuilder ¶ added in v1.3.0
func NewCodecBuilder[T any]() CodecBuilder[T]
NewCodecBuilder returns a new CodecBuilder with sensible defaults. Default marshaller: TypedYamlMarshaller[T]. Default objectLocation: namer.ObjectLocationMissing (unnamed codec — keys are emitted without the per-codec location segment). Call WithObjectLocation to opt into a named layout like /<location>/<name>. Default namerFunc: wraps namer.NewLayeredNamer.
Example ¶
ExampleNewCodecBuilder builds a Codec[T] using the fluent builder. Each WithX call returns a copy, so the original builder is never mutated.
package main
import (
"context"
"fmt"
"log"
storage "github.com/tarantool/go-storage"
"github.com/tarantool/go-storage/driver/dummy"
"github.com/tarantool/go-storage/hasher"
"github.com/tarantool/go-storage/integrity"
)
type codecExampleConfig struct {
Name string `yaml:"name"`
N int `yaml:"n"`
}
func main() {
codec, err := integrity.NewCodecBuilder[codecExampleConfig]().WithObjectLocation("objects").
WithHasher(hasher.NewSHA256Hasher()).
Build()
if err != nil {
log.Fatalf("build codec: %v", err)
}
store := codec.Bind(storage.NewStorage(dummy.New()))
ctx := context.Background()
err = store.Put(ctx, "alice", codecExampleConfig{Name: "alice", N: 42})
if err != nil {
log.Fatalf("put: %v", err)
}
res, err := store.Get(ctx, "alice")
if err != nil {
log.Fatalf("get: %v", err)
}
got := res.Value.Unwrap()
fmt.Printf("Name=%s N=%d\n", got.Name, got.N)
}
Output: Name=alice N=42
Example (Immutability) ¶
ExampleNewCodecBuilder_immutability demonstrates copy-on-write: the original builder is unchanged after a setter call, so two divergent codecs can be built from the same base.
package main
import (
"fmt"
"log"
"github.com/tarantool/go-storage/hasher"
"github.com/tarantool/go-storage/integrity"
)
type codecExampleConfig struct {
Name string `yaml:"name"`
N int `yaml:"n"`
}
func main() {
base := integrity.NewCodecBuilder[codecExampleConfig]().WithObjectLocation("objects")
withHasher := base.WithHasher(hasher.NewSHA256Hasher())
withoutHasher := base // unchanged.
codecHashed, err := withHasher.Build()
if err != nil {
log.Fatalf("codecHashed: %v", err)
}
codecPlain, err := withoutHasher.Build()
if err != nil {
log.Fatalf("codecPlain: %v", err)
}
fmt.Println("two distinct codecs:", codecHashed != codecPlain)
}
Output: two distinct codecs: true
func (CodecBuilder[T]) Build ¶ added in v1.3.0
func (b CodecBuilder[T]) Build() (*Codec[T], error)
Build constructs a *Codec[T] from the current builder state.
It returns an error if any location segment is invalid, if any pair of segments collides, or if a WithHashLocation/WithSignatureLocation key does not match a configured hasher/signer/verifier (catches typos that would otherwise be silently ignored).
func (CodecBuilder[T]) WithHashLocation ¶ added in v1.3.0
func (b CodecBuilder[T]) WithHashLocation(hasherName, loc string) CodecBuilder[T]
WithHashLocation overrides the location segment for the named hasher. hasherName must match the hasher's Name() method. If not called, the location defaults to the hasher's Name().
func (CodecBuilder[T]) WithHasher ¶ added in v1.3.0
func (b CodecBuilder[T]) WithHasher(h hasher.Hasher) CodecBuilder[T]
WithHasher adds a hasher to the codec.
func (CodecBuilder[T]) WithKeyPrefix ¶ added in v1.6.0
func (b CodecBuilder[T]) WithKeyPrefix(prefix string) CodecBuilder[T]
WithKeyPrefix prepends the given path prefix to every key the codec emits and parses. This lets multiple codecs share a single storage.Storage while keeping their keys in disjoint sub-trees — and therefore stay atomic in a single integrity.Tx, which cannot span multiple storage handles.
The prefix must start with '/' and must not end with '/'; empty is a no-op. The validation runs in Build via the underlying namer, which returns namer.ErrKeyPrefixNoLeadingSlash or namer.ErrKeyPrefixTrailingSlash on malformed input.
Custom namers passed through WithNamer receive the prefix as an extra namer.LayeredOption. Namers that ignore LayeredOptions silently drop the prefix; the default namer.NewLayeredNamer honours it.
func (CodecBuilder[T]) WithMarshaller ¶ added in v1.3.0
func (b CodecBuilder[T]) WithMarshaller(m marshaller.TypedMarshaller[T]) CodecBuilder[T]
WithMarshaller sets a custom marshaller.
func (CodecBuilder[T]) WithNamer ¶ added in v1.3.0
func (b CodecBuilder[T]) WithNamer(f CodecNamerConstructor) CodecBuilder[T]
WithNamer sets a custom CodecNamerConstructor.
func (CodecBuilder[T]) WithObjectLocation ¶ added in v1.3.0
func (b CodecBuilder[T]) WithObjectLocation(loc string) CodecBuilder[T]
WithObjectLocation sets the location segment for value keys. If not called, the codec is built in unnamed mode (no per-codec location segment); see NewCodecBuilder.
Example ¶
ExampleCodecBuilder_WithObjectLocation shows overriding the value-layer location segment. The default is "objects".
package main
import (
"context"
"fmt"
"log"
storage "github.com/tarantool/go-storage"
"github.com/tarantool/go-storage/driver/dummy"
"github.com/tarantool/go-storage/hasher"
"github.com/tarantool/go-storage/integrity"
)
type codecExampleConfig struct {
Name string `yaml:"name"`
N int `yaml:"n"`
}
func main() {
codec, err := integrity.NewCodecBuilder[codecExampleConfig]().WithObjectLocation("objects").
WithObjectLocation("users").
WithHasher(hasher.NewSHA256Hasher()).
Build()
if err != nil {
log.Fatalf("build codec: %v", err)
}
store := codec.Bind(storage.NewStorage(dummy.New()))
ctx := context.Background()
err = store.Put(ctx, "alice", codecExampleConfig{Name: "alice", N: 1})
if err != nil {
log.Fatalf("put: %v", err)
}
// The value lands under /users/alice instead of /objects/alice.
res, err := store.Get(ctx, "alice")
if err != nil {
log.Fatalf("get: %v", err)
}
fmt.Println("name:", res.Value.Unwrap().Name)
}
Output: name: alice
func (CodecBuilder[T]) WithSignatureLocation ¶ added in v1.3.0
func (b CodecBuilder[T]) WithSignatureLocation(signerName, loc string) CodecBuilder[T]
WithSignatureLocation overrides the location segment for the named signer/verifier. signerName must match the signer's or verifier's Name() method. If not called, the location defaults to the signer's Name().
func (CodecBuilder[T]) WithSigner ¶ added in v1.3.0
func (b CodecBuilder[T]) WithSigner(s crypto.Signer) CodecBuilder[T]
WithSigner adds a signer to the codec.
func (CodecBuilder[T]) WithSignerVerifier ¶ added in v1.3.0
func (b CodecBuilder[T]) WithSignerVerifier(sv crypto.SignerVerifier) CodecBuilder[T]
WithSignerVerifier adds a combined signer/verifier to both the signer and verifier lists.
Example ¶
ExampleCodecBuilder_WithSignerVerifier configures a codec with both a hasher and an RSA-PSS signer/verifier. Put writes value, hash, and signature keys; Get verifies all of them on read.
package main
import (
"context"
"crypto/rand"
"crypto/rsa"
"fmt"
"log"
storage "github.com/tarantool/go-storage"
"github.com/tarantool/go-storage/crypto"
"github.com/tarantool/go-storage/driver/dummy"
"github.com/tarantool/go-storage/hasher"
"github.com/tarantool/go-storage/integrity"
)
type codecExampleConfig struct {
Name string `yaml:"name"`
N int `yaml:"n"`
}
func main() {
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
log.Fatalf("genkey: %v", err)
}
codec, err := integrity.NewCodecBuilder[codecExampleConfig]().WithObjectLocation("objects").
WithHasher(hasher.NewSHA256Hasher()).
WithSignerVerifier(crypto.NewRSAPSSSignerVerifier(*priv)).
Build()
if err != nil {
log.Fatalf("build codec: %v", err)
}
store := codec.Bind(storage.NewStorage(dummy.New()))
ctx := context.Background()
err = store.Put(ctx, "alice", codecExampleConfig{Name: "alice", N: 7})
if err != nil {
log.Fatalf("put: %v", err)
}
res, err := store.Get(ctx, "alice")
if err != nil {
log.Fatalf("get: %v", err)
}
fmt.Printf("verified Name=%s N=%d\n", res.Value.Unwrap().Name, res.Value.Unwrap().N)
}
Output: verified Name=alice N=7
func (CodecBuilder[T]) WithSingleHashCompact ¶ added in v1.3.0
func (b CodecBuilder[T]) WithSingleHashCompact() CodecBuilder[T]
WithSingleHashCompact opts the codec into the compact hash key layout (/hashes/<objectLocation>/<name>, dropping the per-hasher segment). Build returns ErrSingleHashCompactCardinality if exactly one hasher is not configured.
func (CodecBuilder[T]) WithSingleSigCompact ¶ added in v1.3.0
func (b CodecBuilder[T]) WithSingleSigCompact() CodecBuilder[T]
WithSingleSigCompact opts the codec into the compact sig key layout (/sig/<objectLocation>/<name>, dropping the per-signer segment). Build returns ErrSingleSigCompactCardinality if the union of signers and verifiers (deduped by name) is not of size one.
func (CodecBuilder[T]) WithVerifier ¶ added in v1.3.0
func (b CodecBuilder[T]) WithVerifier(v crypto.Verifier) CodecBuilder[T]
WithVerifier adds a verifier to the codec.
type CodecNamerConstructor ¶ added in v1.3.0
type CodecNamerConstructor func( objectLocation string, hashLocations []namer.LayeredHashLocation, sigLocations []namer.LayeredSigLocation, opts ...namer.LayeredOption, ) (namer.Namer, error)
CodecNamerConstructor builds a namer for a Codec given the resolved object/hash/sig location bindings and any LayeredOptions threaded through from the builder (e.g. compact-mode flags).
type FailedToComputeHashError ¶
type FailedToComputeHashError struct {
// contains filtered or unexported fields
}
FailedToComputeHashError represents an error when hash computation fails.
func (FailedToComputeHashError) Error ¶
func (e FailedToComputeHashError) Error() string
Error returns a string representation of the hash computation error.
func (FailedToComputeHashError) Unwrap ¶
func (e FailedToComputeHashError) Unwrap() error
Unwrap returns the underlying error that caused the hash computation failure.
type FailedToGenerateKeysError ¶
type FailedToGenerateKeysError struct {
// contains filtered or unexported fields
}
FailedToGenerateKeysError represents an error when key generation fails.
func (FailedToGenerateKeysError) Error ¶
func (e FailedToGenerateKeysError) Error() string
Error returns a string representation of the key generation error.
func (FailedToGenerateKeysError) Unwrap ¶
func (e FailedToGenerateKeysError) Unwrap() error
Unwrap returns the underlying error that caused the key generation failure.
type FailedToGenerateSignatureError ¶
type FailedToGenerateSignatureError struct {
// contains filtered or unexported fields
}
FailedToGenerateSignatureError represents an error when signature generation fails.
func (FailedToGenerateSignatureError) Error ¶
func (e FailedToGenerateSignatureError) Error() string
Error returns a string representation of the signature generation error.
func (FailedToGenerateSignatureError) Unwrap ¶
func (e FailedToGenerateSignatureError) Unwrap() error
Unwrap returns the underlying error that caused the signature generation failure.
type FailedToMarshalValueError ¶
type FailedToMarshalValueError struct {
// contains filtered or unexported fields
}
FailedToMarshalValueError represents an error when value marshalling fails.
func (FailedToMarshalValueError) Error ¶
func (e FailedToMarshalValueError) Error() string
Error returns a string representation of the marshalling error.
func (FailedToMarshalValueError) Unwrap ¶
func (e FailedToMarshalValueError) Unwrap() error
Unwrap returns the underlying error that caused the marshalling failure.
type FailedToValidateAggregatedError ¶
type FailedToValidateAggregatedError struct {
// contains filtered or unexported fields
}
FailedToValidateAggregatedError represents aggregated validation errors.
func (*FailedToValidateAggregatedError) Append ¶
func (e *FailedToValidateAggregatedError) Append(err error)
Append adds an error to the aggregated error.
func (*FailedToValidateAggregatedError) Error ¶
func (e *FailedToValidateAggregatedError) Error() string
Error returns a string representation of the aggregated error.
func (*FailedToValidateAggregatedError) Finalize ¶
func (e *FailedToValidateAggregatedError) Finalize() error
Finalize returns nil if there are no errors, otherwise returns error or the aggregated error.
func (*FailedToValidateAggregatedError) Unwrap ¶
func (e *FailedToValidateAggregatedError) Unwrap() []error
Unwrap returns the underlying slice of errors.
type Generator ¶
type Generator[T any] struct { // contains filtered or unexported fields }
Generator creates integrity-protected key-value pairs for storage.
func NewGenerator ¶
func NewGenerator[T any]( namer namer.Namer, marshaller marshaller.TypedMarshaller[T], hashers []hasher.Hasher, signers []crypto.Signer, ) Generator[T]
NewGenerator creates a new Generator instance.
type GetFuture ¶ added in v1.3.0
type GetFuture[T any] struct { // contains filtered or unexported fields }
GetFuture holds the result of a TxGet enqueue. Call Result() after Commit.
Example ¶
ExampleGetFuture shows how TxGet hands back a *GetFuture[T] whose Result() is populated only after Commit.
package main
import (
"context"
"errors"
"fmt"
"log"
storage "github.com/tarantool/go-storage"
"github.com/tarantool/go-storage/driver/dummy"
"github.com/tarantool/go-storage/hasher"
"github.com/tarantool/go-storage/integrity"
)
type txExampleValue struct {
Field string `yaml:"field"`
}
func newTxExampleCodec() *integrity.Codec[txExampleValue] {
codec, err := integrity.NewCodecBuilder[txExampleValue]().WithObjectLocation("objects").
WithHasher(hasher.NewSHA256Hasher()).
Build()
if err != nil {
log.Fatalf("build codec: %v", err)
}
return codec
}
func main() {
ctx := context.Background()
store := storage.NewStorage(dummy.New())
codec := newTxExampleCodec()
seedTx := integrity.NewTx(store)
err := codec.TxPut(seedTx, "alice", txExampleValue{Field: "hello"})
if err != nil {
log.Fatalf("seed: %v", err)
}
_, err = seedTx.Commit(ctx)
if err != nil {
log.Fatalf("seed commit: %v", err)
}
readTx := integrity.NewTx(store)
fut := codec.TxGet(readTx, "alice")
// Calling Result() before Commit returns ErrTxNotCommitted.
_, err = fut.Result()
fmt.Println("before commit:", errors.Is(err, integrity.ErrTxNotCommitted))
_, err = readTx.Commit(ctx)
if err != nil {
log.Fatalf("read commit: %v", err)
}
res, err := fut.Result()
if err != nil {
log.Fatalf("result: %v", err)
}
fmt.Println("field:", res.Value.Unwrap().Field)
}
Output: before commit: true field: hello
func (*GetFuture[T]) Result ¶ added in v1.3.0
func (f *GetFuture[T]) Result() (ValidatedResult[T], error)
Result returns the validated result after the Tx has been committed.
Returns ErrTxNotCommitted if called before Commit. Returns the stored buildErr if Commit surfaced a build error. Returns ErrBranchNotFired if this future's branch did not fire.
type ImpossibleError ¶
type ImpossibleError struct {
// contains filtered or unexported fields
}
ImpossibleError represents an error when an integrity operation cannot be performed due to internal problems.
func (ImpossibleError) Error ¶
func (e ImpossibleError) Error() string
type InvalidNameError ¶
type InvalidNameError struct {
// contains filtered or unexported fields
}
InvalidNameError represents an error when a name is invalid.
func (InvalidNameError) Error ¶
func (e InvalidNameError) Error() string
Error returns a string representation of the invalid name error.
type NamerConstructor ¶
type RangeFuture ¶ added in v1.3.0
type RangeFuture[T any] struct { // contains filtered or unexported fields }
RangeFuture holds the results of a TxRange enqueue. Call Result() after Commit.
Example ¶
ExampleRangeFuture shows TxRange returning every value under a name prefix in a single transaction. Use "" to fetch every value the codec owns; pass a non-empty prefix (must end with "/") to scope.
package main
import (
"context"
"fmt"
"log"
"sort"
storage "github.com/tarantool/go-storage"
"github.com/tarantool/go-storage/driver/dummy"
"github.com/tarantool/go-storage/hasher"
"github.com/tarantool/go-storage/integrity"
)
type txExampleValue struct {
Field string `yaml:"field"`
}
func newTxExampleCodec() *integrity.Codec[txExampleValue] {
codec, err := integrity.NewCodecBuilder[txExampleValue]().WithObjectLocation("objects").
WithHasher(hasher.NewSHA256Hasher()).
Build()
if err != nil {
log.Fatalf("build codec: %v", err)
}
return codec
}
func main() {
ctx := context.Background()
store := storage.NewStorage(dummy.New())
codec := newTxExampleCodec()
seedTx := integrity.NewTx(store)
for _, name := range []string{"users/alice", "users/bob"} {
err := codec.TxPut(seedTx, name, txExampleValue{Field: name})
if err != nil {
log.Fatalf("seed %s: %v", name, err)
}
}
_, err := seedTx.Commit(ctx)
if err != nil {
log.Fatalf("seed commit: %v", err)
}
readTx := integrity.NewTx(store)
fut := codec.TxRange(readTx, "users/")
_, err = readTx.Commit(ctx)
if err != nil {
log.Fatalf("read commit: %v", err)
}
results, err := fut.Result()
if err != nil {
log.Fatalf("result: %v", err)
}
names := make([]string, 0, len(results))
for _, r := range results {
names = append(names, r.Name)
}
sort.Strings(names)
for _, n := range names {
fmt.Println(n)
}
}
Output: users/alice users/bob
func (*RangeFuture[T]) Result ¶ added in v1.3.0
func (f *RangeFuture[T]) Result() ([]ValidatedResult[T], error)
Result returns the validated results after the Tx has been committed.
type Response ¶ added in v1.3.0
type Response struct {
Succeeded bool
}
Response is the result of a Tx.Commit call. It deliberately exposes less than tx.Response so the integrity layer stays storage-agnostic.
type SingletonStore ¶ added in v1.4.0
type SingletonStore[T any] struct { // contains filtered or unexported fields }
SingletonStore[T] is a Store[T] bound to one fixed name. It is intended for configuration-style objects that live at a single, known key (e.g. "/settings/auth") rather than under a directory of "/settings/auth/<id>" items. The name is supplied once at bind time and threaded through every operation, so call sites no longer carry the noisy identifier.
Construct one via Codec[T].BindSingleton. The wire layout is identical to what Store[T] produces for the same name — no separate codec, no separate namer.
func (*SingletonStore[T]) BindPredicate ¶ added in v1.4.0
func (s *SingletonStore[T]) BindPredicate(pred Predicate) (predicate.Predicate, error)
BindPredicate resolves the singleton's value-layer key and applies p to it, returning a concrete predicate.Predicate ready for use in Tx.If.
func (*SingletonStore[T]) Delete ¶ added in v1.4.0
func (s *SingletonStore[T]) Delete( ctx context.Context, opts ...options.OptionCallback[deleteOptions], ) error
Delete removes the singleton's value, hash, and signature keys. WithDeletePredicates(...) makes the delete conditional; ErrPredicateFailed is returned if any predicate fails.
func (*SingletonStore[T]) FullKeys ¶ added in v1.6.0
func (s *SingletonStore[T]) FullKeys() ([]string, error)
FullKeys returns every on-disk key the bound codec would produce for the singleton (value + hashes + signatures). See Store.FullKeys.
func (*SingletonStore[T]) Get ¶ added in v1.4.0
func (s *SingletonStore[T]) Get( ctx context.Context, opts ...options.OptionCallback[getOptions], ) (ValidatedResult[T], error)
Get reads the singleton's value and verifies its hashes/signatures.
func (*SingletonStore[T]) Put ¶ added in v1.4.0
func (s *SingletonStore[T]) Put( ctx context.Context, value T, opts ...options.OptionCallback[putOptions], ) error
Put writes value under the singleton's bound name with integrity protection. WithPutPredicates(...) makes the write conditional; ErrPredicateFailed is returned if any predicate fails.
func (*SingletonStore[T]) TxDelete ¶ added in v1.4.0
func (s *SingletonStore[T]) TxDelete( b Branchable, opts ...options.OptionCallback[deleteOptions], ) error
TxDelete enqueues a Delete for the singleton onto branch b. Mirrors Codec[T].TxDelete without the name parameter.
func (*SingletonStore[T]) TxGet ¶ added in v1.4.0
func (s *SingletonStore[T]) TxGet( b Branchable, opts ...options.OptionCallback[getOptions], ) *GetFuture[T]
TxGet enqueues a Get for the singleton onto branch b, returning a future whose Result() is populated after Commit. Mirrors Codec[T].TxGet without the name parameter — the bound name is used.
func (*SingletonStore[T]) TxPut ¶ added in v1.4.0
func (s *SingletonStore[T]) TxPut(b Branchable, value T) error
TxPut enqueues a Put for the singleton onto branch b. Mirrors Codec[T].TxPut without the name parameter.
func (*SingletonStore[T]) ValueKey ¶ added in v1.6.0
func (s *SingletonStore[T]) ValueKey() (string, error)
ValueKey returns the on-disk value-layer key for the singleton's bound name. See Store.ValueKey.
type Store ¶ added in v1.3.0
type Store[T any] struct { // contains filtered or unexported fields }
Store[T] is a Codec[T] bound to a concrete storage.Storage. Each method is a thin wrapper that builds a *Tx, enqueues one op via the codec, commits, and returns the future's result — observably indistinguishable from a single-op Tx.Commit. Store[T] holds nothing beyond the codec and storage handle, so binding the same codec to different storages is cheap.
Example ¶
ExampleStore demonstrates a Put/Get round-trip via Store[T]. Each method is a thin single-op wrapper around a Tx — observably indistinguishable from a single-op Tx.Commit.
package main
import (
"context"
"fmt"
"log"
storage "github.com/tarantool/go-storage"
"github.com/tarantool/go-storage/driver/dummy"
"github.com/tarantool/go-storage/hasher"
"github.com/tarantool/go-storage/integrity"
)
type storeExampleValue struct {
Field string `yaml:"field"`
}
func newExampleCodec() *integrity.Codec[storeExampleValue] {
codec, err := integrity.NewCodecBuilder[storeExampleValue]().WithObjectLocation("objects").
WithHasher(hasher.NewSHA256Hasher()).
Build()
if err != nil {
log.Fatalf("build codec: %v", err)
}
return codec
}
func newExampleStore() *integrity.Store[storeExampleValue] {
return newExampleCodec().Bind(storage.NewStorage(dummy.New()))
}
func main() {
ctx := context.Background()
store := newExampleStore()
err := store.Put(ctx, "alice", storeExampleValue{Field: "hello"})
if err != nil {
log.Fatalf("put: %v", err)
}
res, err := store.Get(ctx, "alice")
if err != nil {
log.Fatalf("get: %v", err)
}
fmt.Println("name:", res.Name)
fmt.Println("field:", res.Value.Unwrap().Field)
}
Output: name: alice field: hello
func (*Store[T]) Delete ¶ added in v1.3.0
func (s *Store[T]) Delete( ctx context.Context, name string, vOpts ...options.OptionCallback[deleteOptions], ) error
Delete removes a named value with integrity protection. Accepts WithDeletePredicates(...) and WithPrefix(); if any predicate fails, ErrPredicateFailed is returned.
Example (WithPrefix) ¶
ExampleStore_Delete_withPrefix removes every value (and its hashes) whose name starts with the given prefix. The prefix must end with '/'.
package main
import (
"context"
"fmt"
"log"
storage "github.com/tarantool/go-storage"
"github.com/tarantool/go-storage/driver/dummy"
"github.com/tarantool/go-storage/hasher"
"github.com/tarantool/go-storage/integrity"
)
type storeExampleValue struct {
Field string `yaml:"field"`
}
func newExampleCodec() *integrity.Codec[storeExampleValue] {
codec, err := integrity.NewCodecBuilder[storeExampleValue]().WithObjectLocation("objects").
WithHasher(hasher.NewSHA256Hasher()).
Build()
if err != nil {
log.Fatalf("build codec: %v", err)
}
return codec
}
func newExampleStore() *integrity.Store[storeExampleValue] {
return newExampleCodec().Bind(storage.NewStorage(dummy.New()))
}
func main() {
ctx := context.Background()
store := newExampleStore()
for _, name := range []string{"tmp/a", "tmp/b"} {
err := store.Put(ctx, name, storeExampleValue{Field: name})
if err != nil {
log.Fatalf("put %s: %v", name, err)
}
}
before, err := store.Range(ctx, "tmp/")
if err != nil {
log.Fatalf("range before: %v", err)
}
fmt.Println("before:", len(before))
err = store.Delete(ctx, "tmp/", integrity.WithPrefix())
if err != nil {
log.Fatalf("delete: %v", err)
}
after, err := store.Range(ctx, "tmp/")
if err != nil {
log.Fatalf("range after: %v", err)
}
fmt.Println("after:", len(after))
}
Output: before: 2 after: 0
func (*Store[T]) FullKeys ¶ added in v1.6.0
FullKeys returns every on-disk key the bound codec would produce for name (value + hashes + signatures), each prefixed with the bound storage's prefix if it is wrapped with storage.Prefixed. See Codec.FullKeys for the namer-relative form.
func (*Store[T]) Get ¶ added in v1.3.0
func (s *Store[T]) Get( ctx context.Context, name string, opts ...options.OptionCallback[getOptions], ) (ValidatedResult[T], error)
Get retrieves and validates a single named value from storage.
func (*Store[T]) Put ¶ added in v1.3.0
func (s *Store[T]) Put( ctx context.Context, name string, value T, vOpts ...options.OptionCallback[putOptions], ) error
Put stores a named value with integrity protection. Accepts WithPutPredicates(...) to add conditional predicates; if any predicate fails, ErrPredicateFailed is returned.
Example (Predicate) ¶
ExampleStore_Put_predicate uses WithPutPredicates to make the write conditional. ErrPredicateFailed is returned when the predicate does not hold.
package main
import (
"context"
"errors"
"fmt"
"log"
storage "github.com/tarantool/go-storage"
"github.com/tarantool/go-storage/driver/dummy"
"github.com/tarantool/go-storage/hasher"
"github.com/tarantool/go-storage/integrity"
)
type storeExampleValue struct {
Field string `yaml:"field"`
}
func newExampleCodec() *integrity.Codec[storeExampleValue] {
codec, err := integrity.NewCodecBuilder[storeExampleValue]().WithObjectLocation("objects").
WithHasher(hasher.NewSHA256Hasher()).
Build()
if err != nil {
log.Fatalf("build codec: %v", err)
}
return codec
}
func main() {
ctx := context.Background()
codec := newExampleCodec()
store := codec.Bind(storage.NewStorage(dummy.New()))
err := store.Put(ctx, "k", storeExampleValue{Field: "v1"})
if err != nil {
log.Fatalf("seed: %v", err)
}
pred, err := codec.ValueEqual(storeExampleValue{Field: "v1"})
if err != nil {
log.Fatalf("predicate: %v", err)
}
// Predicate matches: this update succeeds.
err = store.Put(ctx, "k", storeExampleValue{Field: "v2"},
integrity.WithPutPredicates(pred))
fmt.Println("first update err:", err)
// Predicate no longer matches (current value is "v2"): ErrPredicateFailed.
err = store.Put(ctx, "k", storeExampleValue{Field: "v3"},
integrity.WithPutPredicates(pred))
fmt.Println("second update is ErrPredicateFailed:", errors.Is(err, integrity.ErrPredicateFailed))
}
Output: first update err: <nil> second update is ErrPredicateFailed: true
func (*Store[T]) Range ¶ added in v1.3.0
func (s *Store[T]) Range( ctx context.Context, name string, opts ...options.OptionCallback[getOptions], ) ([]ValidatedResult[T], error)
Range retrieves and validates all values under the given name prefix.
Example ¶
ExampleStore_Range fetches and validates every value under a name prefix. Pass "" to fetch everything under the codec's object location.
package main
import (
"context"
"fmt"
"log"
"sort"
storage "github.com/tarantool/go-storage"
"github.com/tarantool/go-storage/driver/dummy"
"github.com/tarantool/go-storage/hasher"
"github.com/tarantool/go-storage/integrity"
)
type storeExampleValue struct {
Field string `yaml:"field"`
}
func newExampleCodec() *integrity.Codec[storeExampleValue] {
codec, err := integrity.NewCodecBuilder[storeExampleValue]().WithObjectLocation("objects").
WithHasher(hasher.NewSHA256Hasher()).
Build()
if err != nil {
log.Fatalf("build codec: %v", err)
}
return codec
}
func newExampleStore() *integrity.Store[storeExampleValue] {
return newExampleCodec().Bind(storage.NewStorage(dummy.New()))
}
func main() {
ctx := context.Background()
store := newExampleStore()
for _, name := range []string{"users/alice", "users/bob", "groups/admins"} {
err := store.Put(ctx, name, storeExampleValue{Field: name})
if err != nil {
log.Fatalf("put %s: %v", name, err)
}
}
results, err := store.Range(ctx, "users/")
if err != nil {
log.Fatalf("range: %v", err)
}
names := make([]string, 0, len(results))
for _, r := range results {
names = append(names, r.Name)
}
sort.Strings(names)
for _, n := range names {
fmt.Println(n)
}
}
Output: users/alice users/bob
func (*Store[T]) ValueKey ¶ added in v1.6.0
ValueKey returns the on-disk value-layer key for name — the namer's value key prefixed with the bound storage's prefix (if it is wrapped with storage.Prefixed). See Codec.ValueKey for the namer-relative form.
func (*Store[T]) Watch ¶ added in v1.3.0
Watch returns a channel that receives events for values under the given name prefix. Watch is not transactional and bypasses Tx; it calls s.storage.Watch directly.
Under the signal-only Event.Prefix contract every event carries the watched prefix verbatim (driver-stripped of its trailing "/"), so filtering on event.Prefix is a no-op — callers must Range/Get to learn what changed.
Example ¶
ExampleStore_Watch streams events for changes to a specific name. The emitted event Prefix is the codec-internal absolute key; Watch filters out events whose key does not belong to this codec's namespace.
package main
import (
"context"
"fmt"
"log"
storage "github.com/tarantool/go-storage"
"github.com/tarantool/go-storage/driver/dummy"
"github.com/tarantool/go-storage/hasher"
"github.com/tarantool/go-storage/integrity"
)
type storeExampleValue struct {
Field string `yaml:"field"`
}
func newExampleCodec() *integrity.Codec[storeExampleValue] {
codec, err := integrity.NewCodecBuilder[storeExampleValue]().WithObjectLocation("objects").
WithHasher(hasher.NewSHA256Hasher()).
Build()
if err != nil {
log.Fatalf("build codec: %v", err)
}
return codec
}
func newExampleStore() *integrity.Store[storeExampleValue] {
return newExampleCodec().Bind(storage.NewStorage(dummy.New()))
}
func main() {
ctx, cancel := context.WithCancel(context.Background())
store := newExampleStore()
events, err := store.Watch(ctx, "alice")
if err != nil {
cancel()
log.Fatalf("watch: %v", err)
}
go func() {
_ = store.Put(ctx, "alice", storeExampleValue{Field: "v"})
}()
ev := <-events
cancel()
fmt.Println("event:", string(ev.Prefix))
}
Output: event: /objects/alice
type Tx ¶ added in v1.3.0
type Tx struct {
// contains filtered or unexported fields
}
Tx is the user-facing multi-op, multi-codec transactional accumulator. Tx is not goroutine-safe.
Example (ThenElse) ¶
ExampleTx_thenElse routes operations to the Then or Else branch, depending on which side fires after If predicates are evaluated. Futures attached to a branch that did not fire return ErrBranchNotFired.
package main
import (
"context"
"errors"
"fmt"
"log"
storage "github.com/tarantool/go-storage"
"github.com/tarantool/go-storage/driver/dummy"
"github.com/tarantool/go-storage/hasher"
"github.com/tarantool/go-storage/integrity"
)
type txExampleValue struct {
Field string `yaml:"field"`
}
func newTxExampleCodec() *integrity.Codec[txExampleValue] {
codec, err := integrity.NewCodecBuilder[txExampleValue]().WithObjectLocation("objects").
WithHasher(hasher.NewSHA256Hasher()).
Build()
if err != nil {
log.Fatalf("build codec: %v", err)
}
return codec
}
func main() {
ctx := context.Background()
store := storage.NewStorage(dummy.New())
codec := newTxExampleCodec()
// Seed a value the Else branch will read.
seedTx := integrity.NewTx(store)
err := codec.TxPut(seedTx, "fallback", txExampleValue{Field: "fallback-value"})
if err != nil {
log.Fatalf("seed: %v", err)
}
_, err = seedTx.Commit(ctx)
if err != nil {
log.Fatalf("seed commit: %v", err)
}
// Build a tx whose If can never hold.
mainTx := integrity.NewTx(store)
pred, err := codec.BindPredicate("missing", codec.VersionGreater(999))
if err != nil {
log.Fatalf("bind: %v", err)
}
mainTx.If(pred)
thenFut := codec.TxGet(mainTx.Then(), "missing")
elseFut := codec.TxGet(mainTx.Else(), "fallback")
resp, err := mainTx.Commit(ctx)
if err != nil {
log.Fatalf("commit: %v", err)
}
fmt.Println("succeeded:", resp.Succeeded)
_, thenErr := thenFut.Result()
if errors.Is(thenErr, integrity.ErrBranchNotFired) {
fmt.Println("then branch did not fire")
}
res, err := elseFut.Result()
if err != nil {
log.Fatalf("else result: %v", err)
}
fmt.Println("else field:", res.Value.Unwrap().Field)
}
Output: succeeded: false then branch did not fire else field: fallback-value
func NewTx ¶ added in v1.3.0
NewTx creates a new Tx backed by the given storage.
Example ¶
ExampleNewTx demonstrates a multi-codec, multi-op transaction. Two TxPut calls are accumulated, then committed atomically with a single storage call.
package main
import (
"context"
"fmt"
"log"
storage "github.com/tarantool/go-storage"
"github.com/tarantool/go-storage/driver/dummy"
"github.com/tarantool/go-storage/hasher"
"github.com/tarantool/go-storage/integrity"
)
type txExampleValue struct {
Field string `yaml:"field"`
}
func newTxExampleCodec() *integrity.Codec[txExampleValue] {
codec, err := integrity.NewCodecBuilder[txExampleValue]().WithObjectLocation("objects").
WithHasher(hasher.NewSHA256Hasher()).
Build()
if err != nil {
log.Fatalf("build codec: %v", err)
}
return codec
}
func main() {
ctx := context.Background()
store := storage.NewStorage(dummy.New())
codec := newTxExampleCodec()
txn := integrity.NewTx(store)
err := codec.TxPut(txn, "alice", txExampleValue{Field: "a"})
if err != nil {
log.Fatalf("put alice: %v", err)
}
err = codec.TxPut(txn, "bob", txExampleValue{Field: "b"})
if err != nil {
log.Fatalf("put bob: %v", err)
}
resp, err := txn.Commit(ctx)
if err != nil {
log.Fatalf("commit: %v", err)
}
fmt.Println("succeeded:", resp.Succeeded)
}
Output: succeeded: true
func (*Tx) Commit ¶ added in v1.3.0
Commit executes the accumulated operations as a single atomic storage call. A second call returns ErrTxAlreadyCommitted without touching storage.
type Typed ¶
type Typed[T any] struct { // contains filtered or unexported fields }
Typed provides integrity-protected storage operations for typed values.
func (*Typed[T]) Delete ¶
func (t *Typed[T]) Delete(ctx context.Context, name string, vOpts ...options.OptionCallback[deleteOptions]) error
Delete removes a named value with integrity protection. Use WithPrefix to delete all values under a prefix. Use WithDeletePredicates to specify conditions that must be met for the operation to succeed. If predicates are specified but fail, ErrPredicateFailed is returned.
func (*Typed[T]) Get ¶
func (t *Typed[T]) Get( ctx context.Context, name string, vOpts ...options.OptionCallback[getOptions], ) (ValidatedResult[T], error)
Get retrieves and validates a single named value from storage.
func (*Typed[T]) Put ¶
func (t *Typed[T]) Put(ctx context.Context, name string, val T, vOpts ...options.OptionCallback[putOptions]) error
Put stores a named value with integrity protection. Use WithPutPredicates to specify conditions that must be met for the operation to succeed. If predicates are specified but fail, ErrPredicateFailed is returned.
func (*Typed[T]) Range ¶
func (t *Typed[T]) Range( ctx context.Context, name string, vOpts ...options.OptionCallback[getOptions], ) ([]ValidatedResult[T], error)
Range retrieves and validates all values under the given name prefix.
func (*Typed[T]) ValueEqual ¶ added in v1.1.0
ValueEqual creates a predicate that checks if a key's value equals the specified value.
func (*Typed[T]) ValueNotEqual ¶ added in v1.1.0
ValueNotEqual creates a predicate that checks if a key's value is not equal to the specified value.
func (*Typed[T]) VersionEqual ¶ added in v1.1.0
VersionEqual creates a predicate that checks if a key's version equals the specified version.
func (*Typed[T]) VersionGreater ¶ added in v1.1.0
VersionGreater creates a predicate that checks if a key's version is greater than the specified version.
func (*Typed[T]) VersionLess ¶ added in v1.1.0
VersionLess creates a predicate that checks if a key's version is less than the specified version.
func (*Typed[T]) VersionNotEqual ¶ added in v1.1.0
VersionNotEqual creates a predicate that checks if a key's version is not equal to the specified version.
func (*Typed[T]) Watch ¶
Watch returns a channel for watching changes to values under the given name prefix.
Under the signal-only Event.Prefix contract every event carries the watched prefix verbatim (driver-stripped of its trailing "/"), so filtering on event.Prefix is a no-op — callers must Range/Get to learn what changed.
type TypedBuilder ¶
type TypedBuilder[T any] struct { // contains filtered or unexported fields }
TypedBuilder builds typed storage instances with integrity protection.
func NewTypedBuilder ¶
func NewTypedBuilder[T any](storageInstance storage.Storage) TypedBuilder[T]
NewTypedBuilder creates a new TypedBuilder for the given storage instance.
func (TypedBuilder[T]) Build ¶
func (s TypedBuilder[T]) Build() *Typed[T]
Build creates a new Typed storage instance with the configured options.
func (TypedBuilder[T]) WithHasher ¶
func (s TypedBuilder[T]) WithHasher(h hasher.Hasher) TypedBuilder[T]
WithHasher adds a hasher to the builder.
func (TypedBuilder[T]) WithMarshaller ¶
func (s TypedBuilder[T]) WithMarshaller(marshaller marshaller.TypedMarshaller[T]) TypedBuilder[T]
WithMarshaller sets the marshaller for the builder.
func (TypedBuilder[T]) WithNamer ¶
func (s TypedBuilder[T]) WithNamer(namerFunc NamerConstructor) TypedBuilder[T]
WithNamer sets the namer for the builder using a constructor function. The constructor function will be called during Build() with the current prefix.
func (TypedBuilder[T]) WithPrefix ¶
func (s TypedBuilder[T]) WithPrefix(prefix string) TypedBuilder[T]
WithPrefix sets the key prefix for the builder.
func (TypedBuilder[T]) WithSigner ¶
func (s TypedBuilder[T]) WithSigner(signer crypto.Signer) TypedBuilder[T]
WithSigner adds a signer to the builder.
func (TypedBuilder[T]) WithSignerVerifier ¶
func (s TypedBuilder[T]) WithSignerVerifier(sv crypto.SignerVerifier) TypedBuilder[T]
WithSignerVerifier adds a signer/verifier to the builder.
func (TypedBuilder[T]) WithVerifier ¶
func (s TypedBuilder[T]) WithVerifier(verifier crypto.Verifier) TypedBuilder[T]
WithVerifier adds a verifier to the builder.
type ValidatedResult ¶
type ValidatedResult[T any] struct { // Name is the object identifier under which the value was stored. Name string // Value contains the unmarshalled value if decoding succeeded. Value option.Generic[T] // ModRevision is the storage revision when this value was last modified. ModRevision int64 // Error contains validation errors if integrity verification failed. Error error }
ValidatedResult represents a validated named value.
type ValidationError ¶
type ValidationError struct {
// contains filtered or unexported fields
}
ValidationError represents an error when validation fails.
func (ValidationError) Error ¶
func (e ValidationError) Error() string
Error returns a string representation of the validation error.
func (ValidationError) Unpack ¶
func (e ValidationError) Unpack() error
type Validator ¶
type Validator[T any] struct { // contains filtered or unexported fields }
Validator verifies integrity-protected key-value pairs.
func NewValidator ¶
func NewValidator[T any]( namer namer.Namer, marshaller marshaller.TypedMarshaller[T], hashers []hasher.Hasher, verifiers []crypto.Verifier, ) Validator[T]
NewValidator creates a new Validator instance.