Library Registration
When embedding Scriptling in a Go application, you control which libraries are available to scripts. Libraries are not loaded unless you explicitly register them.
Standard Libraries
23 built-in libraries available without any configuration.
Register All at Once
import "github.com/paularlott/scriptling/stdlib"
stdlib.RegisterAll(p)Register Individually
p.RegisterLibrary(stdlib.JSONLibrary)
p.RegisterLibrary(stdlib.MathLibrary)
p.RegisterLibrary(stdlib.ReLibrary)
p.RegisterLibrary(stdlib.TimeLibrary)| Namespace | Constant |
|---|---|
base64 |
Base64Library |
collections |
CollectionsLibrary |
contextlib |
ContextlibLibrary |
datetime |
DatetimeLibrary |
difflib |
DifflibLibrary |
functools |
FunctoolsLibrary |
hashlib |
HashlibLibrary |
hmac |
HmacLibrary |
html |
HTMLLibrary |
io |
IOLibrary |
itertools |
ItertoolsLibrary |
json |
JSONLibrary |
math |
MathLibrary |
platform |
PlatformLibrary |
random |
RandomLibrary |
re |
ReLibrary |
statistics |
StatisticsLibrary |
string |
StringLibrary |
textwrap |
TextwrapLibrary |
time |
TimeLibrary |
urllib |
URLLibLibrary |
urllib.parse |
URLParseLibrary |
uuid |
UUIDLibrary |
Extended Libraries
These are in the root extlibs package and provide Python-compatible functionality.
Simple Registration
import "github.com/paularlott/scriptling/extlibs"
extlibs.RegisterRequestsLibrary(p)
extlibs.RegisterYAMLLibrary(p)
extlibs.RegisterTOMLLibrary(p)
extlibs.RegisterSecretsLibrary(p)
extlibs.RegisterSubprocessLibrary(p)
extlibs.RegisterHTMLParserLibrary(p)
extlibs.RegisterShlexLibrary(p)| Namespace | Function |
|---|---|
requests |
RegisterRequestsLibrary(p) |
yaml |
RegisterYAMLLibrary(p) |
toml |
RegisterTOMLLibrary(p) |
secrets |
RegisterSecretsLibrary(p) |
subprocess |
RegisterSubprocessLibrary(p) |
html.parser |
RegisterHTMLParserLibrary(p) |
shlex |
RegisterShlexLibrary(p) |
scriptling.csv |
RegisterCsvLibrary(p) |
scriptling.xml |
RegisterXmlLibrary(p) |
Filesystem Libraries
These accept allowedPaths to restrict filesystem access. Pass nil for unrestricted access.
extlibs.RegisterOSLibrary(p, []string{"/tmp", "/data"})
extlibs.RegisterPathlibLibrary(p, []string{"/tmp", "/data"})
extlibs.RegisterFSLibrary(p, []string{"/tmp", "/data"})
extlibs.RegisterGlobLibrary(p, []string{"/tmp", "/data"})
extlibs.RegisterTempfileLibrary(p, []string{"/tmp"})
extlibs.RegisterShutilLibrary(p, []string{"/tmp", "/data"})
extlibs.RegisterZipfileLibrary(p, []string{"/tmp", "/data"})
extlibs.RegisterTarfileLibrary(p, []string{"/tmp", "/data"})
extlibs.RegisterGrepLibrary(p, []string{"/tmp"})
extlibs.RegisterFindLibrary(p, []string{"/tmp"})
extlibs.RegisterSedLibrary(p, []string{"/tmp"})| Namespace | Function |
|---|---|
os + os.path |
RegisterOSLibrary(p, allowedPaths) |
pathlib |
RegisterPathlibLibrary(p, allowedPaths) |
fs |
RegisterFSLibrary(p, allowedPaths) |
glob |
RegisterGlobLibrary(p, allowedPaths) |
tempfile |
RegisterTempfileLibrary(p, allowedPaths) |
shutil |
RegisterShutilLibrary(p, allowedPaths) |
zipfile |
RegisterZipfileLibrary(p, allowedPaths) |
tarfile |
RegisterTarfileLibrary(p, allowedPaths) |
scriptling.grep |
RegisterGrepLibrary(p, allowedPaths) |
scriptling.find |
RegisterFindLibrary(p, allowedPaths) |
scriptling.sed |
RegisterSedLibrary(p, allowedPaths) |
requests reads a file too: the CA bundle a script names with verify="path". RegisterRequestsLibrary lets it be any file; RegisterRequestsLibraryConfigured confines it to allowed paths (outside them it raises an uncatchable PermissionError, as in os) and takes the network policy too. The CLI registers it this way with --allowed-paths and --network-policy.
extlibs.RegisterRequestsLibraryConfigured(p, fssecurity.Config{AllowedPaths: []string{"/tmp", "/data"}}, policy) // policy may be nilCustom Configuration
// sys: requires argv and stdin
extlibs.RegisterSysLibrary(p, []string{"script.py"}, os.Stdin)
// logging: requires a logger instance (or use default)
extlibs.RegisterLoggingLibraryDefault(p)
// or with custom logger:
// extlibs.RegisterLoggingLibrary(p, myLogger)
// secrets provider: requires a secret registry
extlibs.RegisterSecretLibrary(p, registry)
// wait_for, websocket, templates
extlibs.RegisterWaitForLibrary(p)
// or confine file(), dir() and file_content() to allowed paths, with a network policy:
// extlibs.RegisterWaitForLibraryConfigured(p, []string{"/tmp"}, policy)
extlibs.RegisterWebSocketLibrary(p)
extlibs.RegisterTemplateHTMLLibrary(p)
extlibs.RegisterTemplateTextLibrary(p)| Namespace | Function |
|---|---|
sys |
RegisterSysLibrary(p, argv []string, stdin io.Reader) |
logging |
RegisterLoggingLibrary(p, logger) or RegisterLoggingLibraryDefault(p) |
scriptling.secret |
RegisterSecretLibrary(p, registry *secretprovider.Registry) |
scriptling.wait_for |
RegisterWaitForLibrary(p) or RegisterWaitForLibraryConfigured(p, allowedPaths, policy) |
scriptling.net.websocket |
RegisterWebSocketLibrary(p) |
scriptling.template.html |
RegisterTemplateHTMLLibrary(p) |
scriptling.template.text |
RegisterTemplateTextLibrary(p) |
Plain RegisterWaitForLibrary leaves the file(), dir() and file_content() checks unconfined; hosts with allowed paths use RegisterWaitForLibraryConfigured (nil = unrestricted, empty = deny all).
Runtime Libraries
Background tasks, HTTP routing, JSON-RPC, key-value store, concurrency, and sandboxing.
// Register http, kv, sync, sandbox, jsonrpc, and mcp at once (not plugin, see below)
extlibs.RegisterRuntimeLibraryAll(p, []string{"/tmp"})
// Or register individually
extlibs.RegisterRuntimeLibrary(p) // scriptling.runtime (background tasks)
extlibs.RegisterRuntimeHTTPLibrary(p) // scriptling.runtime.http
extlibs.RegisterRuntimeJSONRPCLibrary(p) // scriptling.runtime.jsonrpc
extlibs.RegisterRuntimeMCPLibrary(p) // scriptling.runtime.mcp
extlibs.RegisterRuntimeKVLibrary(p) // scriptling.runtime.kv
extlibs.RegisterRuntimeSyncLibrary(p) // scriptling.runtime.sync
extlibs.RegisterRuntimeSandboxLibrary(p, []string{"/tmp"}) // scriptling.runtime.sandbox| Namespace | Function |
|---|---|
scriptling.runtime |
RegisterRuntimeLibrary(p) |
scriptling.runtime.http, .kv, .sync, .sandbox, .jsonrpc, .mcp |
RegisterRuntimeLibraryAll(p, allowedPaths) |
scriptling.runtime.http |
RegisterRuntimeHTTPLibrary(p) |
scriptling.runtime.jsonrpc |
RegisterRuntimeJSONRPCLibrary(p) |
scriptling.runtime.mcp |
RegisterRuntimeMCPLibrary(p) |
scriptling.runtime.kv |
RegisterRuntimeKVLibrary(p) or RegisterRuntimeKVLibraryWithSecurity(p, allowedPaths) |
scriptling.runtime.sync |
RegisterRuntimeSyncLibrary(p) |
scriptling.runtime.sandbox |
RegisterRuntimeSandboxLibrary(p, allowedPaths) |
scriptling.runtime.plugin is registered separately and is not included in RegisterRuntimeLibraryAll. Register it when the embedded application needs to expose a Scriptling script as a plugin server:
extlibs.RegisterRuntimePluginLibrary(p) // scriptling.runtime.plugin| Namespace | Function |
|---|---|
scriptling.runtime.plugin |
RegisterRuntimePluginLibrary(p) |
Scriptling-Specific Libraries
These live in subpackages under extlibs/ and each expose a Register function.
AI & Agent
import (
"github.com/paularlott/scriptling/extlibs/ai"
"github.com/paularlott/scriptling/extlibs/agent"
aimemory "github.com/paularlott/scriptling/extlibs/ai/memory"
aitools "github.com/paularlott/scriptling/extlibs/ai/tools"
)
ai.Register(p) // scriptling.ai
agent.Register(p) // scriptling.ai.agent (returns error)
agent.RegisterInteract(p) // scriptling.ai.agent.interact (returns error)
aimemory.Register(p) // scriptling.ai.memory
aitools.Register(p) // scriptling.ai.tools| Namespace | Import Path | Call |
|---|---|---|
scriptling.ai |
extlibs/ai |
ai.Register(p) |
scriptling.ai.agent |
extlibs/agent |
agent.Register(p) |
scriptling.ai.agent.interact |
extlibs/agent |
agent.RegisterInteract(p) |
scriptling.ai.memory |
extlibs/ai/memory |
memory.Register(p) |
scriptling.ai.tools |
extlibs/ai/tools |
tools.Register(p) |
MCP Protocol & TOON
import "github.com/paularlott/scriptling/extlibs/mcp"
mcp.Register(p) // scriptling.mcp
mcp.RegisterToolHelpers(p) // scriptling.mcp.tool
mcp.RegisterToon(p) // scriptling.toon| Namespace | Call |
|---|---|
scriptling.mcp |
mcp.Register(p) or mcp.RegisterConfigured(p, policy, allowProcesses) |
scriptling.mcp.tool |
mcp.RegisterToolHelpers(p) |
scriptling.toon |
mcp.RegisterToon(p) |
mcp.Register allows stdio servers, which mcp.Client() starts as subprocesses. Hosts that disable subprocesses use mcp.RegisterConfigured(p, policy, false), which refuses any target that is not an http(s) URL.
Networking
import (
"github.com/paularlott/scriptling/extlibs/net/resolve"
"github.com/paularlott/scriptling/extlibs/net/multicast"
"github.com/paularlott/scriptling/extlibs/net/unicast"
"github.com/paularlott/scriptling/extlibs/net/gossip"
)
resolve.Register(p, myResolver) // scriptling.net.resolve (requires a Resolver implementation)
multicast.Register(p) // scriptling.net.multicast (optional *netsecurity.Config)
unicast.Register(p) // scriptling.net.unicast (optional *netsecurity.Config)
gossip.Register(p, nil) // scriptling.net.gossip (nil = null logger; optional *netsecurity.Config)| Namespace | Import Path | Call |
|---|---|---|
scriptling.net.resolve |
extlibs/net/resolve |
resolve.Register(p, resolver) |
scriptling.net.multicast |
extlibs/net/multicast |
multicast.Register(p) |
scriptling.net.unicast |
extlibs/net/unicast |
unicast.Register(p) |
scriptling.net.gossip |
extlibs/net/gossip |
gossip.Register(p, logger) |
Network Policy
The outbound networking libraries — requests, scriptling.wait_for, scriptling.net.websocket, scriptling.net.unicast, scriptling.net.multicast, scriptling.net.gossip, scriptling.nomad, scriptling.oauth, scriptling.ai, scriptling.mcp, and the Slack, Discord and Telegram messaging libraries — accept an optional *netsecurity.Config that restricts where scripts may connect. scriptling.container takes one through container.RegisterConfigured or container.Options.Policy (it checks the endpoints a script supplies), and scriptling.provision.fetch takes a *netsecurity.Guard through fetch.RegisterConfigured. Pass nil (or omit the argument) for no restrictions; a non-nil policy blocks loopback, link-local (cloud metadata), private, unspecified, and multicast addresses, and IP-literal URLs, by default.
import (
"github.com/paularlott/scriptling/extlibs/ai"
scriptlingmcp "github.com/paularlott/scriptling/extlibs/mcp"
"github.com/paularlott/scriptling/extlibs/netsecurity"
)
policy := &netsecurity.Config{
RequireHTTPS: true,
AllowHosts: []string{"api.example.com"},
}
extlibs.RegisterRequestsLibrary(p, policy)
extlibs.RegisterWaitForLibrary(p, policy)
extlibs.RegisterWebSocketLibrary(p, policy)
ai.Register(p, policy)
scriptlingmcp.Register(p, policy)
// Or load the same TOML file the CLI's --network-policy flag uses
policy, err := netsecurity.LoadConfig("policy.toml")
if err != nil {
return err // invalid policies are an error, never an open policy
}ai.Register’s policy also guards remote_servers — the MCP servers an ai.Client() instance can attach for tool use — so a script cannot reach a policy-blocked endpoint by routing through ai.Client(..., remote_servers=[...]) instead of mcp.Client(...) directly. Stdio-transport mcp.Client() instances (a local subprocess) are unaffected — the policy only governs network access.
Config options (all optional — the zero value plus a non-nil pointer is a safe default policy):
| Option | Type | Default | Meaning |
|---|---|---|---|
RequireHTTPS |
bool |
false |
Reject plain http:// and ws:// URLs |
AllowIPLiterals |
bool |
false |
Permit URLs that name an IP directly; granted addresses still face the address rules |
AllowLoopback |
bool |
false |
Permit 127.0.0.0/8 and ::1 |
AllowPrivateIPs |
bool |
false |
Permit RFC1918 and IPv6 unique-local ranges |
AllowReservedIPs |
bool |
false |
Permit reserved ranges that are neither private nor publicly routable — CGNAT 100.64.0.0/10, 0.0.0.0/8, documentation and benchmarking ranges, 240.0.0.0/4, IPv4-compatible/NAT64/6to4/Teredo IPv6 and more; the full list is netsecurity.ReservedCIDRs. Policy files set it as allow_reserved_ips = true |
AllowHosts |
[]string |
nil |
Host allowlist; when set, only these hosts may be contacted. Listed hosts are trusted — they may resolve to loopback, private and reserved addresses; DeniedCIDRs still applies to them, and a link-local answer still needs an AllowedCIDRs entry. Exact names match themselves; a leading dot (.corp.example.com) matches the domain and all subdomains |
DenyHosts |
[]string |
nil |
Host denylist; always wins, even over AllowHosts. Same syntax |
AllowedCIDRs |
[]string |
nil |
Address ranges permitted explicitly, overriding the built-in blocks (how you grant one slice of a LAN) |
DeniedCIDRs |
[]string |
nil |
Address ranges blocked explicitly; wins over everything, including AllowedCIDRs |
DNSServers |
[]string |
nil |
Resolve through these servers ("1.1.1.1" or "8.8.8.8:53", plain DNS) instead of the host resolver; the same resolver then serves scriptling.net.resolve too |
AllowAll |
bool |
false |
Host-use only (not settable from policy files): disable every address and host check, leaving only the shared DNS resolver — the way to configure nameservers without imposing a policy |
ClientTimeout |
time.Duration |
0 |
Optional end-to-end cap on each HTTP request — dial, TLS, redirects, reading the body — for the client the guard hands to script libraries. 0 enforces no cap: requests run as long as their own per-request timeout allows, which long-running calls such as LLM APIs need. Policy files set it as client_timeout = "45s" |
An invalid config (bad CIDR, malformed DNS server) fails netsecurity.NewGuard / LoadConfig with an error — treat it as a startup failure. netsecurity.FailClosed(err) returns a guard that rejects every request if you need to keep serving after a config error.
Messaging
import (
"github.com/paularlott/scriptling/extlibs/messaging/console"
"github.com/paularlott/scriptling/extlibs/messaging/telegram"
"github.com/paularlott/scriptling/extlibs/messaging/discord"
"github.com/paularlott/scriptling/extlibs/messaging/slack"
)
console.Register(p) // scriptling.messaging.console
telegram.Register(p, nil) // scriptling.messaging.telegram (optional *netsecurity.Config)
discord.Register(p, nil) // scriptling.messaging.discord (optional *netsecurity.Config)
slack.Register(p, nil) // scriptling.messaging.slack (optional *netsecurity.Config)| Namespace | Import Path | Call |
|---|---|---|
scriptling.messaging.console |
extlibs/messaging/console |
console.Register(p) |
scriptling.messaging.telegram |
extlibs/messaging/telegram |
telegram.Register(p, logger) |
scriptling.messaging.discord |
extlibs/messaging/discord |
discord.Register(p, logger) |
scriptling.messaging.slack |
extlibs/messaging/slack |
slack.Register(p, logger) |
Each of telegram, discord and slack also has RegisterConfigured(p, logger, allowedPaths, policy...), which confines the local files send_file() reads to allowedPaths; plain Register leaves them unconfined.
Utilities
import (
"github.com/paularlott/scriptling/extlibs/console"
"github.com/paularlott/scriptling/extlibs/container"
"github.com/paularlott/scriptling/extlibs/nomad"
"github.com/paularlott/scriptling/extlibs/oauth"
"github.com/paularlott/scriptling/extlibs/similarity"
"github.com/paularlott/scriptling/extlibs/provision/fetch"
"github.com/paularlott/scriptling/extlibs/provision/file"
)
console.Register(p) // scriptling.console
container.Register(p, "", "") // scriptling.container (empty = default sockets; RegisterWithOptions adds path/network checks and DenyPrivileged)
nomad.Register(p) // scriptling.nomad (optional *netsecurity.Config)
oauth.Register(p) // scriptling.oauth (optional *netsecurity.Config; RegisterConfigured adds allowed paths for CA bundles)
similarity.Register(p) // scriptling.similarity
file.Register(p) // scriptling.provision.file
fetch.Register(p) // scriptling.provision.fetch
extlibs.RegisterMarkdownLibrary(p) // scriptling.markdown| Namespace | Import Path | Call |
|---|---|---|
scriptling.console |
extlibs/console |
console.Register(p) |
scriptling.container |
extlibs/container |
container.Register(p, dockerSock, podmanSock) or container.RegisterWithOptions(p, opts) |
scriptling.nomad |
extlibs/nomad |
nomad.Register(p) |
scriptling.oauth |
extlibs/oauth |
oauth.Register(p) or oauth.RegisterConfigured(p, paths, policy) |
scriptling.similarity |
extlibs/similarity |
similarity.Register(p) |
scriptling.provision.file |
extlibs/provision/file |
file.Register(p) |
scriptling.provision.fetch |
extlibs/provision/fetch |
fetch.Register(p) |
scriptling.markdown |
root extlibs |
extlibs.RegisterMarkdownLibrary(p) |
Executable Plugins
scriptling.plugin (the control library for executable plugins, listing/calling/loading them at runtime) lives in the root plugin package, not extlibs, and needs a *plugin.Manager rather than just the interpreter:
import (
"github.com/paularlott/scriptling/plugin"
)
manager := plugin.NewManager(myLogger, func(name string, err error) {
myLogger.Error("plugin process exited", "plugin", name, "error", err)
})
manager.AddDir("./plugins")
if err := manager.Load(ctx); err != nil {
// handle error
}
plugin.RegisterLibraries(p, manager) // scriptling.plugin, plus plugin.<name> for each loaded executable| Namespace | Import Path | Call |
|---|---|---|
scriptling.plugin |
plugin |
plugin.RegisterLibraries(p, manager) or plugin.RegisterLibrariesConfigured(p, manager, policy, allowProcesses) |
A plugin whose name the interpreter already has, a registered library or a module its library loader supplies, is skipped with a manager warning rather than shadowing it, so set the library loader before calling plugin.RegisterLibraries. plugin.RegisterLibrariesConfigured with allowProcesses false refuses script-driven load() of an executable while HTTP(S) plugins still load; plugin.ProcessesAllowed(disabledLibs) derives the flag from your disabled-library list, as the CLI’s --no-subprocess does.
See Plugin Manager for the full plugin embedding guide.
Security Considerations
When embedding Scriptling, you have full control over what scripts can access. See the Security Guide for best practices.
Never register these libraries when running untrusted code:
subprocess: allows arbitrary command executionsys: provides access to environment variables and system internalsscriptling.container: controls Docker/Podman containers on the hostscriptling.nomad: grants full control over a Nomad cluster (CSI volumes, jobs)scriptling.runtime.sandbox: can execute arbitrary codescriptling.ai.agent: can execute AI-generated code with tools
See Also
- Basics: creating interpreters and exchanging variables
- Security Guide: security best practices for embedding
- Libraries: usage reference for all libraries
Database Drivers in Embedded Hosts
Scriptling is the library; your application is the host, and the database drivers are optional at every level — you may compile them in, load them as external plugin binaries, or have neither. One call covers all three cases:
import scriptlingplugin "github.com/paularlott/scriptling/plugin"
// On every interpreter your host spins up — main instances, HTTP request
// environments, MCP sessions, sandbox/background factories:
scriptlingplugin.RegisterLibraries(p, pluginManager, scriptlingplugin.PolicyFromSecurity(netPolicy, allowedPaths))- Compiled in (build tags
plugin_sqlite/plugin_sql/plugin_valkey/plugin_badgerdb, or import the plugin packages and callsqlite.RegisterInProcess(p, policy)): registers regardless of the manager — a nilpluginManageris fine. - External plugin binaries: pass your
*plugin.Manager(see Plugin Manager) and the proxy libraries register per instance. - Neither: nothing registers; scripts importing
scriptling.sqlitefail with a namedunknown libraryerror.
The CLI is exactly this pattern — its --plugin-dir handling, exe-relative discovery and per-mode wiring are host choices, not library behaviour. Inside handler scripts, open a connection per run and let the environment’s teardown collect it (Connection closes itself when instances are collected; explicit conn.close() is still good manners).