extensions

package
v0.1.0-alpha.1 Latest Latest
Warning

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

Go to latest
Published: Nov 8, 2026 License: AGPL-3.0 Imports: 29 Imported by: 0

Documentation

Overview

Package extensions loads, verifies and runs Ammonode extensions: a signed zip package (.ammx) with a manifest, an optional WebAssembly backend run by Extism, and an optional browser module that fills UI slots.

Index

Constants

View Source
const (
	FnOnEvent         = "on_event"         // input {event, data}
	FnGatewayCheckout = "gateway_checkout" // input CheckoutInput, output CheckoutOutput
	FnGatewayWebhook  = "gateway_webhook"  // input WebhookInput, output WebhookOutput

	// Provisioners: input ProvisionInput; create returns ProvisionOutput, the others nothing.
	FnProvisionCreate    = "provision_create"
	FnProvisionSuspend   = "provision_suspend"
	FnProvisionUnsuspend = "provision_unsuspend"
	FnProvisionTerminate = "provision_terminate"
	FnProvisionChange    = "provision_change" // new memory/CPU/disk
)

Exported functions the core calls when a plugin has them.

View Source
const APIVersion = "1.0.0"

APIVersion is the extension API the core offers. Bump the minor version for additions and the major version for breaking changes (extensions declare a range in "api").

Variables

View Source
var ErrNotLoaded = errors.New("extension not loaded")

ErrNotLoaded means the extension is not running (disabled, or without a backend).

View Source
var Events = []string{
	"user.registered", "invoice.created", "invoice.paid",
	"service.provisioned", "subscription.suspended", "subscription.ended",
}

Events the core emits (permissions.events must be a subset).

Functions

func Digest

func Digest(files map[string][]byte) []byte

Digest is a stable hash of the package content: sha256 over "name\nsha256hex\n" lines sorted by name. Zip metadata (dates, order, compression) does not matter.

func Satisfies

func Satisfies(v Version, rng string) (bool, error)

Satisfies reports whether v matches a range: "^1.2" (same major, ≥ 1.2.0; for 0.x the same minor), "~1.2" (same minor), "1.x" / "1.2.x", ">=1.0 <2.0" (space-separated comparators, all must hold), "*", or an exact version.

func Scaffold

func Scaffold(dir, id, name string) error

Scaffold writes a new extension project to dir (which must not exist or be empty).

func WritePackage

func WritePackage(files map[string][]byte, key ed25519.PrivateKey) ([]byte, error)

WritePackage zips files (name → content), signing them when key is set.

Types

type CheckoutInput

type CheckoutInput struct {
	Invoice    InvoiceInfo `json:"invoice"`
	Customer   Customer    `json:"customer"`
	ReturnURL  string      `json:"return_url"`
	CancelURL  string      `json:"cancel_url"`
	WebhookURL string      `json:"webhook_url"`
}

CheckoutInput is sent to gateway_checkout.

type CheckoutOutput

type CheckoutOutput struct {
	RedirectURL string `json:"redirect_url"`
}

CheckoutOutput is where to send the customer.

type Customer

type Customer struct {
	Email string `json:"email"`
	Name  string `json:"name"`
}

Customer is who pays.

type Gateway

type Gateway struct {
	Name       string   `json:"name"`       // shown on the pay button
	Currencies []string `json:"currencies"` // empty = any
}

Gateway describes a payment gateway extension.

func (*Gateway) SupportsCurrency

func (g *Gateway) SupportsCurrency(c string) bool

SupportsCurrency reports whether a gateway extension takes the currency.

type HTTPRequest

type HTTPRequest struct {
	Method  string            `json:"method"`
	URL     string            `json:"url"`
	Headers map[string]string `json:"headers,omitempty"`
	Body    string            `json:"body,omitempty"`
}

HTTPRequest is the input of the http_request host function.

type HTTPResponse

