Files
base/plugins/extruntime/extruntime.go
T
zeekay e7df8c856e base: complete pocketbase→base rename; Base = pure IAM client
Rename / namespace:
- Finish github.com/pocketbase/* → github.com/hanzoai/base (+ /dbx) sweep
  across all Go sources; restore the renamed root launcher (Base, base.go).
- Drop the upstream-only surfaces ours doesn't ship (admin ui/, the
  password/OTP/oauth2/impersonate auth files, extensions) re-added by the
  upstream merge.
- Decomplect the on-disk layout to one base_/ namespace: data/, migrations/,
  hooks/, types.d.ts are siblings under base_/ (default data dir
  base_/data). Drop the hz_ prefix everywhere; collapse migratecmd's
  identical JS/Go branches to one path.
- Remove the accidentally-committed hz_data/ dev dir (db + iam.key) and
  gitignore the runtime namespace dirs.

Deps / build:
- kmsclient now imported from the light github.com/hanzoai/kms/sdk/go module
  (the root kms module zip exceeds Go's download limit); cloud/pkg/base
  Mount removed (lives in the fused daemon).
- replicate v0.4.0 → v0.8.0; metric.Counter.Get() (no prometheus dto);
  network/metrics.go import fix; auth gitlab/github tidy.

Tests / behavior:
- Wire registerNotifyWatcherHooks; debounce notify files past the kqueue
  rescan window so cross-instance settings/collections reload fires.
- migratecmd: exclude createdAt/updatedAt (not created/updated); regenerate
  Go+JS automigration goldens from canonical output.
- Convert the two stale archive red-probes to assert the landed defenses
  (backlog cap bounds memory; forged-segment signature is rejected).

IAM-native (Base = pure IAM client):
- Rip the ~1k-line home-grown embedded IAM (iam_embedded*.go) and the
  iam-user CLI. Base no longer reinvents identity as _iam_users/_superusers
  rows. IAM_ENDPOINT (hanzo.id, or an in-process iam.Embed in the fused
  daemon) is required; Base validates IAM JWTs via JWKS only — superuser is
  the IAM admin claim, resolved to an ephemeral unpersisted record.
- Extract the IDV reverse-proxy (/v1/idv/*) into idv_proxy.go — it was
  mis-colocated in the embedded onboarding file and is independent of IAM.
- Bind the SQL console (/v1/sql) and test it through real IAM JWTs against a
  JWKS stub (no hardcoded record tokens, no persisted rows).
2026-06-10 21:29:14 -07:00

115 lines
4.3 KiB
Go

// Package extruntime defines the pluggable extension runtime interface used
// by Base's extension subsystem. Each runtime (goja, wazero, v8go, native Go)
// satisfies this interface so an extension declares its preferred runtime in
// its manifest and Base loads it without caring how the code executes.
//
// This sits ALONGSIDE the existing plugins/jsvm goja runtime — it doesn't
// replace it. Goja remains the default for .base.js / .base.ts hook files.
// The other runtimes are opt-in via an `extension.json` manifest:
//
// # hooks/validate-email/extension.json
// {
// "name": "validate-email",
// "version": "0.1.0",
// "runtime": "wazero",
// "module": "validate.wasm",
// "exports": ["onCreate", "onUpdate"]
// }
package extruntime
import (
"context"
"errors"
)
// Runtime is implemented by every backing engine.
type Runtime interface {
// Name returns the runtime identifier ("native" | "goja" | "wazero" | "v8go").
Name() string
// Capabilities returns metadata about what this runtime can do.
Capabilities() Capabilities
// Load reads an extension package from disk and returns a Module
// instance. The path is the directory containing the extension.json
// manifest. Implementations should pre-compile / pre-instantiate
// here so the per-invocation Invoke cost is minimized.
Load(ctx context.Context, dir string) (Module, error)
// Close releases any runtime-level resources (compilation cache, etc.).
// Modules created by this runtime should already be Close()d by callers.
Close() error
}
// Module is one loaded extension. It exposes named functions.
type Module interface {
// Name returns the extension name from its manifest.
Name() string
// Runtime returns the runtime that loaded this module.
Runtime() string
// Exports returns the list of function names this module declares
// in its manifest. Invoke may still be called with any name — the
// runtime decides whether to error or no-op.
Exports() []string
// Invoke executes the named function with a JSON-encoded payload
// and returns a JSON-encoded result. Payloads are bytes so the
// runtime layer doesn't impose a wire format on the host code —
// callers serialize once and let every runtime see the same bytes.
//
// Context cancellation must abort the running invocation; long-
// running extension code that doesn't respect ctx is the
// extension author's bug, not the runtime's. wazero and v8go can
// hard-abort; goja and native rely on cooperative ctx checking.
Invoke(ctx context.Context, fn string, payload []byte) ([]byte, error)
// Close releases the module's resources (compiled wasm, isolate,
// goja runtime, etc.). After Close, Invoke must error.
Close() error
}
// Capabilities describes what a runtime supports — used so callers can
// decide whether to load a given extension or skip it (e.g. v8go-only
// builds shouldn't try to load AssemblyScript wasm).
type Capabilities struct {
// AcceptsLanguages lists the source languages this runtime can run.
// native: ["go"]
// goja: ["js"]
// wazero: ["wasm"] (compile any-lang-to-wasm ahead of time)
// v8go: ["js"]
AcceptsLanguages []string
// HardSandbox is true if the runtime enforces a memory-isolated
// sandbox (wazero linear memory, v8go isolate). goja is false
// (shares Go heap with host); native is N/A (it IS the host).
HardSandbox bool
// Cgo indicates the runtime requires cgo to build. Affects binary
// distribution and cross-compilation.
Cgo bool
// SupportsAbort is true if a context cancellation can interrupt a
// running invocation without cooperative checks in the guest code.
SupportsAbort bool
}
// Errors returned at the boundary. Wrappers should keep using errors.Is.
var (
ErrNoManifest = errors.New("extruntime: extension.toml not found in directory")
ErrBadManifest = errors.New("extruntime: extension.toml is invalid")
ErrUnknownFn = errors.New("extruntime: function not found in module")
ErrClosed = errors.New("extruntime: module is closed")
ErrUnsupported = errors.New("extruntime: runtime does not support this operation")
)
// Manifest is the parsed extension.json contents.
type Manifest struct {
Name string `json:"name"`
Version string `json:"version"`
Runtime string `json:"runtime"`
Module string `json:"module"`
Exports []string `json:"exports"`
}