Development Guide

LibreFang development environment setup and contribution guidelines.


Development Environment

Prerequisites

  • Rust 1.75+
  • Node.js 18+ (for desktop app)
  • pnpm 8+ (for documentation site)

Clone Repository

git clone https://github.com/librefang/librefang.git
cd librefang

Build

# Build entire workspace
cargo build --workspace

# Build CLI
cargo build -p librefang-cli

# Build desktop app
cargo build -p librefang-desktop

# Build React dashboard (required for web UI)
cd crates/librefang-api/dashboard-react
npm install
npm run build

Test

# Run all tests
cargo test --workspace

# Run specific crate
cargo test -p librefang-kernel

# Run doc tests
cargo test --doc

Linting

# Must have zero warnings
cargo clippy --workspace --all-targets -- -D warnings

# Format check
cargo fmt --all -- --check

Project Structure

librefang/
├── Cargo.lock
├── Cargo.toml
├── crates/
   ├── librefang-cli/          # CLI tool
   ├── librefang-api/         # REST API server
   ├── librefang-kernel/      # Core kernel
   ├── librefang-runtime/     # Agent runtime
   ├── librefang-memory/      # Memory subsystem
   ├── librefang-types/       # Shared types
   ├── librefang-channels/    # Channel adapters
   ├── librefang-skills/      # Skill system
   ├── librefang-hands/       # Hands system
   ├── librefang-wire/        # P2P protocol
   ├── librefang-desktop/     # Desktop app
   ├── librefang-migrate/     # Migration tools
   └── librefang-extensions/   # Extensions
├── xtask/                      # Build scripts
├── docs/                       # Project documentation
└── scripts/                    # Helper scripts

Adding New Channel

1. Create Channel Module

// crates/librefang-channels/src/my_channel.rs

use async_trait::async_trait;
use std::pin::Pin;
use futures::Stream;
use crate::types::{ChannelAdapter, ChannelContent, ChannelMessage, ChannelStatus, ChannelType, ChannelUser};

pub struct MyChannel {
    // channel-specific fields (e.g. API client, config)
}

#[async_trait]
impl ChannelAdapter for MyChannel {
    fn name(&self) -> &str {
        "my_channel"
    }

    fn channel_type(&self) -> ChannelType {
        ChannelType::Custom("my_channel".to_string())
    }

    async fn start(
        &self,
    ) -> Result<
        Pin<Box<dyn Stream<Item = ChannelMessage> + Send>>,
        Box<dyn std::error::Error + Send + Sync>,
    > {
        // Return a stream of incoming messages
        todo!()
    }

    async fn send(
        &self,
        _user: &ChannelUser,
        _content: ChannelContent,
    ) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        // Send response back to the user
        Ok(())
    }

    async fn stop(&self) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
        Ok(())
    }
}

2. Register Channel

Channel adapters are registered with BridgeManager in librefang-api/src/server.rs by calling bridge_manager.add_adapter(Arc::new(MyChannel::new(config))). Each adapter is gated behind a Cargo feature flag (channel-my-channel) in crates/librefang-channels/Cargo.toml.

// crates/librefang-channels/src/lib.rs — add the feature-gated module
#[cfg(feature = "channel-my-channel")]
pub mod my_channel;

3. Add Configuration

// crates/librefang-types/src/config.rs

#[derive(Debug, Clone, Deserialize)]
pub struct MyChannelConfig {
    pub api_key_env: String,
    pub allowed_users: Option<Vec<String>>,
}

Adding New Skill

1. Create Skill Structure

# skills/my-skill/skill.toml
name = "my-skill"
version = "1.0.0"
description = "My custom skill"

[runtime]
type = "python"
entrypoint = "main.py"

[tools]
provided = ["my_tool"]

[requirements]
packages = ["requests"]

2. Implement Code

# skills/my-skill/main.py

def my_tool(param: str) -> str:
    """My custom tool description"""
    # Implement logic
    return f"Result: {param}"

# Register tools
TOOLS = [my_tool]

3. Compile Skill

# Skills are automatically compiled into binary
cargo build --workspace

Adding New Tool

1. Define Tool

// crates/librefang-runtime/src/tools/mod.rs

use crate::tool::{Tool, ToolResult};

pub struct MyTool;

impl Tool for MyTool {
    fn name(&self) -> &str {
        "my_tool"
    }

    fn description(&self) -> &str {
        "My custom tool description"
    }

    async fn execute(&self, params: Value) -> ToolResult {
        // Implement tool logic
        Ok(Value::String("result".to_string()))
    }
}

2. Register Tool

// crates/librefang-runtime/src/lib.rs

pub fn register_tools(registry: &mut ToolRegistry) {
    registry.register(MyTool::new());
}

Adding New LLM Provider

1. Implement Driver

// crates/librefang-runtime/src/llm/my_provider.rs