type HTTPResponse struct {
	Status  int               `json:"status"`
	Headers map[string]string `json:"headers,omitempty"`
	Body    string            `json:"body"`
	Error   string            `json:"error,omitempty"`
}

HTTPResponse is its output; Error is set when the request was not made or failed.

type Host

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

Host keeps compiled plugins and calls them.

func NewHost

func NewHost(opts HostOptions) *Host

NewHost creates the runtime. Plugins make HTTP requests through the http_request host function (Extism's built-in HTTP is switched off: it uses http.DefaultClient, which cannot be restricted per plugin); its client refuses private and loopback addresses, so an allowed host name cannot be pointed at the panel's own network.

func (*Host) Call

func (h *Host) Call(ctx context.Context, id, fn string, input, out any) error

Call runs fn with JSON input and decodes the JSON output into out (when not nil). Each call gets a fresh instance, so plugins keep no memory between calls (use kv for that).

func (*Host) Exports

func (h *Host) Exports(id, fn string) bool

Exports reports whether a loaded plugin exports fn.

func (*Host) Load

func (h *Host) Load(ctx context.Context, m Manifest, wasm []byte, settings map[string]string) error

Load compiles a plugin (replacing a loaded one with the same ID). settings become the plugin's config (pdk.GetConfig).

func (*Host) Unload

func (h *Host) Unload(ctx context.Context, id string)

Unload removes a plugin.

type HostOptions

type HostOptions struct {
	Logger *slog.Logger
	KV     KV
	// AllowPrivateNetworks lets plugin HTTP reach private/loopback addresses (tests only).
	AllowPrivateNetworks bool
}

HostOptions configure the runtime.

type InvoiceInfo

type InvoiceInfo struct {
	ID       string `json:"id"`
	Number   string `json:"number"`
	Total    int64  `json:"total"`
	Currency string `json:"currency"`
}

InvoiceInfo describes the invoice to pay. Amounts are in the smallest currency unit.

type KV

type KV interface {
	Get(ctx context.Context, ext, key string) ([]byte, bool, error)
	Set(ctx context.Context, ext, key string, value []byte) error
}

KV is per-extension storage behind the kv_get / kv_set host functions.

type Manifest

type Manifest struct {
	ID          string      `json:"id"`
	Name        string      `json:"name"`
	Version     string      `json:"version"`
	API         string      `json:"api"` // semver range of the core API, e.g. "^1.0"
	Description string      `json:"description,omitempty"`
	Author      string      `json:"author,omitempty"`
	Homepage    string      `json:"homepage,omitempty"`
	Backend     string      `json:"backend,omitempty"`  // path of the .wasm in the package
	Frontend    string      `json:"frontend,omitempty"` // path of the ES module in the package
	Permissions Permissions `json:"permissions"`
	Settings    []Setting   `json:"settings,omitempty"`
	Gateway     *Gateway    `json:"gateway,omitempty"` // set when the extension is a payment gateway
	// Provisioner is set when the extension delivers products on another system (a
	// Pterodactyl or Pelican panel, Proxmox, ...).
	Provisioner *Provisioner `json:"provisioner,omitempty"`
}

Manifest is extension.json.

func PackDir

func PackDir(dir string) (Manifest, map[string][]byte, error)

PackDir reads an extension project and returns the files that go into its package: extension.json, the backend and frontend it names, and README.md / LICENSE if present.

func ParseManifest

func ParseManifest(raw []byte) (Manifest, error)

ParseManifest decodes and validates extension.json.

func (Manifest) Validate

func (m Manifest) Validate() error

Validate checks the manifest, including that the core API satisfies its range.

type Package

type Package struct {
	Manifest Manifest
	Files    map[string][]byte // every file except the signature
	Signer   string            // base64 public key of a valid signature, "" if unsigned
}

Package is a read package.

func ReadPackage

func ReadPackage(raw []byte) (*Package, error)

ReadPackage unpacks and validates a package. A present but invalid signature is an error; whether unsigned packages are acceptable is the caller's decision (Signer == "").

type Permissions

type Permissions struct {
	// HTTP lists hosts the plugin may call: exact, "*.example.com", or "setting:<key>" for
	// the host of a URL the admin enters in that setting (e.g. a self-hosted panel).
	HTTP   []string `json:"http,omitempty"`
	Events []string `json:"events,omitempty"` // core events delivered to on_event
	KV     bool     `json:"kv,omitempty"`     // a small key/value store
}

Permissions are what the backend may do; everything else is denied.

type ProvisionInput

type ProvisionInput struct {
	Subscription ProvisionSubscription `json:"subscription"`
	User         ProvisionUser         `json:"user"`
	Product      ProvisionProduct      `json:"product"`
	// ExternalID is what provision_create returned (empty for create).
	ExternalID string `json:"external_id"`
}

ProvisionInput is what a provisioner gets for every call.

type ProvisionOutput

type ProvisionOutput struct {
	ExternalID string `json:"external_id"`
	// Info is shown to the customer: a link to manage it, and a few details.
	Info struct {
		URL     string            `json:"url,omitempty"`
		Details map[string]string `json:"details,omitempty"`
	} `json:"info"`
}

ProvisionOutput identifies the delivered thing on the other system.

type ProvisionProduct

type ProvisionProduct struct {
	ID       string            `json:"id"`
	Name     string            `json:"name"`
	Settings map[string]string `json:"settings"`
}

ProvisionProduct carries the product's provisioner settings.

type ProvisionSubscription

type ProvisionSubscription struct {
	ID         string `json:"id"`
	Name       string `json:"name"`
	Cycle      string `json:"cycle"`
	MemoryMB   int    `json:"memory_mb"`
	CPUPercent int    `json:"cpu_percent"`
	DiskMB     int    `json:"disk_mb"`
}

ProvisionSubscription is the purchase being delivered.

type ProvisionUser

type ProvisionUser struct {
	ID    string `json:"id"`
	Email string `json:"email"`
	Name  string `json:"name"`
}

ProvisionUser is the customer.

type Provisioner

type Provisioner struct {
	Name     string    `json:"name"`
	Settings []Setting `json:"settings,omitempty"`
}

Provisioner describes a provisioner extension. Settings are per product (e.g. the egg or VM template); the extension's own settings hold the connection.

type Setting

type Setting struct {
	Key      string `json:"key"`
	Label    string `json:"label"`
	Type     string `json:"type"` // text | secret | boolean
	Required bool   `json:"required,omitempty"`
	Help     string `json:"help,omitempty"`
}

Setting is a value the admin fills in; the backend reads it as plugin config.

type Version

type Version struct{ Major, Minor, Patch int }

Version is a semantic version (pre-release and build suffixes are ignored for ordering).

func ParseVersion

func ParseVersion(s string) (Version, error)

ParseVersion parses "1", "1.2" or "1.2.3" (a leading "v" is allowed).

func (Version) Compare

func (v Version) Compare(o Version) int

Compare returns -1, 0 or 1.

func (Version) String

func (v Version) String() string

type WebhookInput

type WebhookInput struct {
	Method  string              `json:"method"`
	Headers map[string][]string `json:"headers"`
	Query   map[string][]string `json:"query"`
	Body    string              `json:"body"`
}

WebhookInput is an HTTP request to /api/webhooks/ext/{id}.

type WebhookOutput

type WebhookOutput struct {
	Status    string `json:"status"`
	InvoiceID string `json:"invoice_id"`
	Ref       string `json:"ref"`
	Amount    int64  `json:"amount"`
	Currency  string `json:"currency"`
	// RedirectURL continues a browser flow (GET) on a page of the panel.
	RedirectURL string `json:"redirect_url,omitempty"`
}

WebhookOutput reports a payment ("paid") or nothing ("ignored").

Jump to

Keyboard shortcuts

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