wasmmod

package
v0.7.21 Latest Latest
Warning

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

Go to latest
Published: Jul 21, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Overview

Package wasmmod — AOCR push for §4.8.1 WASM snapshot artifacts (D2).

Index

Constants

View Source
const CheckpointWasmHex = "0061736d01000000010401600000030201000503010001071302066d656d6f72790200065f737461727400000a040102000b"

CheckpointWasmHex is a minimal module with exported linear memory for snapshot tests.

View Source
const MinimalWasmHex = "0061736d0100000001040160000003020100070a01065f737461727400000a040102000b"

MinimalWasmHex is a valid wasm module: (module (func) (export "_start" (func 0))). Used by pkg/wasm and pkg/wasmmod tests — the export name length must be 6, not 4.

Variables

View Source
var (
	// ErrModuleNotFound: the ref named no reserved keyword, catalogue id, or
	// local file. The service surfaces the valid reserved keywords alongside.
	ErrModuleNotFound = errors.New("wasm module not found")
	// ErrRegistryNotAllowed: an oci:// ref named a host outside
	// SB_WASM_REGISTRY_ALLOWLIST. This is the SSRF guard — never relax it to
	// a warning.
	ErrRegistryNotAllowed = errors.New("wasm module registry not allowed")
	// ErrRegistryAuth: the registry rejected our credentials (bad/expired PAT).
	// Terminal — retrying without fixing the token will not help.
	ErrRegistryAuth = errors.New("wasm module registry authentication failed")
	// ErrRegistryUnavailable: network/registry transient failure. Retryable.
	ErrRegistryUnavailable = errors.New("wasm module registry unavailable")
	// ErrModuleTooLarge: the artifact exceeded the size cap mid-stream. We
	// abort the pull rather than buffer an unbounded body.
	ErrModuleTooLarge = errors.New("wasm module exceeds size cap")
	// ErrModuleDigestMismatch: a sandbox pinned digest X at create, but the
	// alias/tag it referenced now resolves to different bytes and the frozen
	// copy is gone. We fail loudly rather than silently boot different code on
	// restart/failover (codex C2).
	ErrModuleDigestMismatch = errors.New("wasm module digest drift")
)

Module-resolution error taxonomy. The service layer maps these to HTTP status via apihttp.WriteStoreAwareError so the SDK can tell a terminal failure (fix your ref / token) from a transient one (retry the pull).

ErrComponentModelUnsupported (validate.go) is the sixth member of this set; it is mapped to 422 the same way.

View Source
var ErrComponentModelUnsupported = errors.New("wasm component model not supported")

ErrComponentModelUnsupported flags a WASI Component Model artifact. Neither engine in this repo (wazero, nor wasmtime-go which has no component API) can instantiate a component — both run core wasip1 modules only. Detecting it here turns a cryptic deep parse failure into a directed, actionable error.

Functions

func DeleteSnapshotRef

func DeleteSnapshotRef(ctx context.Context, cfg ORASPushConfig, registryRef string) error

DeleteSnapshotRef removes a tagged WASM checkpoint manifest from AOCR. Missing refs are treated as success so SQLite prune can proceed after manual cleanup.

func PullModuleArtifact

func PullModuleArtifact(ctx context.Context, a ModuleAuth, registryRef, dstDir string, maxBytes int64) (wasmPath string, err error)

PullModuleArtifact downloads a single-layer .wasm artifact from registryRef into dstDir and returns the unpacked file path. The caller is responsible for validation (ValidateFile) and content-addressed publishing — this function only does the transport. dstDir should be a caller-owned temp dir.

maxBytes (>0) bounds the download: the manifest is resolved and its layer sizes summed BEFORE any layer blob is fetched, so an oversized artifact is rejected without buffering it. This is the DoS/SSRF size guard — a malicious registry cannot make us pull an unbounded body.

func PullSnapshotArtifact

func PullSnapshotArtifact(ctx context.Context, cfg ORASPullConfig, registryRef, dstDir string) error

PullSnapshotArtifact downloads a §4.8.1 mem.snap directory from AOCR into dstDir.

func PushModuleArtifact

func PushModuleArtifact(ctx context.Context, a ModuleAuth, wasmPath, registryRef string) (digest string, err error)

PushModuleArtifact uploads a single local .wasm file to registryRef as a one-layer OCI artifact. Returns the manifest digest. wasmPath must already be a validated core module (caller runs ValidateFile).