use crate::llm::{LlmDriver, LlmResponse, LlmError};

pub struct MyProvider {
    api_key: String,
    base_url: String,
}

#[async_trait]
impl LlmDriver for MyProvider {
    async fn complete(&self, prompt: &str) -> Result<LlmResponse, LlmError> {
        // Call API
        Ok(LlmResponse {
            text: "response".to_string(),
            tokens: 100,
        })
    }
}

2. Add to Model Catalog

// crates/librefang-types/src/models.rs

pub fn get_provider(name: &str) -> Option<Box<dyn LlmDriver>> {
    match name {
        "my_provider" => Some(Box::new(MyProvider::new())),
        _ => None,
    }
}

Code Style

Rust Standards

  • Format with cargo fmt
  • Check with cargo clippy
  • Follow Rust naming conventions
  • Add documentation comments (///)

Commit Standards

# Format: <type>(<scope>): <description>

git commit -m "feat(kernel): add new scheduling algorithm"
git commit -m "fix(channels): resolve Slack rate limit"
git commit -m "docs(api): update endpoint documentation"
git commit -m "test(runtime): add tool execution tests"

Types

TypeDescription
featNew feature
fixBug fix
docsDocumentation
styleFormatting
refactorRefactoring
testTesting
choreMaintenance

Testing

Unit Tests

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_my_function() {
        assert_eq!(my_function(2), 4);
    }
}

Integration Tests

#[tokio::test]
async fn test_agent_spawn() {
    let kernel = Kernel::new().await;
    let agent = kernel.spawn("test-agent").await;
    assert!(agent.is_running());
}

Benchmark Tests

#[tokio::bench]
async fn benchmark_llm_call(b: &mut Bencher) {
    b.iter(|| {
        runtime.block_on(llm.complete("test prompt"))
    })
}

Debugging

Logging

use tracing::{info, warn, error};

info!("Starting agent {}", agent_id);
warn!("Rate limit exceeded for channel {}", channel_id);
error!("Failed to connect to provider: {}", error);

Debug Mode

# Enable verbose logging
RUST_LOG=debug cargo run

# See specific module only
RUST_LOG=librefang_kernel=trace cargo run

Performance Profiling

# CPU profiling
cargo flamegraph --bin librefang-cli -- start

# Memory profiling
cargo leptos --bin librefang-cli -- start

Release

Versioning

LibreFang uses Calendar VersioningYYYY.M.DDHH.

TypeFormatExampleDescription
StableYYYY.M.DDHH2026.3.2314Day + hour of release
BetaYYYY.M.DDHH-betaN2026.3.2314-beta1Pre-release for testing
RCYYYY.M.DDHH-rcN2026.3.2314-rc1Release candidate
LTSYYYY.M.PATCH-lts2026.3.0-ltsLong-term support

Release Process

All releases are done via cargo xtask release:

cargo xtask release
#   1) stable  -> 2026.3.2314
#   2) beta    -> 2026.3.2314-beta1
#   3) rc      -> 2026.3.2314-rc1
#   4) lts     -> 2026.3.0-lts

This command automatically:

  • Generates CHANGELOG from git history
  • Syncs version across Cargo.toml, package.json, setup.py
  • Builds React dashboard
  • Creates git tag and push
  • Opens a PR for the version bump

Use --dry-run to preview without making changes:

cargo xtask release --dry-run --version "2026.3.0-lts"

LTS Releases

LTS (Long-Term Support) versions receive only security and bug fixes. No new features.

Version scheme:

v2026.3.0-lts initial LTS
v2026.3.1-lts patch 1 (bug fix)
v2026.3.2-lts patch 2 (security fix)

Creating an LTS release:

# On main branch — choose option 4
cargo xtask release

CI automatically:

  • Creates a release/2026.3 branch from the tag
  • Sets branch protection (requires PR review)
  • Tags Docker image with :lts
  • Marks the GitHub Release as LTS

Patching an LTS release:

# Switch to the LTS branch
git checkout release/2026.3

# Cherry-pick the fix from main
git cherry-pick <commit-sha>

# Release the patch (auto-increments patch number)
cargo xtask release --lts-patch

Rules:

  • Only cherry-pick fixes, never new features
  • Every patch must go through PR review on the release/ branch
  • LTS branches are maintained until the next LTS is declared
  • Docker :lts tag always points to the latest LTS release

Contributing

Contribution Process

  1. Fork the repository
  2. Create feature branch (git checkout -b feature/my-feature)
  3. Commit changes (git commit -m "feat: add my feature")
  4. Push branch (git push origin feature/my-feature)
  5. Create Pull Request

Code Review

  • Ensure cargo clippy has no warnings
  • Ensure cargo test passes
  • Add test coverage for new code
  • Update documentation

Code of Conduct

  • Respect others
  • Welcome newcomers
  • Communicate professionally
  • Accept constructive criticism