blobstore

package
v2.12.2 Latest Latest
Warning

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

Go to latest
Published: Jun 16, 2026 License: MIT Imports: 11 Imported by: 0

Documentation

Overview

Package blobstore is the arcade's wasm blob storage: published catalog artifacts (`artifacts/<sha256>.wasm`, immutable, content-addressed) and room state under the `snapshots/` prefix, in two coexisting schemes:

  • flat hibernation snapshots (`snapshots/<room-id>`, deleted on restore) — the original disposing park/resume path (internal/gameabi);
  • versioned, MAC'd room checkpoints (`snapshots/<room-id>/<epoch>` with a `snapshots/<room-id>/latest` pointer; see CheckpointKey / Sealer) — the non-destructive periodic-durability path added for regional bastions.

Both live under `snapshots/` and share the bucket lifecycle TTL on that prefix; Phase 0 keeps them side by side and Track C / task G.5 unifies room durability onto the versioned scheme. Production is a Fly-attached Tigris bucket via the standard S3 env contract; dev and tests use the in-memory double. Only the arcade ever holds bucket credentials — catalog CI publishes to GitHub releases and never writes here.

Index

Constants

This section is empty.

Variables

View Source
var ErrSealVerify = errors.New("blobstore: checkpoint seal verification failed")

ErrSealVerify is returned by Sealer.Open when a sealed blob's MAC does not verify (tampered payload, tampered MAC, wrong key, or truncated blob). A failed verification MUST refuse the restore before any guest-memory write.

Functions

func CheckpointKey

func CheckpointKey(roomID string, epoch int64) string

CheckpointKey returns the blobstore key for a room's checkpoint at the given epoch: snapshots/<roomID>/<epoch>. The epoch is zero-padded to 20 digits (the width of a uint64's max decimal value) so the lexical key order returned by Store.List matches numeric epoch order — checkpoints sort oldest-to-newest. Keys are written never-overwrite-in-place so a slow in-flight periodic PUT cannot clobber a later drain PUT at a higher epoch.

epoch MUST be >= 0: a negative epoch would emit a leading '-' that breaks the lexical = numeric ordering invariant, so a negative epoch is a programmer error and panics (epochs are monotonic from 0).

func LatestPointerKey

func LatestPointerKey(roomID string) string

LatestPointerKey returns the key of a room's atomic latest-checkpoint pointer: snapshots/<roomID>/latest. The pointer is swapped atomically to the newest epoch; "latest" sorts after every zero-padded numeric epoch so it never shadows a checkpoint under the room prefix.

Types

type Dir

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

Dir is a file-backed Store rooted at a directory: each slash-separated key maps to a file under root, with key segments becoming subdirectories. It exists for dev mode — set SHELLCADE_BLOB_DIR so hibernation snapshots (and the sideloaded catalog) survive a `serve` restart, which the in-memory double cannot do. Production still uses S3; this is never wired in prod.

func NewDir

func NewDir(root string) (*Dir, error)

NewDir returns a Dir store rooted at root, creating it if needed.

func (*Dir) Delete

func (d *Dir) Delete(ctx context.Context, key string) error

func (*Dir) Get

func (d *Dir) Get(ctx context.Context, key string) ([]byte, bool, error)

func (*Dir) List

func (d *Dir) List(ctx context.Context, prefix string) ([]string, error)

func (*Dir) Put

func (d *Dir) Put(ctx context.Context, key string, data []byte) error

type Memory

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

Memory is the in-memory Store double for dev mode and tests.

func NewMemory

func NewMemory() *Memory

NewMemory returns an empty in-memory store.

func (*Memory) Delete

func (m *Memory) Delete(ctx context.Context, key string) error

func (*Memory) Get

func (m *Memory) Get(ctx context.Context, key string) ([]byte, bool, error)

func (*Memory) List

func (m *Memory) List(ctx context.Context, prefix string) ([]string, error)

func (*Memory) Put

func (m *Memory) Put(ctx context.Context, key string, data []byte) error

type Sealer

type Sealer interface {
	// Seal returns payload with an integrity tag appended.
	Seal(payload []byte) []byte
	// Open verifies the tag and returns the original payload, or ErrSealVerify.
	Open(sealed []byte) ([]byte, error)
}

Sealer authenticates checkpoint blobs with a server-side key held outside the wasm sandbox: artifact-digest equality is NOT blob integrity. Seal produces a blob that Open verifies before returning any payload, so a re-hydration always proves integrity before writing guest memory.

func NewHMACSealer

func NewHMACSealer(key []byte) Sealer

NewHMACSealer returns a Sealer that MACs blobs with key using HMAC-SHA256.

type Store

type Store interface {
	// Get returns the blob at key; ok=false when it does not exist.
	Get(ctx context.Context, key string) (data []byte, ok bool, err error)
	// Put writes the blob at key, overwriting any existing object.
	Put(ctx context.Context, key string, data []byte) error
	// Delete removes the blob at key; deleting a missing key is not an error.
	Delete(ctx context.Context, key string) error
	// List returns the keys under prefix in lexical order.
	List(ctx context.Context, prefix string) ([]string, error)
}

Store is the blob surface the catalog pipeline and the hibernation store build on. Keys are slash-separated paths; values are whole blobs (wasm artifacts and zstd snapshots are small enough that streaming buys nothing on a 32 MiB-capped guest).

func Instrument

func Instrument(s Store, rec func(op string, ok bool)) Store

Instrument wraps s so every operation reports its outcome through rec — the recording seam for shellcade_blobstore_ops_total (production passes metrics.Metrics.BlobstoreOp at boot, where the backend is chosen). The blobstore stays metrics-agnostic, matching how the catalog reports its store latency through an injected recorder. op is one of get|put|delete|list; ok is err == nil (a Get of a missing key is ok=true — absence is an answer, not a store failure). A nil s or rec returns s unchanged so callers can wire it unconditionally.

Jump to

Keyboard shortcuts

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