ferrogo

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Nov 11, 2026 License: MIT Imports: 31 Imported by: 0

README

FerroGo

Go Reference CI Go Report Card

Compile and run Rust from Go: in a WebAssembly sandbox by default, or as native processes when you trust the code.

rt, _ := ferrogo.New()
defer rt.Close()

res, err := rt.Run(ctx, ferrogo.Code(`
    fn main() {
        println!("Hello from Rust!");
    }
`))
fmt.Print(res.Stdout) // Hello from Rust!

FerroGo drives your Rust toolchain for you. It generates a Cargo project, runs cargo with structured arguments and a scrubbed environment, parses diagnostics, caches artifacts by a SHA-256 fingerprint of every input, and executes the result with timeouts, output limits and (for WASM) memory and filesystem isolation.

Status: v0.1, pre-1.0. The API may still change before v1.0.

  • Platforms: developed and tested on macOS arm64. The CI matrix covers Linux (amd64 and arm64), macOS (arm64 and amd64) and Windows.
  • Open items: see the roadmap. The main one is an OS sandbox for native processes on Windows.

What you can do

Run Rust programs rt.Run(ctx, ferrogo.Code(...)): sandboxed WASM by default, or native processes
Compile once, call many times rt.Compile + mod.Call: WASM, a native library in-process, or native worker processes
Write Rust inside Go files /*ferrogo … */ + go generate: typed wrappers, WASM embedded, no toolchain needed at run time
Call Go from Rust ferrogo::import! + WithHostFunction
Use crates WithDependencies under a deny-by-default DependencyPolicy, or an existing Cargo Project

Why

Rust code is sometimes the right tool inside a Go service: user-supplied logic, a fast kernel, or an existing Rust snippet. Wiring that up by hand means temporary projects, compiler invocation, artifact management, process plumbing and sandboxing. FerroGo packages all of it behind one API, with secure defaults.

Requirements

  • Go ≥ 1.25.
  • Rust ≥ 1.82 (stable), installed with rustup. Only needed where code is compiled.
  • For the default WASM backend: rustup target add wasm32-wasip1.
  • For native backends: a C linker (xcode-select --install, build-essential, or the Visual Studio Build Tools).

Run ferrogo doctor to check everything.

Installation

go get github.com/alirezahematidev/ferrogo@latest                   # library
go install github.com/alirezahematidev/ferrogo/cmd/ferrogo@latest   # CLI

Quick start

ferrogo doctor                      # check toolchain, targets, backends
ferrogo run main.rs                 # WASM sandbox (default)
ferrogo run main.rs --backend native -- arg1 arg2
ferrogo build main.rs -o main.wasm  # compile only
ferrogo generate ./mypkg            # compile Rust embedded in Go files
ferrogo call lib.rs add 10 20       # call an exported function (args typed from the Rust signature)
ferrogo inspect lib.wasm            # list a module's exports
ferrogo doctor --fix                # install missing targets with rustup (explicit)
ferrogo run main.rs --json          # machine-readable result
ferrogo cache stats
ferrogo init                        # write ferrogo.toml

Backends

