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 ¶
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 ¶
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 ¶
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.
type Memory ¶
type Memory struct {
// contains filtered or unexported fields
}
Memory is the in-memory Store double for dev mode and tests.
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 ¶
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 ¶
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.