Documentation
¶
Index ¶
- Constants
- Variables
- func CompiledInNames() []string
- func Logger(ctx context.Context) logger.Logger
- func MatchGlob(pattern, name string) bool
- func NewControlLibrary(manager *Manager, registrar Registrar, scriptRegistrar ScriptLibraryRegistrar, ...) *object.Library
- func NormalizeLibraryName(name string) string
- func RegisterClientLibrary(registrar Registrar, client *Client)
- func RegisterCompiledIn(name, description string, build CompiledInBuild)
- func RegisterLibraries(registrar Registrar, manager *Manager, policy ...*Policy)
- func Release(obj object.Object) error
- func ReleaseWithContext(ctx context.Context, obj object.Object) error
- type Callback
- type CallbackRef
- type ClassSchema
- type Client
- func (c *Client) Batch(ctx context.Context, requests []batchRequest) ([]json.RawMessage, error)
- func (c *Client) Call(ctx context.Context, method string, params any, out any) error
- func (c *Client) CallFunction(ctx context.Context, name string, args []Value, kwargs map[string]Value) (Value, error)
- func (c *Client) CallFunctionWithCallbacks(ctx context.Context, name string, args []Value, kwargs map[string]Value, ...) (Value, error)
- func (c *Client) CallMethod(ctx context.Context, objectID, method string, args []Value, ...) (Value, error)
- func (c *Client) CallMethodWithCallbacks(ctx context.Context, objectID, method string, args []Value, ...) (Value, error)
- func (c *Client) Close() error
- func (c *Client) DestroyObject(ctx context.Context, objectID string) error
- func (c *Client) FetchFile(ctx context.Context, source, path string) ([]byte, error)
- func (c *Client) FetchGlob(ctx context.Context, source, pattern string) ([]FetchEntry, error)
- func (c *Client) HandshakeDone() bool
- func (c *Client) Health() error
- func (c *Client) Metadata() Metadata
- func (c *Client) NewObject(ctx context.Context, class string, args []Value, kwargs map[string]Value) (*RemoteRef, error)
- func (c *Client) NewObjectWithCallbacks(ctx context.Context, class string, args []Value, kwargs map[string]Value, ...) (*RemoteRef, error)
- func (c *Client) Path() string
- func (c *Client) Scheme() string
- func (c *Client) SetName(name string)
- func (c *Client) SupportsFetch() bool
- type CompiledInBuild
- type ConstantSchema
- type FetchEntry
- type Fetcher
- type FunctionSchema
- type LibraryChecker
- type LibraryUnregistrar
- type Manager
- func (m *Manager) AddDir(dir string)
- func (m *Manager) Close() error
- func (m *Manager) Get(name string) (*Client, bool)
- func (m *Manager) Health() map[string]error
- func (m *Manager) List() []Metadata
- func (m *Manager) Load(ctx context.Context) error
- func (m *Manager) LoadPath(ctx context.Context, name, path string, scriptling bool, args []string) (*Client, error)
- func (m *Manager) LoadPlugin(ctx context.Context, path string, args []string) (*Client, error)
- func (m *Manager) LoadPlugins(ctx context.Context, specs []PluginSpec) error
- func (m *Manager) LoadURL(ctx context.Context, name, rawURL string, scriptling, insecureSkipTLS bool, ...) (*Client, error)
- func (m *Manager) NewScope(opts ...ScopeOption) *Manager
- func (m *Manager) SetCrashHandler(handler func(name string, err error))
- func (m *Manager) SetLogger(log logger.Logger)
- func (m *Manager) SetMaxParallelPluginLoads(n int)
- func (m *Manager) SetPolicy(policy *Policy)
- func (m *Manager) Unload(name string) error
- func (m *Manager) Warnings() []string
- type Metadata
- type NetworkPolicy
- type PluginSpec
- type Policy
- type PolicySource
- type PropertySchema
- type RPCError
- type Registrar
- type RemoteRef
- type Schema
- type ScopeOption
- type ScriptLibraryRegistrar
- type Server
- func (s *Server) Constant(name string, value any) *Server
- func (s *Server) ObjectCount() int
- func (s *Server) Policy() *Policy
- func (s *Server) RegisterBuiltin(name string, fn object.BuiltinFunction) *Server
- func (s *Server) RegisterBuiltinClass(name string, class *object.Class) *Server
- func (s *Server) RegisterClass(builder *object.ClassBuilder) *Server
- func (s *Server) RegisterFetcher(scheme string, f Fetcher) *Server
- func (s *Server) RegisterFunc(name string, builder *object.FunctionBuilder) *Server
- func (s *Server) RegisterLibrary(lib *object.Library) *Server
- func (s *Server) RegisterScriptClass(name string, source string) *Server
- func (s *Server) RegisterScriptFunc(name string, source string) *Server
- func (s *Server) Run() error
- func (s *Server) RunIO(input io.Reader, output io.Writer) error
- func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)
- func (s *Server) Wrapper(name string, source string) *Server
- type StaticPolicy
- type TransportMode
- type Value
Constants ¶
const ( // FetchNotFoundCode is the JSON-RPC error code a fetcher returns for a // missing source or path, mapped back to ErrFetchNotFound on the host. FetchNotFoundCode = -32001 // FetchDeniedCode reports an access the fetcher refused: credentials, // permissions. It is permanent, and the host never retries it. FetchDeniedCode = -32002 // (network blip, upstream 503). The host retries these. FetchUnavailableCode = -32003 )
const ( FetchRetryAttempts = 3 FetchRetryDelay = 150 * time.Millisecond )
Fetch retries: fetch operations are idempotent reads, so a transport hiccup or an unavailable backend is retried a bounded number of times with a short linear backoff. Permanent errors (not found, denied) are never retried.
const ( // ProtocolVersion is the JSON-RPC plugin protocol version supported by this host. ProtocolVersion = "1.0" // NamespacePrefix is where plugins declaring a bare name register: a // plugin declaring "hello" becomes plugin.hello, keeping third-party // code visibly namespaced and unable to collide with single-word // built-in libraries. A name containing a dot is used verbatim — its // author owns the namespace ("paul.hello", or "scriptling.sqlite" for // first-party plugins whose imports match compiled-in builds) — and // registration refuses names already taken by another library. NamespacePrefix = "plugin." // PluginPeerEnv is the environment variable set (to the host version) on // every executable spawned as a plugin peer. Multi-role executables check // it to divert a bare invocation into plugin mode without a subcommand. PluginPeerEnv = "SCRIPTLING_PLUGIN_PEER" )
const ControlLibraryName = "scriptling.plugin"
const DefaultFetchTimeout = 30 * time.Second
DefaultFetchTimeout bounds fetch.read / fetch.glob calls when the caller provides no deadline of its own.
const DefaultHandshakeTimeout = 15 * time.Second
DefaultHandshakeTimeout caps how long handshake() will wait for the plugin protocol handshake to complete. It bounds detection of broken or unresponsive plugins while still tolerating slow subprocess startup under load (cold container starts, network filesystems, parallel test execution). The caller's context deadline always takes precedence if it is shorter.
const DefaultMaxParallelPluginLoads = 5
DefaultMaxParallelPluginLoads caps how many plugin processes are started concurrently. Process spawn plus handshake dominates load time, so batches start in parallel, but an unbounded burst of subprocesses would hurt constrained hosts more than sequential loading helps them.
const DefaultReleaseTimeout = 2 * time.Second
DefaultReleaseTimeout is used by Release and GC finalizers when no caller context is available. Use ReleaseWithContext for request-scoped cleanup.
Variables ¶
var ( ErrFetchNotFound = errors.New("fetch source not found") ErrFetchDenied = errors.New("fetch access denied") )
Fetch error sentinels. Fetcher implementations wrap them (fmt.Errorf("%w: ...", ErrFetchNotFound)) and the server transports each as its error code above; the client maps the codes back, so hosts can tell a plain miss from a refusal from a flaky backend.
var ErrManagerClosed = errors.New("plugin manager closed")
ErrManagerClosed is returned by Manager load operations after shutdown has begun. A closed Manager is terminal and cannot be used to load more plugins.
Functions ¶
func CompiledInNames ¶ added in v0.23.0
func CompiledInNames() []string
CompiledInNames returns the sorted names of all registered compiled-in plugins. Hosts use it to keep a discovered external plugin from shadowing (or being shadowed by) a compiled-in one.
func Logger ¶ added in v0.11.0
Logger returns a call-scoped proxy to the manager's host logger. It uses ctx to find the active plugin call runtime, so plugin code should request it from functions, constructors, methods, or property accessors and should not store it globally. If ctx is not attached to an active plugin call, Logger returns a no-op logger.
func MatchGlob ¶ added in v0.23.0
MatchGlob reports whether name, a slash path relative to a source root, matches pattern in the fetch glob language. Fetcher implementations can serve Glob by matching their known paths with this helper instead of reimplementing the semantics.
func NewControlLibrary ¶
func NewControlLibrary(manager *Manager, registrar Registrar, scriptRegistrar ScriptLibraryRegistrar, unregistrar LibraryUnregistrar) *object.Library
func NormalizeLibraryName ¶
NormalizeLibraryName returns the library name a script imports. A bare name registers under the plugin namespace ("hello" -> "plugin.hello"); a dotted name is the author's namespace and is used verbatim ("paul.hello", "scriptling.sqlite").
func RegisterClientLibrary ¶ added in v0.15.0
RegisterClientLibrary registers the proxy library for a single already-handshaked client. Useful when you have a Client obtained via LoadClientFromIO or similar and want to expose it without a full Manager.
func RegisterCompiledIn ¶ added in v0.23.0
func RegisterCompiledIn(name, description string, build CompiledInBuild)
RegisterCompiledIn records a compiled-in plugin. It is called from init() in build-tag-guarded files (e.g. //go:build plugin_sqlite), so a binary only carries the plugins its build flags selected. Registering the same name twice panics: it means two tagged files disagree.
func RegisterLibraries ¶
Types ¶
type Callback ¶
Callback is a host callback passed into a plugin call. It is valid only until the outer plugin function, constructor, or method returns.
type CallbackRef ¶
type CallbackRef struct {
ID string `json:"id"`
}
CallbackRef identifies a callback valid during one outer plugin call.
type ClassSchema ¶
type ClassSchema struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Constructor FunctionSchema `json:"constructor,omitempty"`
Methods []FunctionSchema `json:"methods,omitempty"`
Properties []PropertySchema `json:"properties,omitempty"`
Source string `json:"source,omitempty"`
}
ClassSchema describes a plugin class and its methods.
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a connection to a loaded plugin. Over stdio it is a bidirectional JSON-RPC peer (built on jsonrpc.Peer) — outbound calls to the plugin and inbound host callbacks (callback.call, host.log) share one stream. Over HTTP it is a unidirectional JSON-RPC client (built on jsonrpc.HTTPTransport); callbacks are not available over HTTP.
func LoadClient ¶ added in v0.12.1
LoadClient spawns an executable and performs the plugin protocol handshake. The returned client has Metadata populated from the handshake result. args, if non-empty, are passed as command-line arguments to the executable. A standalone client carries no host security policy; managers that want one delivered should load through a Manager with SetPolicy.
func LoadClientFromIO ¶ added in v0.15.0
func LoadClientFromIO(ctx context.Context, in io.ReadCloser, out io.WriteCloser) (*Client, error)
LoadClientFromIO connects to a plugin server over an existing bidirectional stream and performs the plugin protocol handshake. Use this when the server is already running and accessible via in-process pipes (e.g. tests, embedded servers). The caller is responsible for closing in/out when done.
func SpawnClient ¶ added in v0.12.1
SpawnClient spawns an executable without performing the plugin handshake. The caller is responsible for any handshake exchange via Call. args, if non-empty, are passed as command-line arguments to the executable.
func (*Client) Batch ¶ added in v0.12.1
Batch sends multiple raw JSON-RPC requests in one batch frame and returns results in the same order as requests. Batch does not support host callbacks.
func (*Client) Call ¶ added in v0.12.1
Call sends a raw JSON-RPC request to the executable and unmarshals the result into out (which may be nil to ignore the result). params may be any JSON-marshalable value (struct, map, slice, scalar). It is the low-level building block for non-plugin JSON-RPC peers; plugin callers should prefer CallFunction / NewObject / CallMethod which use the plugin method names.
func (*Client) CallFunction ¶
func (*Client) CallFunctionWithCallbacks ¶
func (*Client) CallMethod ¶
func (*Client) CallMethodWithCallbacks ¶
func (*Client) Close ¶
Close shuts down this plugin process. The plugin.shutdown notification is best-effort: peers that do not implement the plugin protocol (e.g. raw JSON-RPC executables loaded via LoadPath(scriptling=false)) will return a method-not-found error which is intentionally ignored. Handshaken Scriptling plugins still report shutdown RPC errors. Real failures — the process not exiting, or exiting with a non-zero status — are also reported.
func (*Client) DestroyObject ¶
func (*Client) FetchFile ¶ added in v0.23.0
FetchFile reads one file from a source. path is a slash path relative to the source; an empty path denotes a source that is itself a single file (a script). Transport failures and unavailable backends are retried a bounded number of times (fetch reads are idempotent); permanent errors are not.
func (*Client) FetchGlob ¶ added in v0.23.0
FetchGlob returns the entries of a source whose paths match pattern in the fetch glob language (see MatchGlob). No match is an empty result; a missing source is an error wrapping ErrFetchNotFound. Retried like FetchFile.
func (*Client) HandshakeDone ¶ added in v0.12.1
HandshakeDone reports whether the plugin protocol handshake was completed. call_function uses this to route automatically: handshook clients use the typed plugin transport (function.call), non-handshook clients send the method name directly as a raw JSON-RPC request.
func (*Client) NewObjectWithCallbacks ¶
func (*Client) Path ¶ added in v0.12.1
Path returns the filesystem path of the executable this client runs.
func (*Client) Scheme ¶ added in v0.23.0
Scheme returns the source scheme the peer's fetcher serves, as advertised in its handshake. Empty when the plugin has no fetcher.
func (*Client) SetName ¶ added in v0.12.1
SetName overrides the library name used to register this client. It is only meaningful before a client is added to a Manager and is intended for raw (non-plugin-handshake) clients whose name would otherwise be empty.
func (*Client) SupportsFetch ¶ added in v0.23.0
SupportsFetch reports whether this peer has a fetcher: a non-empty scheme in the handshake says so, which is the whole advertisement. Fetch calls are refused (without contacting the peer) when it does not, so plugins without fetchers keep working unchanged.
type CompiledInBuild ¶ added in v0.23.0
CompiledInBuild produces the two halves a compiled-in plugin registers: a native library and, when non-empty, the script source of the user-facing module. The native library registers under a twin name (scriptling._sqlite) and the script module under the public name (scriptling.sqlite), so scripts see exactly the namespace an external plugin of the same name would give them — and script-defined surfaces (like the ORM kit) execute host-side in both modes.
policy is the host security context for the interpreter being configured; nil means no restrictions.
type ConstantSchema ¶
ConstantSchema describes a plugin constant.
type FetchEntry ¶ added in v0.23.0
FetchEntry is one match returned by Fetcher.Glob: Name is the entry's slash path relative to the source root (full path, not a bare base name).
func GlobDisk ¶ added in v0.23.0
func GlobDisk(root, pattern string) ([]FetchEntry, error)
GlobDisk is the reference Glob for a fetcher serving files from a directory: it walks root and returns every entry whose path relative to root matches pattern (directories included, with is_dir set).
Symlink defense comes built in: each symlink encountered is resolved to its real path and must stay inside root, so a link planted in the served tree cannot serve files from outside it. Directory symlinks are not followed (the walk would leave root); file symlinks that resolve inside root are served as files. Fetchers with richer backends implement Glob themselves but owe their users the same containment guarantee.
type Fetcher ¶ added in v0.23.0
type Fetcher interface {
Read(ctx context.Context, source, path string) ([]byte, error)
Glob(ctx context.Context, source, pattern string) ([]FetchEntry, error)
}
Fetcher serves file content for sources under a registered scheme. Read is called with the full source string and a slash path relative to it (empty for a source that denotes a single file, such as a script) and returns the file's bytes; an error wrapping ErrFetchNotFound is a miss. Data travels base64-encoded on the wire so binary assets survive intact. There is no conditional-read machinery — the host does not cache what a plugin serves, so a plugin whose backend is slow caches behind its own Read, where the freshness rules live; the host stays a dumb pipe.
Glob matches a pattern in the fetch glob language (see MatchGlob) against the source's tree and returns every match, directories included. It answers in one call what a directory-by-directory walk would need one round trip per level for, which is the point: existence is a wildcard-free pattern, a listing is "<dir>/*", a whole subtree is "<dir>/**". No match is an empty result, never an error; errors mean the fetcher could not answer. The MatchGlob and GlobDisk helpers implement the semantics for in-memory and disk-backed fetchers respectively.
type FunctionSchema ¶
type FunctionSchema struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Source string `json:"source,omitempty"`
}
FunctionSchema describes a plugin function or supplied wrapper source.
type LibraryChecker ¶ added in v0.23.0
LibraryChecker is implemented by registrars that can report whether a library name is already taken; the plugin loader refuses plugins whose verbatim (dotted) name collides with it.
type LibraryUnregistrar ¶ added in v0.12.1
type Manager ¶
type Manager struct {
// contains filtered or unexported fields
}
func NewManager ¶
NewManager creates an empty plugin manager. If log is not nil, plugin log records emitted through Logger(ctx) are forwarded to it. If crashHandler is provided, it is called when a loaded plugin process exits unexpectedly.
func (*Manager) AddDir ¶
AddDir adds a directory whose executable files should be loaded as plugins.
func (*Manager) Close ¶
Close shuts down all loaded plugin processes and clears the local client map. For scoped managers this fully releases all locally loaded plugins without touching the parent's plugins. Close is safe to call concurrently and more than once; every caller waits for the same shutdown and receives its result.
func (*Manager) Get ¶
Get returns a loaded plugin client by short or fully-qualified library name. It checks the local map first; if not found and this is a scope with a parent, it falls back to the parent (and so on up the chain). Local always wins.
func (*Manager) Health ¶
Health returns loaded plugins whose process or stdio transport is unhealthy.
func (*Manager) List ¶
List returns metadata for all loaded plugins sorted by library name. If this Manager is a scope, parent plugins are included in the result. A child scope cannot load a name that an ancestor already owns, so name collisions between local and parent are not possible; the seen-map guard is kept as a safety net in case of direct manager manipulation.
func (*Manager) Load ¶
Load eagerly starts all executable plugins in configured plugin directories. The starts run concurrently, capped at SetMaxParallelPluginLoads, while registration stays in directory order so naming behaves exactly as sequential loading: the first executable declaring a library name wins.
func (*Manager) LoadPath ¶ added in v0.12.1
func (m *Manager) LoadPath(ctx context.Context, name, path string, scriptling bool, args []string) (*Client, error)
LoadPath starts a single executable, or connects to an http(s) JSON-RPC endpoint, and registers it under name. Executable identity is by absolute path; HTTP identity is by URL.
If scriptling is true, the plugin protocol handshake is performed and the client can be driven through CallFunction / CallMethod / call_function / call_method. If scriptling is false, the handshake is skipped and call_function sends the function name directly as the JSON-RPC method.
args, if non-empty, are passed as command-line arguments to the executable (e.g. ["--json-rpc", "./setup.py"] when spawning `scriptling` itself).
name is normalised into the plugin.* namespace (e.g. "widgets" becomes "plugin.widgets"); the returned client's Metadata().Name reflects that.
func (*Manager) LoadPlugin ¶ added in v0.23.0
LoadPlugin starts a single executable plugin, performing the plugin protocol handshake, and registers it under the library name it declares — the same naming rule directory discovery uses, so a plugin behaves identically however it is loaded. args, if non-empty, are passed as command-line arguments to the executable.
Executable identity is the resolved absolute path: loading an executable that is already registered (e.g. discovered earlier via LoadPath or an explicit load ahead of a --plugin-dir scan) returns the existing client.
func (*Manager) LoadPlugins ¶ added in v0.23.0
func (m *Manager) LoadPlugins(ctx context.Context, specs []PluginSpec) error
LoadPlugins starts each executable plugin, at most SetMaxParallelPluginLoads at a time, and registers them in the order the specs are given: a library name declared by two plugins resolves to the first spec, exactly as sequential loading resolves it. The first failed start (in spec order) is returned as an error; plugins started before it are kept. Embedders use this (or Load) for capped parallel loading.
func (*Manager) LoadURL ¶ added in v0.12.1
func (m *Manager) LoadURL(ctx context.Context, name, rawURL string, scriptling, insecureSkipTLS bool, headers ...map[string]string) (*Client, error)
LoadURL connects to an HTTP(S) JSON-RPC endpoint and registers it under name. If scriptling is true, the plugin protocol handshake is performed. If insecureSkipTLS is true, HTTPS certificate verification is skipped. Optional headers are sent with every HTTP request.
func (*Manager) NewScope ¶ added in v0.12.3
func (m *Manager) NewScope(opts ...ScopeOption) *Manager
NewScope creates a child Manager that inherits the logger and shared HTTP transports from this Manager. Plugins loaded into the scope are invisible to the parent and to other scopes. When the scope is closed, only its locally loaded plugins are unloaded; the parent's plugins are unaffected.
Calling Get or List on the scope chains to the parent for fallback: the scope sees its own plugins first and parent plugins where there is no clash.
The scope does not inherit the parent's dirs or crash handler.
func (*Manager) SetCrashHandler ¶
SetCrashHandler installs a callback for loaded plugin processes that exit unexpectedly. The handler is not called for normal manager shutdown.
func (*Manager) SetLogger ¶ added in v0.11.0
SetLogger installs the host logger used for log records emitted by plugins.
func (*Manager) SetMaxParallelPluginLoads ¶ added in v0.23.0
SetMaxParallelPluginLoads caps how many plugin processes are started concurrently by Load and LoadPlugins. Values below 1 mean one at a time; the default is DefaultMaxParallelPluginLoads. Registration order is the input order whatever the cap, so name collisions resolve identically to sequential loading.
func (*Manager) SetPolicy ¶ added in v0.23.0
SetPolicy sets the security policy delivered to every plugin this manager handshakes with. Call it before Load/LoadPlugin/LoadURL — the policy rides the handshake, which is the first message on each connection. A nil policy (the default) tells plugins the host imposes no restrictions. Scopes created earlier still see the policy through the parent chain at handshake time.
func (*Manager) Unload ¶ added in v0.12.1
Unload closes a client registered via LoadPath and removes it from the manager. It is intended for runtime-loaded executables; calling Unload on a plugin discovered via Load also works but the plugin will not be restarted. Returns an error if no client is registered under name (after normalisation).
type Metadata ¶
type Metadata struct {
Name string `json:"name"`
Version string `json:"version"`
Description string `json:"description"`
Transport string `json:"transport,omitempty"`
Capabilities []string `json:"capabilities,omitempty"`
Scheme string `json:"scheme,omitempty"` // the source scheme this plugin's fetcher serves
Schema Schema `json:"schema"`
}
Metadata describes a loaded plugin library.
type NetworkPolicy ¶ added in v0.23.0
type NetworkPolicy struct {
RequireHTTPS bool `json:"require_https,omitempty"`
AllowIPLiterals bool `json:"allow_ip_literals,omitempty"`
AllowLoopback bool `json:"allow_loopback,omitempty"`
AllowPrivateIPs bool `json:"allow_private_ips,omitempty"`
AllowHosts []string `json:"allow_hosts,omitempty"`
DenyHosts []string `json:"deny_hosts,omitempty"`
AllowedCIDRs []string `json:"allowed_cidrs,omitempty"`
DeniedCIDRs []string `json:"denied_cidrs,omitempty"`
}
NetworkPolicy is the wire form of the host's outbound network policy. It carries exactly the fields of netsecurity.Config, so a Guard rebuilt from it enforces the same rules the host applies to requests and websockets, including DNS-rebinding protection via validated-IP dialing.
A nil *NetworkPolicy means the host imposes no network restriction.
type PluginSpec ¶ added in v0.23.0
type PluginSpec struct {
Path string
Args []string
Env []string
Headers map[string]string
Insecure bool
}
PluginSpec names one plugin to load: an executable path with optional command-line arguments and environment entries, or an http(s) URL of a remote JSON-RPC plugin server. Env entries are KEY=VALUE strings layered on top of the inherited environment (existing keys are overridden); they apply to executables only, an HTTP server owns its own environment. Headers ride every HTTP request (e.g. Authorization for a bearer token). Insecure skips TLS certificate verification for https URLs.
type Policy ¶ added in v0.23.0
type Policy struct {
// AllowedPaths restricts filesystem locations (database files, storage
// directories) exactly like the fs/pathlib libraries. nil = unrestricted.
AllowedPaths []string `json:"allowed_paths,omitempty"`
// Network restricts outbound connections. nil = unrestricted.
Network *NetworkPolicy `json:"network,omitempty"`
}
Policy is the security context a host sends to plugins in the scriptling.handshake params. Plugins that understand it (advertised via the "policy" capability) enforce it on every operation that opens a file or a network connection; plugins that predate it simply ignore the field.
A nil *Policy means no restrictions. A non-nil Policy with nil AllowedPaths leaves filesystem access unrestricted; a nil Network leaves network access unrestricted. This mirrors the nil-semantics of fssecurity.Config and netsecurity.Config.
func PolicyFromSecurity ¶ added in v0.23.0
func PolicyFromSecurity(cfg *netsecurity.Config, allowedPaths []string) *Policy
PolicyFromSecurity converts the host-side security configuration into the wire Policy form. A nil cfg and nil allowedPaths yield a nil Policy (no restrictions); otherwise only non-nil parts are carried.
func (*Policy) Guard ¶ added in v0.23.0
func (p *Policy) Guard() (*netsecurity.Guard, error)
Guard returns a netsecurity.Guard enforcing the policy's network rules, or (nil, nil) when the network is unrestricted. The returned Guard validates every resolved IP at dial time, so connections made through its DialContext get the same SSRF and DNS-rebinding protection as host-side requests.
func (*Policy) NetworkEnabled ¶ added in v0.23.0
NetworkEnabled reports whether the policy carries a network policy at all. Callers use this to decide between a guarded dialer and the default one.
func (*Policy) PathAllowed ¶ added in v0.23.0
PathAllowed reports whether path is within the policy's allowed paths. A nil policy (or nil AllowedPaths) allows everything, matching fssecurity.
type PolicySource ¶ added in v0.23.0
type PolicySource interface {
Policy() *Policy
}
PolicySource supplies the effective security policy at call time. The host guarantees a plugin's handshake completes before its first function call, so external plugins can read Server.Policy() lazily inside connect/open.
type PropertySchema ¶ added in v0.11.0
type PropertySchema struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
Settable bool `json:"settable,omitempty"`
}
PropertySchema describes a plugin class property.
type RemoteRef ¶
type RemoteRef struct {
Library string `json:"library"`
Class string `json:"class"`
ID string `json:"id"`
}
RemoteRef identifies an object stored in a plugin process.
type Schema ¶
type Schema struct {
Functions []FunctionSchema `json:"functions"`
Classes []ClassSchema `json:"classes"`
Constants []ConstantSchema `json:"constants"`
}
Schema describes functions, classes, and constants exposed by a plugin.
type ScopeOption ¶ added in v0.12.3
type ScopeOption func(*Manager)
ScopeOption configures a scoped Manager created by NewScope.
func WithTransport ¶ added in v0.12.3
func WithTransport(mode TransportMode) ScopeOption
WithTransport sets the transport restriction for a scoped Manager. Use TransportHTTP to permit only HTTP(S) plugins, TransportStdio for only stdio executables, or TransportAll (default) to allow both.
type ScriptLibraryRegistrar ¶
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
func (*Server) ObjectCount ¶ added in v0.15.0
ObjectCount returns the number of live server-side objects. Useful in tests.
func (*Server) Policy ¶ added in v0.23.0
Policy returns the security policy the host delivered in its handshake, or nil when the host sent none (no restrictions). Registration code reads it lazily inside connect/open closures because the handshake is the first message on every connection.
func (*Server) RegisterBuiltin ¶ added in v0.15.0
func (s *Server) RegisterBuiltin(name string, fn object.BuiltinFunction) *Server
RegisterBuiltin registers a raw builtin function directly, bypassing the FunctionBuilder reflection layer. Use this when the function is already an object.BuiltinFunction (e.g. a closure that wraps a script handler).
func (*Server) RegisterBuiltinClass ¶ added in v0.15.0
RegisterBuiltinClass registers a scriptling *object.Class directly, bypassing the ClassBuilder. Use this when the class was loaded from a scriptling module (e.g. via Scriptling.Eval) rather than built from Go code.
func (*Server) RegisterClass ¶
func (s *Server) RegisterClass(builder *object.ClassBuilder) *Server
func (*Server) RegisterFetcher ¶ added in v0.23.0
RegisterFetcher registers f as this plugin's fetcher: the host routes <scheme>:// sources here, attaches the plugin's library automatically, and asks for files only as imports resolve. The whole fetcher contract is this one call — one plugin serves one scheme, with the standard layout (modules under lib/, scripts as bare scheme:// sources). It must be called before Run / ServeHTTP, like the other registration methods. Built-in schemes (http, https, file) are rejected, as is a second registration.
func (*Server) RegisterFunc ¶
func (s *Server) RegisterFunc(name string, builder *object.FunctionBuilder) *Server
func (*Server) RegisterLibrary ¶ added in v0.23.0
RegisterLibrary ingests a pre-built *object.Library — functions, classes (held as class constants), and constants — into the server. It is how a single registration implementation serves both plugin modes: the same library handed to RegisterLibrary here, or to a Scriptling instance's RegisterLibrary for compiled-in plugins.
func (*Server) RegisterScriptClass ¶
func (*Server) RegisterScriptFunc ¶
func (*Server) RunIO ¶
RunIO serves the plugin protocol over a bidirectional stream (stdio). The jsonrpc.Peer handles framing, batch dispatch, notification detection and response correlation in both directions; dispatch runs in the peer's server.
RunIO blocks until the input reaches EOF — normally because the host closed the child's stdin after plugin.shutdown (which destroys objects and returns a response). The server need not force-exit on shutdown: the response is written before the host closes stdin, so there is no flush-before-exit race. Object finalizers run on plugin.shutdown and again on EOF (idempotent).
func (*Server) ServeHTTP ¶ added in v0.12.1
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP serves the Scriptling plugin JSON-RPC protocol over HTTP. Mount it at a path such as /json-rpc and load it with plugin.Manager.LoadURL or scriptling.plugin.load(..., scriptling=True).
The handler answers whoever reaches it: it ships no authentication of its own, so whatever fronts the endpoint (auth middleware, TLS plus credentials at a reverse proxy, a loopback-only bind) is the access control. Credentials arrive as headers on r; see the php-server example for a token check.
HTTP plugin transport supports normal plugin calls, object lifecycle, and batches. Host callbacks and plugin.Logger(ctx) require the bidirectional stdio transport and are not available over HTTP. Framing (POST handling, batches, notifications, error responses, 204 on no-response) is provided by jsonrpc.Server.ServeHTTP; this server only supplies the plugin methods.
type StaticPolicy ¶ added in v0.23.0
type StaticPolicy struct {
P *Policy
}
StaticPolicy is a PolicySource with a fixed value. It is the in-process counterpart of Server.Policy(): compiled-in plugins receive one built from the interpreter's security configuration, external plugins read the policy the handshake delivered.
func (*StaticPolicy) Policy ¶ added in v0.23.0
func (s *StaticPolicy) Policy() *Policy
Policy returns the fixed policy.
type TransportMode ¶ added in v0.12.3
type TransportMode int
TransportMode restricts which plugin transport protocols a Manager or scope will accept when LoadPath or LoadURL is called.
const ( // TransportAll permits both stdio/executable and HTTP(S) plugins (default). TransportAll TransportMode = iota // TransportHTTP permits only HTTP(S) endpoints; loading executables fails. TransportHTTP // TransportStdio permits only stdio executables; loading HTTP URLs fails. TransportStdio )
type Value ¶
type Value struct {
Type string `json:"type"`
Value any `json:"value,omitempty"`
Items []Value `json:"items,omitempty"`
Entries map[string]Value `json:"entries,omitempty"`
Remote *RemoteRef `json:"remote,omitempty"`
Callback *CallbackRef `json:"callback,omitempty"`
}
Value is the JSON-RPC transport representation of Scriptling values.