WASM (default) Native process Native library
Status ✅ ✅ ✅ (Linux, macOS, NetBSD, Windows)
Run programs / call functions ✅ / ✅ ✅ / ✅ (worker processes) — / ✅
Runs wasm32-wasip1 in wazero host executable as a child process .so/.dylib/.dll loaded in-process
Sandbox yes: no FS/network/env unless granted no (your user's privileges) no (your process)
Allowed for untrusted code yes no: needs WithTrust(Trusted) no: needs WithTrust(Trusted)
Timeouts interrupt running code yes yes (process-group kill) no
Memory limit yes no (returns ErrUnsupported) no
Threads, sockets no yes yes

BackendAuto always means WASM. FerroGo never silently falls back to a native backend. ferrogo.CapabilitiesOf(b) reports capabilities at run time, and rt.Inspect(ctx) reports what is usable on this machine. Details: docs/backends.md.

Runtime compilation

res, err := rt.Run(ctx, ferrogo.File("tool.rs"),
    ferrogo.WithStdin(strings.NewReader(input)),
    ferrogo.WithArgs("--fast"),
    ferrogo.WithEnv(map[string]string{"LEVEL": "3"}),
    ferrogo.WithTimeout(5*time.Second),
)
var ce *ferrogo.CompileError
switch {
case errors.As(err, &ce):
    for _, d := range ce.Errors() {
        fmt.Println(d.Location(), d.Message) // tool.rs:3:18 mismatched types
    }
case errors.Is(err, ferrogo.ErrTimeout):
    // res holds the partial output
}
  • Run returns a *Result (stdout, stderr, exit code, timing, artifact info) whenever the program started, even alongside an error.
  • Non-zero exits are *ExitError. Rust panics are *PanicError, with the file and line mapped to your names.
  • Build compiles without running.

Compile once, call many times

mod, err := rt.Compile(ctx, ferrogo.Code(`
    ferrogo::export! {
        fn add(a: i32, b: i32) -> i32 { a + b }
        fn greet(name: String) -> Result<String, String> {
            if name.is_empty() { return Err("empty name".into()); }
            Ok(format!("Hello, {name}!"))
        }
    }
`))
defer mod.Close()

sum, err := mod.Call(ctx, "add", ferrogo.Int32(10), ferrogo.Int32(20)) // i32(30)
_, err = mod.Call(ctx, "greet", ferrogo.String(""))                    // *ferrogo.CallError{"empty name"}
  • ferrogo::export! is injected into every library crate.
  • Functions take and return:
    • bool, i32/i64/u32/u64, f32/f64
    • String, Vec<u8>
    • ferrogo::Json (structs via encoding/json)
    • Result<T, E>
  • Panics become *PanicError on every backend and never crash your process.
  • Plain #[no_mangle] extern "C" scalar functions are callable on WASM (signature read from the module) and, with a declared Signature, on native libraries (CallRaw).
  • On the native-process backend, calls run in worker processes: they can be timed out, sandboxed and memory-limited, and crashes are contained.
  • One envelope ABI, identical on WASM and native libraries: docs/abi.md.
  • LoadModule(path) and LoadWASM(bytes) load prebuilt artifacts, for example from go:embed, with no toolchain at run time. Example: examples/functions.

Inline Rust

//go:generate go run github.com/alirezahematidev/ferrogo/cmd/ferrogo generate

/*ferrogo
pub fn fib(n: u32) -> u64 {
    let (mut a, mut b) = (0u64, 1u64);
    for _ in 0..n { (a, b) = (b, a + b); }
    a
}
*/

func main() {
    n, err := Fib(ctx, 90) // generated, typed wrapper
}
  • ferrogo generate compiles the block to WASM, embeds it with go:embed, and writes typed wrappers.
  • Programs using the package need no Rust toolchain.
  • Errors point at your .go lines, and generate -check keeps CI honest.
  • Details: docs/inline-rust.md. Example: examples/inline.

Calling Go from Rust (host functions)

rt, _ := ferrogo.New(ferrogo.WithHostFunction("lookup_user",
    func(ctx context.Context, args []ferrogo.Value) (ferrogo.Value, error) {
        id, _ := args[0].AsUint64()
        return ferrogo.String(users[id]), nil
    }))
mod, _ := rt.Compile(ctx, ferrogo.Code(`
    ferrogo::import! { fn lookup_user(id: u64) -> String; }
    ferrogo::export! {
        fn greet(id: u64) -> Result<String, String> { Ok(format!("hi {}", lookup_user(id)?)) }
    }
`))

WASM code can call exactly the functions you register and nothing else. Go errors and panics reach Rust as Err.

Dependencies and Cargo projects

rt, _ := ferrogo.New(ferrogo.WithDependencyPolicy(ferrogo.DependencyPolicy{
    AllowRegistry: true, // crates.io; AllowGit / AllowPath are separate
    Network:       false, // default: --offline, using crates already in ~/.cargo
}))
res, err := rt.Run(ctx, ferrogo.Code(`
    use serde_json::json;
    fn main() { println!("{}", json!({"language": "Rust", "fast": true})); }
`), ferrogo.WithDependencies(ferrogo.Dependency{
    Name: "serde_json", Version: "1", Features: []string{"preserve_order"},
}))
  • Denied by default. Dependencies run build scripts and proc-macros on your machine, so nothing is used unless a DependencyPolicy allows it:

    • registry, git and path dependencies are allowed separately;
    • crate allowlists are supported;
    • network access is a separate opt-in.
  • Reproducible. The resolved Cargo.lock is stored per dependency set and is part of the cache key.

  • Fast. Compiled dependencies are shared: a second snippet using serde_json builds in about 0.1 s instead of 1.7 s.

  • Cargo projects. ferrogo.Project("./tool") (CLI: ferrogo run ./tool) builds an existing project in place, with:

    • multi-file crates
    • its own Cargo.lock
    • custom profiles
    • incremental compilation

    Projects require WithTrust(Trusted) because their build scripts run on your machine. Their declared dependencies are checked against the policy.

Caching

Artifacts are stored in os.UserCacheDir()/ferrogo under a SHA-256 key over:

  • source
  • generated Cargo.toml
  • full rustc -vV output and cargo -V
  • target, backend and profile
  • edition and flags
  • explicit build environment
  • the contents of Cargo config files
  • the ABI helper version

Writes are atomic, entries are verified on read, and least-recently-used entries are evicted beyond 2 GiB. Machine code compiled from WASM is cached too. Use WithCacheDir, WithCacheMaxBytes and WithCache(false), or the CLI's ferrogo cache stats|clean.

Security

Treat Rust source as untrusted; FerroGo does by default.

  • The compiler's environment is scrubbed, so env!() cannot read your secrets.
  • No shell is ever used.
  • WASM guests get nothing they are not granted.
  • Every limit is finite by default:
    • execution timeout: 30s
    • compile timeout: 5m
    • output: 4 MiB
    • WASM memory: 256 MiB

Native processes can additionally run under the OS sandbox with WithSandbox (sandbox-exec on macOS, bubblewrap on Linux), with memory limits on Linux and Windows.

include_str! can read any file the compiler can read. Enable WithCompilerSandbox(true) to run the toolchain itself under the OS sandbox. It blocks reading your home directory at compile time. Otherwise, compile untrusted code in a container or VM. See docs/security.md.

Performance

Measured on Apple M-series, Go 1.25.6, Rust 1.94.1, wazero 1.12:

WASM Native process
Cold compile (trivial program) ~106 ms ~129 ms
Warm run, cached artifact 0.75 ms 2.0 ms
fib(30) incl. startup 26 ms 3.5 ms
Module.Call of add(i32, i32) 1.0 µs (raw C export: 0.55 µs) 0.7 µs (native library)

The WASM compute gap comes from interruption checks, which cost 8.7× on this call-heavy code. Those checks are what make timeouts work on untrusted code. Details and methodology: docs/performance.md. Reproduce with go test -run '^$' -bench . ..

Platform support

Library + CLI WASM backend Native process Native library
macOS arm64 tested tested tested tested
Linux amd64 / arm64 CI configured CI configured CI configured CI configured
macOS amd64, Windows amd64 CI configured CI configured CI configured (Windows: no process-tree kill yet) CI configured
FreeBSD, other builds builds builds not supported

"Tested" means tested locally. "CI configured" means the GitHub Actions matrix covers it but has not run yet. Unit tests (go test -short ./...) need no Rust toolchain.

Limitations

  • No machine-code JIT: code is compiled ahead of time by rustc, so the first build of a snippet takes 0.1–1s.
  • File sources are single files. Use a Project source for multi-file crates.
  • Dependencies are built offline unless DependencyPolicy.Network allows downloads, and their build scripts run on the build machine. Use WithCompilerSandbox to confine them.
  • Native processes have no memory limit on macOS, and no OS sandbox on Windows.
  • Host functions (Rust → Go) and deterministic mode work on WASM only.
  • CallRaw on native libraries is unavailable on Windows.
  • WASM (wasip1) has no threads and no sockets.
  • On macOS, a newly built native binary takes about 300 ms to start the first time (OS assessment).

Documentation

Development

make test         # unit + integration tests (integration tests skip without rustc)
make race         # with the race detector
make lint         # gofmt, go vet for linux/darwin/windows, staticcheck
make examples     # build and run every example

Contributing

See CONTRIBUTING.md. Report vulnerabilities privately, as described in SECURITY.md.

License

MIT

Documentation

Overview

Package ferrogo compiles Rust source code from Go and runs it: as a program, or as a library whose functions Go calls repeatedly.

A Runtime drives the user's Rust toolchain (rustc and cargo), caches compiled artifacts by a SHA-256 fingerprint of every build input, and executes them on one of three backends:

  • BackendWASM runs code in the wazero WebAssembly sandbox. It is the default (BackendAuto) and the only backend allowed for untrusted code: guests see no files, network or environment unless granted, and memory, time and output are limited.
  • BackendNativeProcess runs native executables, and serves library calls from worker processes, optionally under the OS sandbox (WithSandbox).
  • BackendNativeLibrary loads a native shared library into the Go process for the fastest calls, with no isolation at all.

Native backends require WithTrust(Trusted). CapabilitiesOf reports what each backend supports; Runtime.Inspect reports what is usable on this machine. FerroGo never falls back silently from WASM to a native backend, and never silently ignores a limit it cannot enforce.

Running a program

rt, err := ferrogo.New()
if err != nil {
	return err
}
defer rt.Close()

res, err := rt.Run(ctx, ferrogo.Code(`fn main() { println!("Hello from Rust!"); }`))
if err != nil {
	return err
}
fmt.Print(res.Stdout)

Calling Rust functions

Compile builds a library and keeps it loaded. Functions are exported with the ferrogo::export! macro, which FerroGo makes available in every library:

mod, err := rt.Compile(ctx, ferrogo.Code(`
    ferrogo::export! {
        fn add(a: i32, b: i32) -> i32 { a + b }
    }
`))
sum, err := mod.Call(ctx, "add", ferrogo.Int32(10), ferrogo.Int32(20))

Values cross the boundary through one tagged wire format on every backend: bool, integers, floats, String, Vec<u8>, JSON (for structs) and Result<T, E>. Rust can call back into Go through ferrogo::import! and WithHostFunction (WASM).

Inline Rust

Rust written in /*ferrogo ... */ comments in .go files is compiled by `ferrogo generate` (run through go generate) into typed Go wrappers with the WebAssembly embedded, so programs using the package need no Rust toolchain. See docs/inline-rust.md in the repository.

Dependencies and security

Cargo dependencies (WithDependencies) and Cargo projects (Project) are denied unless a DependencyPolicy allows them, because their build scripts run on the build machine. WithCompilerSandbox runs the toolchain itself under the OS sandbox. See docs/security.md for the full model.

Errors

Failures are reported with sentinel errors (ErrCompilation, ErrTimeout, ErrUntrusted, ...) and typed errors that carry details (*CompileError with rustc diagnostics mapped to your file names, *ExitError, *PanicError, *CallError, *TargetError, *UnsupportedError). Match them with errors.Is and errors.As.

FerroGo does not perform machine-code JIT compilation: code is compiled ahead of time by rustc and then executed or loaded.

Example

Run a Rust program in the default backend, the WebAssembly sandbox. Requires Rust with the wasm32-wasip1 target.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/alirezahematidev/ferrogo"
)

func main() {
	rt, err := ferrogo.New()
	if err != nil {
		log.Fatal(err)
	}
	defer rt.Close()

	res, err := rt.Run(context.Background(), ferrogo.Code(`
		fn main() {
			println!("Hello from Rust!");
		}
	`))
	if err != nil {
		log.Fatal(err)
	}
	fmt.Print(res.Stdout)
}

Index

Examples

Constants

View Source
const (
	DefaultTimeout        = 30 * time.Second
	DefaultCompileTimeout = 5 * time.Minute
	DefaultMaxOutput      = 4 << 20 // bytes, stdout+stderr combined
	DefaultGracePeriod    = 2 * time.Second
	DefaultCacheMaxBytes  = 2 << 30
	DefaultEdition        = "2021"
	// DefaultWASMMemoryLimit is the WASM guest memory limit unless
	// WithWASMMemoryLimit or WithMemoryLimit says otherwise.
	DefaultWASMMemoryLimit = 256 << 20
)