func PushSnapshotArtifact

func PushSnapshotArtifact(ctx context.Context, cfg ORASPushConfig, memSnapDir, registryRef string) (digest string, err error)

PushSnapshotArtifact uploads a local mem.snap directory to an OCI registry.

func ResolveModuleManifestDigest

func ResolveModuleManifestDigest(ctx context.Context, a ModuleAuth, registryRef string) (string, error)

ResolveModuleManifestDigest performs a credentialed manifest HEAD/GET and returns the manifest content digest WITHOUT downloading any layer blob. It is the cheap, per-caller authorization + cache-key step the resolver runs before a full pull: every caller still authenticates to the registry here (so a cache shortcut never bypasses registry authz), but only ONE of N concurrent duplicate creates goes on to download the blobs (codex P0 single-flight).

func ValidateFile

func ValidateFile(path string) error

ValidateFile checks the WASM magic header, the core-vs-component layer, and the size bounds. A WASM binary's 8-byte preamble is 4 magic bytes + a 4-byte version/layer field: core modules carry layer 0x0000 (version u32 = 1), components carry layer 0x0001 (bytes 6–7 == 0x01 0x00). We reject the latter.

func WasmCheckpointDigestTag

func WasmCheckpointDigestTag(digest string) string

WasmCheckpointDigestTag normalizes a manifest digest for use as an OCI tag.

func WasmCheckpointRef

func WasmCheckpointRef(host, clusterID, sandboxID string) string

WasmCheckpointRef is the AOCR destination for a durable WASM checkpoint (:latest).

func WasmCheckpointRefTagged

func WasmCheckpointRefTagged(host, clusterID, sandboxID, tag string) string

WasmCheckpointRefTagged builds an AOCR ref with an explicit tag (digest or latest).

func WriteCheckpointWasm

func WriteCheckpointWasm(t *testing.T, dir, name string) string

WriteCheckpointWasm writes a module with exported memory for checkpoint tests.

func WriteMinimalWasm

func WriteMinimalWasm(t *testing.T, dir, name string) string

WriteMinimalWasm writes a compile-ready empty _start module into dir/name.

Types

type ModuleAuth

type ModuleAuth struct {
	Username string
	PAT      string
	PATPath  string
}

ModuleAuth carries the registry credentials for a BYO module pull/push. Username is the AOCR login.

Two credential sources, by design:

  • PAT: an inline token. This is the PER-TENANT path — the token rides the create/push request (req.Registry.Password) and is never persisted in plaintext. Thousands of tenants each supply their own; nothing is configured globally.
  • PATPath: a token file. This is the SYSTEM path — the operator's identity for pulling the curated standard modules. Set once, re-read each call so rotation needs no restart.

PAT wins when both are set.

type ModuleResolver

type ModuleResolver struct {

	// CacheDir holds pulled oci:// modules as <digest>.wasm. Distinct from the
	// checkpoint root so eviction never walks a passivated sandbox's mem.snap.
	CacheDir string
	// Reserved maps a standard keyword to its staged filename under ModulesDir
	// (e.g. "python" -> "python.wasm"). Provisioned by Ansible, identical fleet
	// wide; never DB-backed (codex C1).
	Reserved map[string]string
	// Allowlist is the set of registry hosts an oci:// ref may target. Empty
	// means "deny all remote pulls" — the safe default until configured.
	Allowlist map[string]struct{}
	// PullTimeout bounds a single oci:// pull so it cannot stall sandbox boot.
	PullTimeout time.Duration
	// MaxBytes caps a pulled artifact (mirrors ValidateFile's 256MiB).
	MaxBytes int64
	// Auth carries the default registry credentials for oci:// pulls. A
	// per-tenant override can be passed to ResolveWithAuth (failover, BYO).
	Auth ModuleAuth
	// CatalogueLookup resolves a BYO catalogue id to an already-local path +
	// digest. Optional; nil disables step 3.
	CatalogueLookup func(ctx context.Context, id string) (path, digest string, ok bool)
	// contains filtered or unexported fields
}

ModuleResolver is the single resolution chokepoint for every module_ref shape. ALL callers (sandbox create, CreateWasmModule, start/rehydrate by pinned digest, failover) go through Resolve so that allowlist enforcement, core-wasip1 validation, digest computation, and atomic cache publishing happen in exactly one place. Splitting this logic across call sites is how the SSRF allowlist or the validation gets silently skipped on one path.

