ferrule

imports

α1
basic-importselective-importcapability-gated-imports (α2)content-addressed-imports (β)derivation-mutation (β)

imports

ferrule uses a rust-like module system. files are modules, directories with mod.fe are submodules.

project structure

filepurpose
Package.fepackage manifest (like cargo.toml)
ferrule.locklockfile for dependencies
src/main.febinary entrypoint
src/lib.felibrary root (optional)
src/parser/mod.fesubmodule definition
src/parser/lexer.fefile in submodule
tests/*.fetest files

directories with mod.fe become submodules. the module path matches the file path: src/parser/lexer.fe is parser.lexer.

visibility

function helper() { }              // default: module-private
pub function process() { }         // public to package
pub export function api() { }      // public to everyone (lib.fe only)

local imports

// from local modules
import parser { Parser, ParseError };  // from src/parser/mod.fe
import utils { helper };               // from src/utils.fe

// re-export
pub import lexer { Token };  // re-export from lexer.fe

basic import

Import by name (resolved via Package.fe manifest and ferrule.lock):

import time { Clock };
import mylib.http { get, post };

Capability-Gated Imports

An import can declare that loading requires build-time capability permission:

import time { Clock } using capability time;
import mylib.http { get, post } using capability net;

This is build-time capability gating — the build tool must have permission X to load the import.

At runtime, capabilities are still passed explicitly as values (no ambient authority).


Content-Addressed Imports

For critical dependencies, import by explicit content address:

import store://sha256:e1f4a3b2... { io as stdio } using capability fs;

Direct hash imports:

  • pin to exact versions
  • bypass name resolution
  • provide maximum reproducibility

Import Resolution

Name-based imports are resolved:

  1. Check Package.fe manifest for declared dependencies
  2. Look up content address in ferrule.lock
  3. Fetch from cache or registry
  4. Verify content hash

Aliasing

Rename imports with as:

import net.http { Client as HttpClient };
import store://sha256:... { io as stdio };

Selective Imports

Import specific items:

import net.http { get, post, Client };

Or import the module namespace:

import net.http;
// use as: http.get(...), http.Client, etc.

Derivation Mutation

Imports can override derivation parameters, producing a different content address:

import stdlib { io } with { features: { simd: false } };

The with { ... } clause:

  • modifies the imported package's build configuration
  • creates a variant with a different content address
  • the lockfile records both original and mutated addresses

Example

package my.app;

// standard imports
import time { Clock } using capability time;
import net.http { Client, Response } using capability net;

// pinned critical dependency
import store://sha256:abc123... { crypto } using capability ffi;

// modified derivation
import stdlib { io } with { features: { debug: true } };

On this page