Defaults. Every limit is finite by default; pass 0 to the corresponding option to disable it explicitly.

View Source
const ConfigFileName = "ferrogo.toml"

ConfigFileName is the conventional project configuration file name.

View Source
const ConfigTemplate = `` /* 1501-byte string literal not displayed */

ConfigTemplate is the annotated ferrogo.toml written by `ferrogo init`.

Variables

View Source
var (
	// ErrToolchainNotFound: no usable rustc/cargo was found, or rustc is too old.
	ErrToolchainNotFound = errdefs.ErrToolchainNotFound
	// ErrTargetNotInstalled: the target required by a backend is not installed.
	// errors.As with *TargetError gives the remediation command.
	ErrTargetNotInstalled = errdefs.ErrTargetNotInstalled
	// ErrCompilation: Rust compilation failed. errors.As with *CompileError
	// gives the diagnostics.
	ErrCompilation = errdefs.ErrCompilation
	// ErrTimeout: compilation or execution exceeded its time limit.
	ErrTimeout = errdefs.ErrTimeout
	// ErrOutputLimit: the program wrote more output than allowed.
	ErrOutputLimit = errdefs.ErrOutputLimit
	// ErrMemoryLimit: guest code exceeded its memory limit.
	ErrMemoryLimit = errdefs.ErrMemoryLimit
	// ErrUnsupported: the backend cannot perform the operation or enforce a
	// requested limit. errors.As with *UnsupportedError gives details.
	ErrUnsupported = errdefs.ErrUnsupported
	// ErrUntrusted: the trust policy forbids the backend. See WithTrust.
	ErrUntrusted = errdefs.ErrUntrusted
	// ErrABIMismatch: argument or result types do not match the Rust function.
	ErrABIMismatch = errdefs.ErrABIMismatch
	// ErrFunctionNotFound: the module does not export the function.
	ErrFunctionNotFound = errdefs.ErrFunctionNotFound
	// ErrClosed: the Runtime or Module has been closed.
	ErrClosed = errdefs.ErrClosed
	// ErrDependencyPolicy: the dependency policy forbids a dependency.
	ErrDependencyPolicy = errdefs.ErrDependencyPolicy
)

Sentinel errors. Match them with errors.Is.

Backends lists the concrete backends.

Functions

func Version

func Version() string

Version returns the version of the FerroGo module linked into the running binary, as recorded by the Go toolchain: the release tag (for example "v0.1.0") when FerroGo is a dependency or was installed with `go install ...@version`, a pseudo-version for untagged commits, or "(devel)" when the version is unknown.

Types

type Artifact

type Artifact struct {
	// Path is the artifact file. It is read-only and lives in the cache (or,
	// with caching disabled, in a temporary directory removed by
	// Runtime.Close). Copy it if you need it elsewhere.
	Path    string       `json:"path"`
	Kind    ArtifactKind `json:"kind"`
	Backend Backend      `json:"backend"`
	Target  string       `json:"target"`
	// Key is the SHA-256 cache key of the build inputs.
	Key  string `json:"key"`
	Size int64  `json:"size"`
	// Cached reports whether the artifact was reused instead of built.
	Cached bool `json:"cached"`
	// BuildDuration is the compile time (zero when Cached).
	BuildDuration time.Duration `json:"build_duration_ns"`
	// Warnings are the compiler warnings of the build.
	Warnings []Diagnostic `json:"warnings,omitempty"`
}

Artifact is a compiled program or library.

type ArtifactKind

type ArtifactKind string

ArtifactKind is the kind of compiled artifact.

const (
	// ArtifactProgram is an executable with fn main.
	ArtifactProgram ArtifactKind = "program"
	// ArtifactLibrary is a library exposing exported functions
	// (a cdylib: .wasm, .so, .dylib or .dll).
	ArtifactLibrary ArtifactKind = "library"
)

type Backend

type Backend string

Backend selects how compiled Rust code is executed.

const (
	// BackendAuto selects the safest backend that supports the operation.
	// Today that is always BackendWASM; Auto never resolves to a native
	// backend and never falls back silently.
	BackendAuto Backend = "auto"
	// BackendWASM runs code in the wazero WebAssembly sandbox (wasm32-wasip1).
	BackendWASM Backend = "wasm"
	// BackendNativeProcess runs native executables as child processes.
	// Not a sandbox; requires WithTrust(Trusted).
	BackendNativeProcess Backend = "native-process"
	// BackendNativeLibrary loads native shared libraries into the Go process.
	// No isolation at all; requires WithTrust(Trusted).
	BackendNativeLibrary Backend = "native-library"
)

Execution backends.

func ParseBackend

func ParseBackend(s string) (Backend, error)

ParseBackend parses a backend name. It accepts "auto", "wasm", "native-process" (alias "native", "process"), and "native-library" (aliases "library", "shared", "dylib").

Example

Parse backend names from flags or configuration.

package main

import (
	"fmt"
	"log"

	"github.com/alirezahematidev/ferrogo"
)

func main() {
	for _, name := range []string{"auto", "native", "dylib"} {
		b, err := ferrogo.ParseBackend(name)
		if err != nil {
			log.Fatal(err)
		}
		fmt.Println(name, "=>", b)
	}
}
Output:
auto => auto
native => native-process
dylib => native-library

type BackendStatus

type BackendStatus struct {
	Backend Backend `json:"backend"`
	// Target is the Rust target triple the backend compiles for.
	Target string `json:"target,omitempty"`
	// Usable reports whether Run or Call can work with the current
	// toolchain, trust policy and FerroGo version.
	Usable bool `json:"usable"`
	// Problems explains why the backend is not usable, with remediation.
	Problems     []string     `json:"problems,omitempty"`
	Capabilities Capabilities `json:"capabilities"`
}

BackendStatus reports whether a backend is usable right now.

type CacheStats

type CacheStats struct {
	// Enabled is false when the runtime was created with WithCache(false).
	Enabled bool   `json:"enabled"`
	Dir     string `json:"dir"`
	Entries int    `json:"entries"`
	// Bytes is the total artifact size; MaxBytes is the eviction threshold (0 = unlimited).
	Bytes    int64 `json:"bytes"`
	MaxBytes int64 `json:"max_bytes"`
	// OtherBytes counts build directories and runtime compilation caches.
	OtherBytes int64 `json:"other_bytes"`
}

CacheStats describes the artifact cache.

type CallError

type CallError struct {
	Function string
	// Message is e.to_string().
	Message string
}

CallError reports that a Rust function returned Err(e).

func (*CallError) Error

func (e *CallError) Error() string

type Capabilities

type Capabilities struct {
	Backend Backend `json:"backend"`
	// Run: can execute programs with fn main.
	Run bool `json:"run"`
	// Call: can load modules and call exported functions.
	Call bool `json:"call"`
	// CaptureOutput, Stdin, Env, Args, WorkingDir: program I/O and environment control.
	CaptureOutput bool `json:"capture_output"`
	Stdin         bool `json:"stdin"`
	Env           bool `json:"env"`
	Args          bool `json:"args"`
	WorkingDir    bool `json:"working_dir"`
	// Interruptible: running code can be stopped by timeouts and cancellation.
	Interruptible bool `json:"interruptible"`
	// MemoryLimit: WithMemoryLimit is enforced.
	MemoryLimit bool `json:"memory_limit"`
	// FilesystemIsolation: code sees only explicitly granted directories.
	FilesystemIsolation bool `json:"filesystem_isolation"`
	// NetworkIsolation: code cannot open network connections.
	NetworkIsolation bool `json:"network_isolation"`
	// Threads: code may spawn OS threads.
	Threads bool `json:"threads"`
	// ProcessIsolation: code runs outside the Go process.
	ProcessIsolation bool `json:"process_isolation"`
	// Sandboxed: the backend is a security boundary for untrusted code.
	Sandboxed bool `json:"sandboxed"`
	// RequiresTrust: the backend is only allowed with WithTrust(Trusted).
	RequiresTrust bool `json:"requires_trust"`
	// OSSandbox: WithSandbox is implemented on this platform (it still needs
	// sandbox-exec or bubblewrap at run time).
	OSSandbox bool `json:"os_sandbox"`
}