Resolution precedence (a single ref grammar with explicit ordering — see codex C3, the alias/bare-file collision):

  1. reserved keyword ("python") -> ModulesDir/<staged filename>
  2. oci:// ref ("oci://h/r:t") -> allowlist -> pull -> cache/<digest>.wasm
  3. catalogue id (BYO digest id) -> CatalogueLookup -> local path
  4. file:// / abs / bare filename -> ModulesDir (legacy Resolver)

Reserved keywords win over a same-named bare file on disk, so a stray ModulesDir/python file can never shadow the standard "python" runtime.

func NewModuleResolver

func NewModuleResolver(modulesDir, cacheDir string) *ModuleResolver

NewModuleResolver builds a chokepoint over modulesDir. cacheDir defaults to modulesDir/cache when empty.

func (*ModuleResolver) Resolve

func (r *ModuleResolver) Resolve(ctx context.Context, ref string) (*ResolvedModule, error)

Resolve resolves ref using the default Auth.

func (*ModuleResolver) ResolveByDigest

func (r *ModuleResolver) ResolveByDigest(digest string) (*ResolvedModule, bool)

ResolveByDigest returns the content-addressed cached module for digest, if a frozen copy exists in CacheDir. start/rehydrate use this to boot the EXACT bytes pinned at create rather than re-resolving a mutable alias/tag (codex C2). Reports ok=false on a cache miss; the caller then falls back to ref resolution with a digest-match assertion.

func (*ModuleResolver) ResolveWithAuth

func (r *ModuleResolver) ResolveWithAuth(ctx context.Context, ref string, authOverride *ModuleAuth) (*ResolvedModule, error)

ResolveWithAuth resolves ref, using authOverride for an oci:// pull when non-nil (failover hands the original tenant's sealed creds here so a private module re-pulls on the new owner — codex C4).

func (*ModuleResolver) SetDigestMode added in v0.5.5

func (r *ModuleResolver) SetDigestMode(mode string)

SetDigestMode switches verify-once vs always-hash for local file resolves.

type ORASPullConfig

type ORASPullConfig struct {
	Host      string
	ClusterID string
	PATPath   string
}

ORASPullConfig wires AOCR auth for WASM checkpoint pull (failover-from-snapshot).

func (ORASPullConfig) Validate

func (c ORASPullConfig) Validate() error

Validate enforces required fields when pull is requested.

type ORASPushConfig

type ORASPushConfig struct {
	Host      string
	ClusterID string
	PATPath   string
}

ORASPushConfig wires AOCR auth for WASM checkpoint push. Mirrors the snapshot-push PAT file semantics: token is re-read on every call.

func (ORASPushConfig) Validate

func (c ORASPushConfig) Validate() error

Validate enforces required fields when push is requested.

type ResolvedModule

type ResolvedModule struct {
	Ref       string
	Path      string
	Digest    string // sha256 hex
	SizeBytes int64
}

ResolvedModule is a validated local .wasm artifact ready for the engine.

type Resolver

type Resolver struct {
	ModulesDir string
	// DigestMode controls per-resolve hashing: once (default) or always.
	DigestMode string
	// contains filtered or unexported fields
}

Resolver maps a module reference to a local .wasm path under modulesDir.

func NewResolver

func NewResolver(modulesDir string) *Resolver

NewResolver constructs a resolver. modulesDir is the host cache root (SB_WASM_MODULES_DIR).

func (*Resolver) InvalidateDigestCache added in v0.5.5

func (r *Resolver) InvalidateDigestCache(path string)

InvalidateDigestCache drops verify-once entries for path after module delete/replace.

func (*Resolver) Resolve

func (r *Resolver) Resolve(_ context.Context, ref string) (*ResolvedModule, error)

Resolve turns ref into a local file path. Phase 2 accepts:

  • absolute paths to .wasm files
  • file:// URLs
  • bare filenames or relative paths under modulesDir

func (*Resolver) SetDigestMode added in v0.5.5

func (r *Resolver) SetDigestMode(mode string)

SetDigestMode switches verify-once vs always-hash behavior (SB_WASM_MODULE_DIGEST_MODE).

Jump to

Keyboard shortcuts

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