ferrule documentation[styled mode]
specrfcshome

packages

Status: α1


packages


Package Declaration

In .fe source files, declare the package name only (no version):

package net.http;

The content address (hash of source + derivation) is computed by build tools, not written in source.


project structure

filepurpose
deps.fedependencies (cli-editable, restricted format)
build.febuild configuration (full ferrule code, optional)
ferrule.locklocked content addresses
src/main.feapplication entrypoint

deps.fe (Dependency Declaration)

A restricted format that CLI tools can read and modify:

.{
  .name = "my-app",
  .version = "0.1.0",
  
  .dependencies = .{
    // registry shorthand
    .http = .{ .pkg = "net/http", .version = "~> 1.2" },
    .json = .{ .pkg = "data/json", .version = "^2.0" },
    
    // direct URL with hash
    .crypto = .{ 
      .url = "https://github.com/ferrule/crypto/archive/v1.0.0.tar.gz",
      .hash = "sha256:abc123..."
    },
  },
  
  .dev_dependencies = .{
    .testing = .{ .pkg = "std/testing", .version = "~> 1.0" },
  },
}

Version Specifiers

SyntaxMeaning
"1.2.3"Exact version
"~> 1.2"Compatible with 1.2.x
"^1.2"Compatible with 1.x.x
">= 1.0, < 2.0"Range

Dependency Sources

SourceSyntax
Registry.pkg = "namespace/name"
URL.url = "https://...", .hash = "sha256:..."
Git.git = "https://...", .rev = "..."
Local.path = "../local-dep"

build.fe (Build Configuration)

For complex builds, use full Ferrule code:

const build = @import("std/build");
const deps = @import("deps.fe");

pub function configure(b: build.Builder) -> Unit {
  const exe = b.addExecutable(.{
    .name = "my-app",
    .root = "src/main.fe",
  });
  
  // add dependencies
  exe.addDependency(deps.http);
  exe.addDependency(deps.json);
  
  // configure target
  exe.setTarget(b.standardTargetOptions());
  exe.setOptimize(b.standardOptimizeOption());
  
  // custom build steps
  if b.option("with-debug") {
    exe.addDefine("DEBUG", "1");
  }
}

Most projects only need deps.fe. Use build.fe when you need:


Content Addressing

A package's content address is:

hash(source_tree + derivation)

Properties:


Lockfile (ferrule.lock)

Pins exact content addresses:

# ferrule.lock — auto-generated, do not edit manually

[[package]]
name = "net/http"
version = "1.2.3"
hash = "sha256:2f7c84a93fb..."
source = "registry"
dependencies = ["sha256:a1b2c3..."]

[[package]]
name = "crypto"
version = "1.0.0"
hash = "sha256:def456..."
source = "https://github.com/ferrule/crypto/archive/v1.0.0.tar.gz"
dependencies = []

Properties:


Package Capabilities

Packages declare required capabilities:

// in deps.fe
.{
  .name = "my-server",
  .capabilities = .{ .fs, .net, .time },
  
  .dependencies = .{
    .http = .{ 
      .pkg = "net/http", 
      .version = "~> 1.2",
      .capabilities = .{ .net }  // this dep needs net
    },
  },
}

Build tool enforces capability requirements:

$ ferrule build
error: dependency 'net/http' requires capability [net]

  Your project has not declared this capability.
  
  Add to deps.fe:
    .capabilities = .{ .net },

Registry vs Direct URLs

Both are supported:

.dependencies = .{
  // registry (convenient)
  .http = .{ .pkg = "net/http", .version = "~> 1.2" },
  
  // direct URL (no registry dependency)
  .crypto = .{ 
    .url = "https://...", 
    .hash = "sha256:..." 
  },
}

Registry is a convenience layer; direct URLs always work.


Provenance

Build artifacts embed provenance information:

This enables verification of how any artifact was built.


CLI Commands

# create new project
ferrule new my-app

# add dependency (updates deps.fe and ferrule.lock)
ferrule add net/http

# add specific version
ferrule add net/http@1.2.3

# add from URL
ferrule add --url https://... --hash sha256:...

# update dependencies
ferrule update

# build
ferrule build

# run
ferrule run

Summary

FilePurposeEditable
deps.feDependencies (restricted format)CLI + manual
build.feBuild logic (full code)Manual only
ferrule.lockLocked hashesCLI only