Capabilities describes what a backend can do in this build of FerroGo on this platform. Use it instead of assuming every feature works everywhere.

func CapabilitiesOf

func CapabilitiesOf(b Backend) Capabilities

CapabilitiesOf reports the capabilities of b as implemented in this version of FerroGo. BackendAuto reports the capabilities of the backend it resolves to.

Example

Discover what a backend can do instead of assuming.

package main

import (
	"fmt"

	"github.com/alirezahematidev/ferrogo"
)

func main() {
	for _, b := range ferrogo.Backends {
		c := ferrogo.CapabilitiesOf(b)
		fmt.Printf("%-15s sandboxed=%-5v interruptible=%-5v requires-trust=%v\n",
			b, c.Sandboxed, c.Interruptible, c.RequiresTrust)
	}
}
Output:
wasm            sandboxed=true  interruptible=true  requires-trust=false
native-process  sandboxed=false interruptible=true  requires-trust=true
native-library  sandboxed=false interruptible=false requires-trust=true

type Code

type Code string

Code is Rust source text. For programs it must define fn main; for libraries it is the crate root. Diagnostics refer to it as main.rs or lib.rs.

rt.Run(ctx, ferrogo.Code(`fn main() { println!("hi"); }`))

type CompileError

type CompileError struct {
	// Reason classifies the failure: "compile", "link", "target",
	// "dependency", "manifest", "timeout" or "internal".
	Reason string
	Target string
	// Diagnostics holds every compiler message, errors and warnings.
	Diagnostics []Diagnostic
	// Output is cargo's textual output, useful when Reason is not "compile".
	Output string
	// contains filtered or unexported fields
}

CompileError reports a failed build.

func (*CompileError) Error

func (e *CompileError) Error() string

func (*CompileError) Errors

func (e *CompileError) Errors() []Diagnostic

Errors returns only the error-level diagnostics.

func (*CompileError) Rendered

func (e *CompileError) Rendered() string

Rendered returns all diagnostics as rustc would print them, or the raw cargo output when there are none.

func (*CompileError) Unwrap

func (e *CompileError) Unwrap() []error

Unwrap matches ErrCompilation, plus ErrTimeout or ErrTargetNotInstalled when those caused the failure.

type CompileOption

type CompileOption interface {
	// contains filtered or unexported methods
}

CompileOption configures a single Build call.

type Dependency

type Dependency struct {
	// Name is the crate name, e.g. "serde_json".
	Name string
	// Version is a Cargo version requirement for a registry crate, e.g. "1"
	// or "=1.0.150".
	Version string
	// Features enables crate features.
	Features []string
	// NoDefaultFeatures disables the crate's default features.
	NoDefaultFeatures bool
	// Path is a local crate directory (relative paths are resolved against
	// the working directory).
	Path string
	// Git is a repository URL, optionally pinned by Branch, Tag or Rev.
	Git              string
	Branch, Tag, Rev string
	// Registry names an alternative registry configured in Cargo's config.
	Registry string
}

Dependency is a Cargo dependency of the Rust code being compiled. Exactly one source must be set: Version (a registry crate), Path or Git.

Dependencies execute code on the build machine at compile time (build scripts and procedural macros), so they are only used when a DependencyPolicy allows them.

type DependencyPolicy

type DependencyPolicy struct {
	// AllowRegistry permits crates from crates.io or a configured registry.
	AllowRegistry bool
	// AllowGit permits git dependencies.
	AllowGit bool
	// AllowPath permits local path dependencies.
	AllowPath bool
	// Network lets Cargo download crates and resolve versions online.
	// Without it every build runs with --offline and can only use crates
	// already in the Cargo home.
	Network bool
	// Crates, if non-empty, restricts dependencies to these crate names.
	Crates []string
}

DependencyPolicy decides which dependencies may be used. The zero value allows none. Allowing any dependency (or building a Project) permits code execution on the build machine through build scripts and procedural macros; see docs/security.md.

type Diagnostic

type Diagnostic struct {
	// Severity is "error", "warning", "note", "help", or
	// "error: internal compiler error".
	Severity string `json:"severity"`
	// Code is the rustc error code such as "E0308", if any.
	Code     string       `json:"code,omitempty"`
	Message  string       `json:"message"`
	Spans    []Span       `json:"spans,omitempty"`
	Children []Diagnostic `json:"children,omitempty"`
	// Rendered is rustc's human-readable rendering (no ANSI colours), with
	// file names re-mapped to the names you supplied.
	Rendered string `json:"rendered,omitempty"`
}

Diagnostic is a Rust compiler message.

func (Diagnostic) Location

func (d Diagnostic) Location() string

Location returns the primary span as "file:line:column", or "".

type ExitError

type ExitError struct {
	// Code is the exit status, or -1 if the program was killed by a signal.
	Code int
	// Signal names the terminating signal (Unix), or is empty.
	Signal string
	// Trap describes the WebAssembly trap that ended the program (WASM
	// backend), e.g. "unreachable" after a Rust panic.
	Trap string
	// Panic is set when the program exited because of a Rust panic.
	Panic *PanicError
}

ExitError reports a program that exited unsuccessfully. Run returns it together with the Result, which holds the program's output.

func (*ExitError) Error

func (e *ExitError) Error() string

func (*ExitError) Unwrap

func (e *ExitError) Unwrap() error

Unwrap returns the panic, if any, so errors.As(err, **PanicError) works.

type File

type File string

File is the path of a single-file Rust program or library. Diagnostics refer to it by this path. Sibling modules (`mod foo;`) are not included; use a Cargo project for multi-file crates.

type Fragment

type Fragment struct {
	// File is the name used in diagnostics and panic locations.
	File string
	// Line and Column locate the first character of Text in File (1-based).
	Line, Column int
	// Text is the Rust source.
	Text string
}

Fragment is a piece of Rust source taken from a larger file, such as a /*ferrogo ... */ block in a Go file.

type Fragments

type Fragments []Fragment

Fragments is Rust source assembled from fragments of other files. The fragments are concatenated in order into one crate root, and compiler diagnostics and panics refer to the original files, lines and columns.

type Function

type Function struct {
	Name string `json:"name"`
	// Signature is the Rust signature, e.g. "fn add(a: i32, b: i32) -> i32".
	Signature string `json:"signature"`
}

Function describes an exported function.

type HostFunc

type HostFunc func(ctx context.Context, args []Value) (Value, error)

HostFunc is a Go function that WASM modules can call. Rust declares it with ferrogo::import! and calls it like a function returning Result<T, String>:

ferrogo::import! {
    fn lookup_user(id: u64) -> String;
}

args holds the arguments with the kinds Rust passed; the returned Value must have the kind Rust expects (Void for no return value). A returned error reaches Rust as Err(err.Error()). The context is the context of the Module.Call that led to the host call.

type Kind

type Kind uint8

Kind identifies the type of a Value. Each kind corresponds to one Rust type at the call boundary (docs/abi.md §2).

const (
	KindVoid    Kind = Kind(wire.Void)    // Rust ()
	KindBool    Kind = Kind(wire.Bool)    // Rust bool
	KindInt32   Kind = Kind(wire.Int32)   // Rust i32
	KindInt64   Kind = Kind(wire.Int64)   // Rust i64
	KindUint32  Kind = Kind(wire.Uint32)  // Rust u32
	KindUint64  Kind = Kind(wire.Uint64)  // Rust u64
	KindFloat32 Kind = Kind(wire.Float32) // Rust f32
	KindFloat64 Kind = Kind(wire.Float64) // Rust f64
	KindString  Kind = Kind(wire.String)  // Rust String
	KindBytes   Kind = Kind(wire.Bytes)   // Rust Vec<u8>
	KindJSON    Kind = Kind(wire.JSON)    // Rust ferrogo::Json
)

Value kinds.

func (Kind) String

func (k Kind) String() string

String returns the Rust type name for the kind ("i32", "String", ...).

type Module

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

Module is a loaded Rust library whose exported functions can be called repeatedly. Create one with Runtime.Compile, Runtime.LoadModule or Runtime.LoadWASM, and release it with Close. A Module is safe for concurrent use.

Functions exported with ferrogo::export! accept and return any Value kind, report Result::Err as *CallError and panics as *PanicError, and behave the same on every backend. On the WASM backend, plain #[no_mangle] extern "C" functions with scalar parameters can be called too (see docs/abi.md).

State differs by backend: a WASM module runs calls in a pool of instances with separate memories, so Rust statics are per instance and an instance is discarded after a panic; a native library has one shared state.

func (*Module) Call

func (m *Module) Call(ctx context.Context, name string, args ...Value) (Value, error)

Call invokes an exported function. Argument kinds must match the Rust parameter types exactly (no implicit conversions); mismatches fail with ErrABIMismatch. The context bounds the call on the WASM backend (together with WithTimeout); native-library calls can only be cancelled before they start.

func (*Module) CallRaw

func (m *Module) CallRaw(ctx context.Context, name string, sig Signature, args ...Value) (Value, error)

CallRaw calls a plain `#[no_mangle] extern "C"` function with scalar arguments, using a signature you declare.

On WASM the declaration is checked against the module's export, so a mismatch fails with ErrABIMismatch. A native shared library carries no type information: the declaration is trusted, and a wrong one is undefined behavior (native libraries already require WithTrust(Trusted)). Raw calls are not available on the native-process backend, or for native libraries on Windows.

func (*Module) Close

func (m *Module) Close() error

Close waits for in-flight calls and unloads the module. It is safe to call more than once; later calls fail with ErrClosed.

func (*Module) Functions

func (m *Module) Functions(ctx context.Context) ([]Function, error)

Functions lists the functions exported with ferrogo::export!, with their Rust signatures.

func (*Module) Info

func (m *Module) Info() ModuleInfo

Info describes the module.

type ModuleInfo

type ModuleInfo struct {
	Backend Backend `json:"backend"`
	// Target is the Rust target triple (empty for prebuilt artifacts).
	Target string `json:"target,omitempty"`
	// Path is the artifact file (empty for LoadWASM).
	Path string `json:"path,omitempty"`
	// Key is the artifact cache key (empty for prebuilt artifacts).
	Key string `json:"key,omitempty"`
}

ModuleInfo describes a loaded module.

type Mount

type Mount struct {
	// HostPath is the host directory to expose.
	HostPath string
	// GuestPath is where the guest sees it, e.g. "/data".
	GuestPath string
	// ReadOnly forbids writes when true.
	ReadOnly bool
}

Mount grants sandboxed code access to a host directory.

type Option

type Option interface {
	// contains filtered or unexported methods
}

Option configures a Runtime. Pass options to New.

func WithCache

func WithCache(enabled bool) Option

WithCache enables or disables the persistent artifact cache (default enabled). When disabled, every build compiles from scratch and artifacts live in a temporary directory removed by Runtime.Close.

func WithCacheDir

func WithCacheDir(dir string) Option

WithCacheDir sets the cache directory (default: os.UserCacheDir()/ferrogo).

func WithCacheMaxBytes

func WithCacheMaxBytes(n int64) Option

WithCacheMaxBytes bounds the total artifact size in the cache; least recently used artifacts are evicted beyond it. 0 means unlimited.

func WithCompilerSandbox

func WithCompilerSandbox(enabled bool) Option

WithCompilerSandbox runs the Rust toolchain itself (cargo, rustc, build scripts, proc-macros, the linker) under the OS sandbox: sandbox-exec on macOS, bubblewrap on Linux. Compilation then cannot read the user's home directory (beyond the toolchain, Cargo home, FerroGo cache and the build's inputs), cannot write elsewhere, and has no network unless the DependencyPolicy allows downloads. This closes the include_str!/ include_bytes! exfiltration path for untrusted source. Where no sandbox is available, builds fail with ErrUnsupported.

func WithConfigFile

func WithConfigFile(path string) Option

WithConfigFile loads settings from a ferrogo.toml file. Unknown keys are errors. Relative paths in the file are resolved against the file's directory. Options after WithConfigFile override values from the file.

func WithDependencyPolicy

func WithDependencyPolicy(p DependencyPolicy) Option

WithDependencyPolicy sets the dependency policy (default: no dependencies).

func WithLogger

func WithLogger(l *slog.Logger) Option

WithLogger sets a structured logger for diagnostics about FerroGo itself (cache maintenance, cargo invocations at debug level). Default: discard.

func WithMaxConcurrentBuilds

func WithMaxConcurrentBuilds(n int) Option

WithMaxConcurrentBuilds bounds simultaneous compilations (default GOMAXPROCS/2, minimum 1).

func WithToolchain

func WithToolchain(tc ToolchainConfig) Option

WithToolchain selects the Rust toolchain explicitly.

func WithTrust

func WithTrust(level TrustLevel) Option

WithTrust sets the trust policy. The default, Untrusted, only allows the sandboxed WASM backend. Trusted also allows native backends, which run code with the host's privileges. See docs/security.md.

type PanicError

type PanicError struct {
	Message string `json:"message"`
	// Thread is the panicking thread's name ("main" for the main thread).
	Thread string `json:"thread"`
	// File, Line and Column locate the panic, re-mapped to your file names.
	File   string `json:"file,omitempty"`
	Line   int    `json:"line,omitempty"`
	Column int    `json:"column,omitempty"`
}

PanicError describes a Rust panic.

func (*PanicError) Error

func (e *PanicError) Error() string

type Profile

type Profile string

Profile is a build profile.

const (
	ProfileRelease Profile = "release"
	ProfileDebug   Profile = "debug"
)

Build profiles.

type Project

type Project string

Project is the directory of an existing Cargo project (containing Cargo.toml). Cargo builds it in place, with its target directory in the FerroGo cache, so incremental compilation and the project's own Cargo.lock, profiles and dependencies apply. Programs need exactly one binary target (or default-run); libraries need crate-type = ["cdylib"].

Building a project runs its build script, so Project sources require WithTrust(Trusted); its dependencies must be allowed by the DependencyPolicy.

type Report

type Report struct {
	Version   string `json:"version"`
	GoVersion string `json:"go_version"`
	Platform  string `json:"platform"`
	// DefaultBackend is the configured backend; BackendAuto resolves to WASM.
	DefaultBackend Backend    `json:"default_backend"`
	Trust          string     `json:"trust"`
	Cache          CacheStats `json:"cache"`
	// Toolchain is nil when detection failed; ToolchainError says why.
	Toolchain      *ToolchainInfo  `json:"toolchain,omitempty"`
	ToolchainError string          `json:"toolchain_error,omitempty"`
	Backends       []BackendStatus `json:"backends"`
}

Report is a snapshot of the runtime's environment, returned by Inspect.

type ResourceUsage

type ResourceUsage struct {
	UserCPU         time.Duration `json:"user_cpu_ns"`
	SystemCPU       time.Duration `json:"system_cpu_ns"`
	PeakMemoryBytes uint64        `json:"peak_memory_bytes"`
}

ResourceUsage reports resources consumed by an execution. Zero means the backend cannot measure the value.

type Result

type Result struct {
	// Stdout and Stderr hold the captured output (bounded by WithMaxOutput).
	Stdout string `json:"stdout"`
	Stderr string `json:"stderr"`
	// ExitCode is the exit status, or -1 if the program was killed by a signal.
	ExitCode int `json:"exit_code"`
	// Backend is the backend that executed the program (never BackendAuto).
	Backend Backend `json:"backend"`
	// Artifact describes the compiled program.
	Artifact *Artifact `json:"artifact"`
	// Duration is the execution wall time, excluding compilation.
	Duration time.Duration `json:"duration_ns"`
	Usage    ResourceUsage `json:"usage"`
}

Result is the outcome of Run.

type RunOption

type RunOption interface {
	// contains filtered or unexported methods
}

RunOption configures a single Run call.

func WithArgs

func WithArgs(args ...string) RunOption

WithArgs sets the program's command-line arguments (after argv[0]).

func WithStderr

func WithStderr(w io.Writer) RunOption

WithStderr streams the program's standard error to w as it is produced, in addition to capturing it in Result.Stderr.

func WithStdin

func WithStdin(r io.Reader) RunOption

WithStdin connects r to the program's standard input.

func WithStdout

func WithStdout(w io.Writer) RunOption

WithStdout streams the program's standard output to w as it is produced, in addition to capturing it in Result.Stdout.

type Runtime

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

Runtime compiles and executes Rust code. It is safe for concurrent use. Create one with New and release it with Close.

func New

func New(opts ...Option) (*Runtime, error)

New creates a Runtime. It does not require a Rust toolchain: the toolchain is detected on first use.

Example

Configure a runtime for trusted code on the native-process backend.

package main

import (
	"log"
	"time"

	"github.com/alirezahematidev/ferrogo"
)

func main() {
	rt, err := ferrogo.New(
		ferrogo.WithBackend(ferrogo.BackendNativeProcess),
		ferrogo.WithTrust(ferrogo.Trusted), // native code runs with your privileges
		ferrogo.WithProfile(ferrogo.ProfileRelease),
		ferrogo.WithTimeout(10*time.Second),
	)
	if err != nil {
		log.Fatal(err)
	}
	defer rt.Close()
}

func (*Runtime) Build

func (r *Runtime) Build(ctx context.Context, src Source, kind ArtifactKind, opts ...CompileOption) (*Artifact, error)

Build compiles src into an artifact for the configured backend without executing it. Build does not execute code, so the trust policy does not apply; it governs Run.

Example

Compile without running, for example to validate code or ship the artifact.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/alirezahematidev/ferrogo"
)

func main() {
	rt, err := ferrogo.New()
	if err != nil {
		log.Fatal(err)
	}
	defer rt.Close()

	art, err := rt.Build(context.Background(), ferrogo.Code(`fn main() {}`), ferrogo.ArtifactProgram)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(art.Target, art.Path, art.Cached)
}

func (*Runtime) CacheStats

func (r *Runtime) CacheStats() (CacheStats, error)

CacheStats reports cache usage.

func (*Runtime) ClearCache

func (r *Runtime) ClearCache() error

ClearCache deletes every cached artifact and build directory. Builds running concurrently in other processes that share the cache may fail.

func (*Runtime) Close

func (r *Runtime) Close() error

Close cancels in-flight operations, waits for them to return, and releases all resources. It is safe to call more than once.

func (*Runtime) Compile

func (r *Runtime) Compile(ctx context.Context, src Source, opts ...CompileOption) (*Module, error)

Compile compiles src as a library and loads it, so its exported functions can be called repeatedly with Module.Call. Functions are exported with the ferrogo::export! macro, which FerroGo makes available in every library crate:

m, err := rt.Compile(ctx, ferrogo.Code(`
    ferrogo::export! {
        fn add(a: i32, b: i32) -> i32 { a + b }
    }
`))

The module stays loaded until Module.Close or Runtime.Close.

Example

Compile a library once and call its exported functions repeatedly.

package main

import (
	"context"
	"errors"
	"fmt"
	"log"

	"github.com/alirezahematidev/ferrogo"
)

func main() {
	rt, err := ferrogo.New()
	if err != nil {
		log.Fatal(err)
	}
	defer rt.Close()

	mod, err := rt.Compile(context.Background(), ferrogo.Code(`
		ferrogo::export! {
			fn add(a: i32, b: i32) -> i32 { a + b }
			fn greet(name: String) -> Result<String, String> {
				if name.is_empty() { return Err("empty name".into()); }
				Ok(format!("Hello, {name}!"))
			}
		}
	`))
	if err != nil {
		log.Fatal(err)
	}
	defer mod.Close()

	sum, err := mod.Call(context.Background(), "add", ferrogo.Int32(10), ferrogo.Int32(20))
	if err != nil {
		log.Fatal(err)
	}
	n, _ := sum.AsInt32()
	fmt.Println(n) // 30

	_, err = mod.Call(context.Background(), "greet", ferrogo.String(""))
	var callErr *ferrogo.CallError
	if errors.As(err, &callErr) {
		fmt.Println(callErr.Message) // empty name
	}
}

func (*Runtime) Inspect

func (r *Runtime) Inspect(ctx context.Context) (*Report, error)

Inspect reports the toolchain, cache and the status of every backend. Problems are reported in the Report rather than as errors; the error is non-nil only if the runtime is closed or the cache is unreadable.

func (*Runtime) LoadModule

func (r *Runtime) LoadModule(ctx context.Context, path string, opts ...CompileOption) (*Module, error)

LoadModule loads a prebuilt library: a .wasm module (WASM backend) or a native shared library (native-library backend, which requires WithTrust(Trusted)). The kind is detected from the file contents.

func (*Runtime) LoadWASM

func (r *Runtime) LoadWASM(ctx context.Context, code []byte, opts ...CompileOption) (*Module, error)

LoadWASM loads a prebuilt WebAssembly library from memory, for example an artifact embedded with go:embed. It needs no Rust toolchain.

func (*Runtime) Run

func (r *Runtime) Run(ctx context.Context, src Source, opts ...RunOption) (*Result, error)

Run compiles src as a program (fn main) and executes it.

Run returns a non-nil *Result whenever the program started, even when it also returns an error: on a non-zero exit (*ExitError, possibly wrapping a *PanicError), a timeout (ErrTimeout) or an output-limit violation (ErrOutputLimit), the Result holds the output produced so far.

Example

Pass input to a program and handle the ways it can fail.

package main

import (
	"context"
	"errors"
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/alirezahematidev/ferrogo"
)

func main() {
	rt, err := ferrogo.New()
	if err != nil {
		log.Fatal(err)
	}
	defer rt.Close()

	res, err := rt.Run(context.Background(), ferrogo.File("wordcount.rs"),
		ferrogo.WithStdin(strings.NewReader("the quick brown fox")),
		ferrogo.WithArgs("--verbose"),
		ferrogo.WithEnv(map[string]string{"LANG": "en"}),
		ferrogo.WithTimeout(5*time.Second),
	)
	var (
		compileErr *ferrogo.CompileError
		panicErr   *ferrogo.PanicError
		exitErr    *ferrogo.ExitError
	)
	switch {
	case err == nil:
		fmt.Print(res.Stdout)
	case errors.As(err, &compileErr):
		for _, d := range compileErr.Errors() {
			fmt.Printf("%s: %s\n", d.Location(), d.Message)
		}
	case errors.As(err, &panicErr):
		fmt.Printf("panic at %s:%d: %s\n", panicErr.File, panicErr.Line, panicErr.Message)
	case errors.As(err, &exitErr):
		fmt.Printf("exit status %d; stderr: %s\n", exitErr.Code, res.Stderr)
	case errors.Is(err, ferrogo.ErrTimeout):
		fmt.Printf("timed out; partial output: %s\n", res.Stdout)
	default:
		log.Fatal(err)
	}
}

func (*Runtime) Toolchain

func (r *Runtime) Toolchain(ctx context.Context) (*ToolchainInfo, error)

Toolchain detects (once per Runtime) and describes the Rust toolchain.

type Sandbox

type Sandbox struct {
	// AllowNetwork permits network access.
	AllowNetwork bool
	// Writable lists host directories the program may write, besides its
	// private working directory.
	Writable []string
	// Readable lists directories inside the user's home directory the
	// program may read; file contents in the home directory are otherwise
	// hidden.
	Readable []string
}

Sandbox is an OS-level isolation policy for the native-process backend (see WithSandbox).

type SharedOption

type SharedOption interface {
	Option
	RunOption
	CompileOption
}

SharedOption is accepted by New (as a runtime default) and by Run and Build (as a per-call override).

func WithBackend

func WithBackend(b Backend) SharedOption

WithBackend selects the execution backend. The default is BackendAuto.

func WithBuildEnv

func WithBuildEnv(env map[string]string) SharedOption

WithBuildEnv adds environment variables visible to the compiler and build scripts. The host environment is otherwise scrubbed during builds.

func WithBuildOutput

func WithBuildOutput(w io.Writer) SharedOption

WithBuildOutput streams cargo's progress messages ("Compiling ...") to w.

func WithCompileTimeout

func WithCompileTimeout(d time.Duration) SharedOption

WithCompileTimeout bounds compilation time (default 5m; 0 disables).

func WithDependencies

func WithDependencies(deps ...Dependency) SharedOption

WithDependencies adds Cargo dependencies to code compiled from Code, File or Fragments sources. They are only used if the DependencyPolicy allows them. Resolved versions are recorded in a lockfile stored in the cache, so builds stay reproducible until the cache is cleared.

func WithDir

func WithDir(dir string) SharedOption

WithDir sets the working directory of executed programs. By default each run gets a fresh private temporary directory.

func WithEdition

func WithEdition(edition string) SharedOption

WithEdition sets the Rust edition ("2015", "2018", "2021" or "2024"). The default is 2021, because edition 2024 rejects bare #[no_mangle].

func WithEnv

func WithEnv(env map[string]string) SharedOption

WithEnv adds environment variables for executed code. Executed code sees no host environment variables unless granted here or by WithInheritEnv.

func WithGracePeriod

func WithGracePeriod(d time.Duration) SharedOption

WithGracePeriod sets how long a program may take to exit after a graceful termination request before it is killed (default 2s).

func WithHostFunction

func WithHostFunction(name string, fn HostFunc) SharedOption

WithHostFunction exposes fn to WASM modules under name. Host functions are capabilities you grant explicitly: WASM code can call nothing else. The native-library backend rejects them with ErrUnsupported.

func WithInheritEnv

func WithInheritEnv(names ...string) SharedOption

WithInheritEnv passes the named host environment variables (if set) to executed code.

func WithMaxOutput

func WithMaxOutput(n int64) SharedOption

WithMaxOutput bounds combined stdout+stderr bytes (default 4 MiB; 0 disables). A program exceeding it is terminated with ErrOutputLimit.

func WithMemoryLimit

func WithMemoryLimit(n int64) SharedOption

WithMemoryLimit bounds guest memory in bytes. Backends that cannot enforce it fail with ErrUnsupported rather than ignoring it. 0 means the backend default.

func WithMount

func WithMount(m Mount) SharedOption

WithMount grants sandboxed (WASM) code access to a host directory. WASM code sees no filesystem at all without mounts. Native backends have full filesystem access, so they reject mounts with ErrUnsupported rather than pretend to restrict access.

Example

Grant sandboxed code read-only access to one directory.

package main

import (
	"context"
	"fmt"
	"log"

	"github.com/alirezahematidev/ferrogo"
)

func main() {
	rt, err := ferrogo.New()
	if err != nil {
		log.Fatal(err)
	}
	defer rt.Close()

	res, err := rt.Run(context.Background(),
		ferrogo.Code(`fn main() { print!("{}", std::fs::read_to_string("/data/input.txt").unwrap()); }`),
		ferrogo.WithMount(ferrogo.Mount{HostPath: "./testdata", GuestPath: "/data", ReadOnly: true}),
	)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Print(res.Stdout)
}

func WithOptLevel

func WithOptLevel(level string) SharedOption

WithOptLevel overrides the optimisation level: "0"–"3", "s" or "z". Empty restores the profile default.

func WithProfile

func WithProfile(p Profile) SharedOption

WithProfile selects the build profile (default ProfileRelease). Project sources may also use custom profiles defined in their Cargo.toml.

func WithRustFlags

func WithRustFlags(flags ...string) SharedOption

WithRustFlags appends rustc flags (each element is one argument; nothing is split on spaces). They are part of the cache key.

func WithSandbox

func WithSandbox(s Sandbox) SharedOption

WithSandbox runs native processes under the operating system's sandbox: sandbox-exec on macOS, bubblewrap on Linux. The program then has no network (unless allowed), cannot write outside its working directory and the Writable directories, and cannot read the user's home directory (except Readable). Where no sandbox is available, Run fails with ErrUnsupported instead of running unsandboxed. The WASM backend is always sandboxed and rejects this option (use WithMount).

func WithTimeout

func WithTimeout(d time.Duration) SharedOption

WithTimeout bounds execution wall time of Run and of each Module.Call on the WASM backend (default 30s; 0 disables). Native-library calls cannot be interrupted, so an explicit timeout makes Compile fail with ErrUnsupported on that backend.

func WithWASMDeterminism

func WithWASMDeterminism(seed uint64) SharedOption

WithWASMDeterminism gives WASM code fixed, fake wall and monotonic clocks and a pseudo-random source (ChaCha8) seeded with seed, so repeated runs of the same code with the same inputs behave identically: useful for tests, replays and consensus-style workloads. Sleeps still take real time (and remain interruptible). It does not apply to native backends.

func WithWASMInterruption

func WithWASMInterruption(enabled bool) SharedOption

WithWASMInterruption controls the checks that let timeouts and cancellation stop running WASM code (default enabled). Disabling them makes call-heavy code up to ~9x faster (docs/performance.md) but running code can then only finish on its own: it requires WithTrust(Trusted), and an explicit WithTimeout is rejected with ErrUnsupported. Compiled code is cached separately for each setting.

func WithWASMMemoryLimit

func WithWASMMemoryLimit(n int64) SharedOption

WithWASMMemoryLimit sets the default WASM guest memory limit in bytes (default 256 MiB, rounded down to 64 KiB pages, at most 4 GiB). Unlike WithMemoryLimit it only applies to the WASM backend.

type Signature

type Signature struct {
	Params []Kind
	Result Kind
}

Signature declares the C signature of a plain `#[no_mangle] extern "C"` function for Module.CallRaw. Params and Result use the scalar kinds (KindBool, KindInt32, KindInt64, KindUint32, KindUint64, KindFloat32, KindFloat64); Result may be KindVoid.

func (Signature) String

func (s Signature) String() string

type Source

type Source interface {
	// contains filtered or unexported methods
}

Source is Rust code to compile: Code, File, Fragments or Project.

type Span

type Span struct {
	File      string `json:"file"`
	Line      int    `json:"line"`
	Column    int    `json:"column"`
	EndLine   int    `json:"end_line"`
	EndColumn int    `json:"end_column"`
	Label     string `json:"label,omitempty"`
	Primary   bool   `json:"primary"`
}

Span is a source range in a diagnostic. Lines and columns are 1-based.

type TargetError

type TargetError struct {
	Target  string
	Backend Backend
	// Fix is a command or instruction that installs the target.
	Fix string
}

TargetError reports a missing compilation target.

func (*TargetError) Error

func (e *TargetError) Error() string

func (*TargetError) Unwrap

func (e *TargetError) Unwrap() error

Unwrap matches ErrTargetNotInstalled.

type ToolchainConfig

type ToolchainConfig struct {
	// Rustc is the rustc executable (default: rustc on PATH).
	Rustc string
	// Cargo is the cargo executable (default: cargo next to the sysroot, then PATH).
	Cargo string
	// Toolchain is a rustup toolchain name such as "stable" or "1.94.1".
	Toolchain string
	// RustupHome and CargoHome isolate rustup and cargo state, e.g. for a
	// per-project toolchain that needs no system-wide installation.
	RustupHome string
	CargoHome  string
}

ToolchainConfig selects the Rust toolchain. Zero values mean auto-detection.

type ToolchainInfo

type ToolchainInfo struct {
	// Rustc and Cargo are the executables used for builds.
	Rustc string `json:"rustc"`
	Cargo string `json:"cargo"`
	// Rustup is the rustup executable, or empty if rustup is not installed.
	Rustup       string   `json:"rustup,omitempty"`
	Version      string   `json:"version"`
	CommitHash   string   `json:"commit_hash"`
	CommitDate   string   `json:"commit_date"`
	Host         string   `json:"host"`
	LLVMVersion  string   `json:"llvm_version"`
	CargoVersion string   `json:"cargo_version"`
	Sysroot      string   `json:"sysroot"`
	Targets      []string `json:"targets"`
}

ToolchainInfo describes the detected Rust toolchain.

func (*ToolchainInfo) HasTarget

func (t *ToolchainInfo) HasTarget(triple string) bool

HasTarget reports whether the standard library for triple is installed.

type TrustLevel

type TrustLevel int

TrustLevel declares how much the host trusts the Rust code it runs.

const (
	// Untrusted (the default) allows only sandboxed backends (WASM).
	Untrusted TrustLevel = iota
	// Trusted additionally allows native backends, which run code with the
	// privileges of the host user (native process) or the host process
	// itself (native library).
	Trusted
)

func ParseTrustLevel

func ParseTrustLevel(s string) (TrustLevel, error)

ParseTrustLevel parses "untrusted" or "trusted".

func (TrustLevel) String

func (t TrustLevel) String() string

type UnsupportedError

type UnsupportedError struct {
	Backend Backend
	Feature string
	// Hint suggests an alternative, if any.
	Hint string
}

UnsupportedError reports an operation or limit a backend cannot provide.

func (*UnsupportedError) Error

func (e *UnsupportedError) Error() string

func (*UnsupportedError) Unwrap

func (e *UnsupportedError) Unwrap() error

Unwrap matches ErrUnsupported.

type Value

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

Value is an immutable value passed to or returned from a Rust function. The zero Value is KindVoid. Create values with the constructors (Int32, String, JSON, ...); read them with the As* methods, which fail with ErrABIMismatch on a kind mismatch.

func Bool

func Bool(b bool) Value

Bool returns a bool value.

func Bytes

func Bytes(b []byte) Value

Bytes returns a Vec<u8> value holding a copy of b.

func Float32

func Float32(f float32) Value

Float32 returns an f32 value.

func Float64

func Float64(f float64) Value

Float64 returns an f64 value.

func Int32

func Int32(n int32) Value

Int32 returns an i32 value.

func Int64

func Int64(n int64) Value

Int64 returns an i64 value.

func JSON

func JSON(v any) (Value, error)

JSON marshals v with encoding/json into a JSON value (Rust ferrogo::Json), the way structs cross the boundary.

func RawJSON

func RawJSON(data []byte) (Value, error)

RawJSON returns a JSON value from already-encoded JSON text, which must be valid.

func String

func String(s string) Value

String returns a String value. Strings must be valid UTF-8 (Rust requires it); Module.Call reports invalid UTF-8 as ErrABIMismatch.

func Uint32

func Uint32(n uint32) Value

Uint32 returns a u32 value.

func Uint64

func Uint64(n uint64) Value

Uint64 returns a u64 value.

func Void

func Void() Value

Void returns the unit value (Rust ()).

func (Value) AsBool

func (v Value) AsBool() (bool, error)

AsBool returns the value of a KindBool value.

func (Value) AsBytes

func (v Value) AsBytes() ([]byte, error)

AsBytes returns a copy of the value of a KindBytes value.

func (Value) AsFloat32

func (v Value) AsFloat32() (float32, error)

AsFloat32 returns the value of a KindFloat32 value.

func (Value) AsFloat64

func (v Value) AsFloat64() (float64, error)

AsFloat64 returns the value of a KindFloat64 value.

func (Value) AsInt32

func (v Value) AsInt32() (int32, error)

AsInt32 returns the value of a KindInt32 value.

func (Value) AsInt64

func (v Value) AsInt64() (int64, error)

AsInt64 returns the value of a KindInt64 value.

func (Value) AsJSON

func (v Value) AsJSON() ([]byte, error)

AsJSON returns a copy of the JSON text of a KindJSON value.

func (Value) AsString

func (v Value) AsString() (string, error)

AsString returns the value of a KindString value.

func (Value) AsUint32

func (v Value) AsUint32() (uint32, error)

AsUint32 returns the value of a KindUint32 value.

func (Value) AsUint64

func (v Value) AsUint64() (uint64, error)

AsUint64 returns the value of a KindUint64 value.

func (Value) Decode

func (v Value) Decode(dst any) error

Decode unmarshals a KindJSON value into dst with encoding/json.

func (Value) Kind

func (v Value) Kind() Kind

Kind returns the value's kind.

func (Value) String

func (v Value) String() string

String formats the value for humans, e.g. `i32(30)` or `String("hi")`. It implements fmt.Stringer; use AsString to read a string value.

Directories

Path Synopsis
cmd
ferrogo command
Command ferrogo compiles and runs Rust code through the FerroGo library.
Command ferrogo compiles and runs Rust code through the FerroGo library.
examples
basic command
Command basic is the FerroGo quick start: compile a Rust snippet and run it in the default backend (the WebAssembly sandbox).
Command basic is the FerroGo quick start: compile a Rust snippet and run it in the default backend (the WebAssembly sandbox).
functions command
Command functions compiles a Rust library once and calls its exported functions repeatedly: on the WASM sandbox, and (as trusted code) as a native shared library loaded into this process.
Command functions compiles a Rust library once and calls its exported functions repeatedly: on the WASM sandbox, and (as trusted code) as a native shared library loaded into this process.
inline command
Command inline shows Rust written directly inside a Go file.
Command inline shows Rust written directly inside a Go file.
native command
Command native runs Rust programs as native child processes.
Command native runs Rust programs as native child processes.
wasm command
Command wasm shows the WebAssembly sandbox: untrusted code gets no filesystem, network, or host environment unless granted, and is stopped by memory, time, and output limits.
Command wasm shows the WebAssembly sandbox: untrusted code gets no filesystem, network, or host environment unless granted, and is stopped by memory, time, and output limits.
internal
abi
Package abi embeds the Rust side of the FerroGo ABI (ferrogo.rs) and injects it into library crates.
Package abi embeds the Rust side of the FerroGo ABI (ferrogo.rs) and injects it into library crates.
backend
Package backend defines the contract between the public ferrogo package and its execution backends.
Package backend defines the contract between the public ferrogo package and its execution backends.
backend/dylib
Package dylib loads native shared libraries (.so, .dylib, .dll) built with the FerroGo ABI helper into the current process and calls their exports.
Package dylib loads native shared libraries (.so, .dylib, .dll) built with the FerroGo ABI helper into the current process and calls their exports.
backend/native
Package native executes compiled Rust programs as operating-system processes, and serves function calls from worker processes.
Package native executes compiled Rust programs as operating-system processes, and serves function calls from worker processes.
backend/wasm
Package wasm executes WebAssembly (wasm32-wasip1) programs in the wazero runtime.
Package wasm executes WebAssembly (wasm32-wasip1) programs in the wazero runtime.
cache
Package cache implements FerroGo's persistent, content-addressed artifact store.
Package cache implements FerroGo's persistent, content-addressed artifact store.
compiler
Package compiler turns Rust sources into artifacts.
Package compiler turns Rust sources into artifacts.
errdefs
Package errdefs defines the sentinel errors shared by FerroGo's internal packages.
Package errdefs defines the sentinel errors shared by FerroGo's internal packages.
inline
Package inline implements `ferrogo generate`: it extracts Rust code embedded in a Go package (/*ferrogo ...
Package inline implements `ferrogo generate`: it extracts Rust code embedded in a Go package (/*ferrogo ...
outcap
Package outcap captures program output under a combined byte limit, optionally forwarding it live to another writer.
Package outcap captures program output under a combined byte limit, optionally forwarding it live to another writer.
process
Package process runs external programs safely: arguments are passed as an argv vector (never through a shell), the environment is always explicit, output is captured with a hard limit, and cancellation terminates the whole process group gracefully before killing it.
Package process runs external programs safely: arguments are passed as an argv vector (never through a shell), the environment is always explicit, output is captured with a hard limit, and cancellation terminates the whole process group gracefully before killing it.
sandbox
Package sandbox runs processes under the operating system's sandbox: Seatbelt (sandbox-exec) on macOS and bubblewrap on Linux.
Package sandbox runs processes under the operating system's sandbox: Seatbelt (sandbox-exec) on macOS and bubblewrap on Linux.
toolchain
Package toolchain discovers the Rust toolchain (rustc, cargo, sysroot and installed targets) and builds the scrubbed environment used to run it.
Package toolchain discovers the Rust toolchain (rustc, cargo, sysroot and installed targets) and builds the scrubbed environment used to run it.
wire
Package wire implements the Go side of the FerroGo wire format v1: the tagged, little-endian encoding of values crossing the Go/Rust boundary (docs/abi.md §5).
Package wire implements the Go side of the FerroGo wire format v1: the tagged, little-endian encoding of values crossing the Go/Rust boundary (docs/abi.md §5).

Jump to

Keyboard shortcuts

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