RavenFabric
Security-first distributed execution engine. Network-agnostic, E2E encrypted, policy-driven, ZTNA.
RavenFabric is a universal agent that provides secure, policy-controlled access to any system — regardless of network topology, operating system, or device class. It unifies mesh VPN, remote execution, configuration management, and zero-trust access into a single binary with no runtime dependencies.
Key Properties
- E2E encrypted — Noise XX mutual authentication (same crypto core as WireGuard)
- Deny-by-default — Policy engine rejects anything not explicitly allowed
- Network-agnostic — Works over WebSocket, QUIC, WireGuard, Tor, LoRa, Bluetooth, and more
- Single static binary — No runtime dependencies, deploys anywhere
- Audit everything — Every action produces a structured JSON audit entry
Quick Links
Installation
From Source (Recommended)
RavenFabric requires Rust 1.85+ (Edition 2024).
# Clone the repository
git clone https://github.com/egkristi/RavenFabric.git
cd RavenFabric
# Build all binaries (release mode)
cargo build --release
# Binaries are in target/release/
ls target/release/rf target/release/rf-agent target/release/rf-relay
Static Binary (Linux)
For Linux deployments, build with musl for a fully static binary:
# Install musl target
rustup target add x86_64-unknown-linux-musl
# Build static binary
cargo build --release --target x86_64-unknown-linux-musl
Verify Installation
# Check CLI
./target/release/rf --version
# Check agent
./target/release/rf-agent --help
# Check relay
./target/release/rf-relay --help
Platform Support
| Platform | Status | Notes |
|---|---|---|
| Linux x86_64 | Fully supported | Static musl binaries |
| Linux aarch64 | Fully supported | Static musl binaries |
| macOS x86_64 | Fully supported | |
| macOS aarch64 | Fully supported | Apple Silicon |
| Windows x86_64 | Fully supported | |
| Linux armv7 | Best effort | Raspberry Pi |
| FreeBSD | Best effort | |
| Android | Planned | NDK cross-compile |
| iOS | Planned | Network Extension |
Quick Start
This guide walks through a minimal setup: one relay, one agent, one CLI client.
1. Generate Keys
# Generate agent keypair
rf-agent --generate-key /etc/ravenfabric/agent.key
# Generate CLI keypair
rf --generate-key ~/.config/ravenfabric/cli.key
2. Start the Relay
The relay is a stateless encrypted broker. It never sees plaintext.
rf-relay --listen 0.0.0.0:9090 --secret "your-meet-secret"
3. Start the Agent
rf-agent \
--relay wss://relay.example.com:9090/meet \
--key /etc/ravenfabric/agent.key \
--policy /etc/ravenfabric/policy.yaml \
--id web-01
4. Execute a Command
# One-time enrollment token
rf exec --relay wss://relay.example.com:9090/meet \
--token "enrollment-token" \
"hostname"
Output:
web-01
5. Verify Audit Log
Every action is logged:
{
"timestamp": "2024-01-15T10:30:00Z",
"action": "exec",
"command": "hostname",
"agent_id": "web-01",
"result": "success",
"exit_code": 0
}
Next Steps
- Configuration — Full agent configuration
- Policy Configuration — Define what's allowed
- Security Model — Understand the trust model
Configuration
RavenFabric uses a TOML configuration file (raven.toml).
Agent Configuration
[agent]
id = "web-01"
relay = "wss://relay.example.com/meet"
key_path = "/etc/ravenfabric/agent.key"
policy_path = "/etc/ravenfabric/policy.yaml"
audit_path = "/var/log/ravenfabric/audit.jsonl"
[transport]
driver = "websocket"
reconnect_interval = 5
max_retries = 0 # infinite
[resources]
max_output_bytes = 10485760 # 10 MB
timeout_seconds = 300 # 5 minutes
max_concurrent = 10
Relay Configuration
[relay]
listen = "0.0.0.0:9090"
meet_secret = "env:RELAY_SECRET" # Load from environment
[rate_limit]
requests_per_second = 100
burst = 200
per_ip = true
CLI Configuration
The CLI reads from ~/.config/ravenfabric/config.toml:
[cli]
default_relay = "wss://relay.example.com/meet"
key_path = "~/.config/ravenfabric/cli.key"
timeout = 30
Environment Variables
| Variable | Description |
|---|---|
RELAY_SECRET | Relay meet secret (when using env: prefix) |
RF_KEY_PATH | Override key file path |
RF_RELAY | Override relay URL |
RF_POLICY | Override policy file path |
RUST_LOG | Logging level (info, debug, trace) |
Architecture Overview
RavenFabric is organized as a layered architecture with strict dependency boundaries.
Layers
┌──────────────────────────────────┐
│ Application Layer │ rf-cli, rf-agent, rf-relay
│ (binaries, user-facing tools) │
├──────────────────────────────────┤
│ Executor Layer │ rf-executor
│ (command execution, streaming) │
├──────────────────────────────────┤
│ Policy Layer │ rf-policy
│ (deny-by-default enforcement) │
├──────────────────────────────────┤
│ RPC Layer │ rf-rpc
│ (message types, codec, mux) │
├──────────────────────────────────┤
│ Transport Layer │ rf-transport
│ (drivers, connection mgmt) │
├──────────────────────────────────┤
│ Crypto Layer │ rf-crypto
│ (Noise XX, key management) │
└──────────────────────────────────┘
Crates
| Crate | Purpose | LOC | Tests |
|---|---|---|---|
rf-crypto | Noise XX handshake, SecureChannel, key management, post-quantum KEM | ~1,300 | 25 |
rf-transport | Driver trait, WebSocket/QUIC/Memory, NAT, mesh, WireGuard, overlays | ~5,300 | 121 |
rf-rpc | Message types, msgpack codec, yamux mux, DTN, routing, controller | ~2,900 | 61 |
rf-audit | Structured JSON-lines audit logging | 53 | — |
rf-policy | Policy enforcement, RBAC, capabilities, distributed policy | ~1,500 | 31 |
rf-executor | Command execution, streaming, orchestration, PTY, plugins | ~3,600 | 48 |
rf-bootstrap | OTP enrollment, TrustStore | ~380 | 11 |
Total: ~16,700 LOC | 336 tests | 0 clippy warnings
Data Flow
Client (rf CLI)
│
│ Noise XX handshake
│ ↕ mutual authentication
│
├── SecureChannel (E2E encrypted)
│ │
│ │ yamux multiplexed
│ │
│ ├── RPC stream (msgpack)
│ │ ├── Request → Policy check → Execute → Audit → Response
│ │ └── Streaming stdout/stderr
│ │
│ └── Control stream
│ ├── Heartbeat
│ └── Metrics
│
└── Transport (WebSocket / QUIC / Memory / ...)
│
└── Relay (opaque forwarding, never decrypts)
│
└── Agent (rf-agent)
├── Policy engine (final authority)
├── Executor (sandboxed)
└── Audit log (append-only)
Design Principles
- Security is non-negotiable — No command executes without policy check
- Agent is final authority — Orchestrator cannot override agent policy
- Zero trust — Every connection mutually authenticated
- Audit everything — Every action logged, no exceptions
- Network agnostic — Any byte-moving channel is a valid transport
- Single binary — No runtime dependencies
Security Model
RavenFabric's security is built on three pillars: cryptographic identity, deny-by-default policy, and comprehensive audit logging.
Trust Model
┌─────────────┐
│ Trust Root │
│ (key pair) │
└──────┬──────┘
│
┌────────────┼────────────┐
│ │ │
┌────▼────┐ ┌────▼────┐ ┌───▼────┐
│ Agent │ │ Agent │ │ CLI │
│ key pair│ │ key pair│ │key pair│
└─────────┘ └─────────┘ └────────┘
Every entity has a unique Ed25519 key pair. Identity is cryptographic — there are no usernames, passwords, or certificates.
Noise XX Handshake
All connections use the Noise XX handshake pattern:
Noise_XX_25519_ChaChaPoly_BLAKE2s
This provides:
- Mutual authentication — Both sides prove their identity
- Forward secrecy — Ephemeral keys per session
- Identity hiding — Static keys encrypted during handshake
- Relay opacity — Relay sees only random bytes
Handshake Flow
Initiator Responder
│ │
│── e ─────────────────────────► │ (ephemeral key)
│ │
│ ◄──────────────── e, ee, s, es │ (ephemeral + static)
│ │
│── s, se ─────────────────────► │ (static key, encrypted)
│ │
│ [secure channel] │
Policy Engine
The policy engine is deny-by-default. If a rule doesn't explicitly allow an action, it is denied.
Two-Phase Check
- Controller pre-flight — Validates the request before forwarding
- Agent local check — Agent independently validates (final authority)
A compromised controller cannot override agent policy.
Policy YAML
spec:
commands:
allow:
- pattern: "^systemctl status .*"
- pattern: "^journalctl.*"
deny:
- pattern: ".*rm.*-rf.*"
filesystem:
allow:
- path: /opt/app
- path: /var/log
deny:
- path: /etc/shadow
resources:
maxOutputBytes: 10485760
timeoutSeconds: 300
Security Invariants
These invariants are enforced at all times:
- No command executes without policy check
- No connection accepted without completed Noise handshake
- Audit log is append-only (no delete/truncate)
- Private keys zeroed from memory on drop
- OTP tokens are single-use, hash-stored, TTL-enforced
- Symlink resolution before path policy checks
- Output size bounded (prevent memory exhaustion)
- Execution timeout enforced (prevent hanging)
- No shell injection — commands policy-checked
- Relay never decrypts payload (E2E between agent and client)
Capability Tokens
RavenFabric supports Biscuit-inspired capability tokens:
- Self-contained — Carry their own signed permissions
- Delegatable — Agent A can grant Agent B limited capabilities
- Attenuatable — Capabilities can be narrowed, never widened
- Offline-verifiable — No central authority needed at execution time
Post-Quantum Resistance
Hybrid key exchange (ML-KEM + X25519) protects against harvest-now-decrypt-later attacks. The post-quantum layer is additive — classical security is never weakened.
Wire Protocol
RavenFabric uses a custom binary wire protocol optimized for security and efficiency.
Frame Format
┌──────────────┬─────────┬──────────────────────────────────┐
│ Magic (4B) │ Ver (1B)│ Payload │
│ R V N F │ 0x01 │ [Noise handshake / frames] │
└──────────────┴─────────┴──────────────────────────────────┘
Magic Bytes
Every connection starts with RVNF (0x52 0x56 0x4E 0x46) followed by version byte (currently 0x01). Invalid magic causes immediate disconnection.
Handshake Phase
After magic + version validation, the Noise XX handshake begins:
Noise_XX_25519_ChaChaPoly_BLAKE2s
Message 1: Initiator → Responder: e
Message 2: Responder → Initiator: e, ee, s, es
Message 3: Initiator → Responder: s, se
Encrypted Frame Format
After handshake completion, all data is encrypted:
┌────────────────┬──────────────────────────────────────┐
│ Length (4B BE) │ Ciphertext + MAC (16B) │
└────────────────┴──────────────────────────────────────┘
- Length: 4 bytes, big-endian, total ciphertext length including MAC
- Ciphertext: ChaCha20-Poly1305 encrypted payload
- MAC: 16-byte Poly1305 authentication tag
Multiplexing
Yamux multiplexing runs over the SecureChannel, allowing concurrent RPC streams:
SecureChannel
└── yamux
├── Stream 0: RPC requests/responses
├── Stream 1: stdout streaming
├── Stream 2: stderr streaming
└── Stream N: additional streams
RPC Encoding
RPC messages are encoded with msgpack (MessagePack):
#![allow(unused)] fn main() { enum Request { Exec { command: String }, FileRead { path: String }, FileWrite { path: String, data: Vec<u8> }, Status, Heartbeat, } enum Response { ExecResult { exit_code: i32, stdout: Vec<u8>, stderr: Vec<u8> }, FileData { data: Vec<u8> }, FileWritten { bytes: u64 }, StatusInfo { ... }, Pong, Error { message: String }, } }
Transport Independence
The wire protocol is transport-agnostic. The same framing works over:
- WebSocket (primary)
- QUIC
- TCP
- Unix sockets
- Memory channels (testing)
- Any
AsyncRead + AsyncWriteimplementation
Transport Layer
The transport layer provides network-agnostic connectivity. Any channel that can move bytes is a valid transport.
Driver Trait
All transports implement the Driver trait:
#![allow(unused)] fn main() { #[async_trait] pub trait Driver: Send + Sync { async fn connect(&self, addr: &str) -> Result<Connection>; async fn listen(&self, addr: &str) -> Result<Listener>; } }
Built-in Transports
WebSocket (Primary)
Default transport for relay connections. Works through firewalls, proxies, and CDNs.
[transport]
driver = "websocket"
QUIC
UDP-based transport with built-in encryption. Faster connection establishment.
[transport]
driver = "quic"
Memory
In-process transport for testing. Uses tokio::io::duplex.
WireGuard
Userspace WireGuard for direct peer-to-peer connections on open networks.
Exotic Transports
RavenFabric supports steganographic and physical transports for censorship resistance and air-gapped environments:
Steganographic
- DNS tunneling
- ICMP tunneling
- Domain fronting (via CDN)
- MASQUE (HTTP/3 proxy)
- Protocol mimicry (looks like normal HTTPS/SSH)
- Tor hidden service
- Encrypted Client Hello (ECH)
Physical
- Serial port (RS-232/USB)
- Bluetooth/BLE
- Wi-Fi Direct
- LoRa/Meshtastic (10+ km range)
- AX.25 packet radio
- Audio modem (data over sound)
- QR-stream (animated QR codes)
- Satellite (Iridium/Starlink)
- Physical media (USB/SD card, NNCP-style)
Overlay Networks
- Reticulum Network Stack
- Yggdrasil (self-configuring IPv6 mesh)
- I2P (garlic routing)
- Veilid (DHT-based, onion-routed)
- Mixnet (Nym/Loopix)
Connection Management
The ConnectionManager handles:
- Happy Eyeballs (parallel connection attempts)
- Automatic reconnection with exponential backoff
- Multipath scheduling (round-robin, latency-weighted)
- Transport migration on tampering detection
- Proxy detection and CONNECT tunneling
NAT Traversal
ICE-style hole punching with:
- STUN binding discovery
- Candidate gathering (host, server-reflexive, relay)
- Birthday paradox port prediction for symmetric NAT
- TURN relay fallback
Policy Engine
The policy engine is the security core of RavenFabric. It enforces deny-by-default access control at every level.
Deny-by-Default
If a policy does not explicitly allow an action, it is denied. There is no implicit "allow all" — every capability must be granted.
Policy YAML Format
spec:
commands:
allow:
- pattern: "^systemctl status .*"
- pattern: "^journalctl.*"
- pattern: "^cat /var/log/.*"
deny:
- pattern: ".*rm.*-rf.*"
- pattern: ".*shutdown.*"
filesystem:
allow:
- path: /opt/app
- path: /var/log
deny:
- path: /etc/shadow
- path: /root
resources:
maxOutputBytes: 10485760 # 10 MB
timeoutSeconds: 300 # 5 minutes
RBAC
Role-Based Access Control with five built-in roles:
| Role | Permissions |
|---|---|
| Admin | Full access (execute, read, write, view status, manage policy) |
| Operator | Execute commands, read/write files, view status |
| Viewer | View status only |
| Auditor | View status, read audit logs |
| Custom | User-defined permission set |
Capability Tokens
Biscuit-inspired capability tokens for fine-grained, delegatable authorization:
- Authority block defines maximum scope
- Attenuation blocks narrow (never widen) permissions
- Caveats enforce constraints (time windows, source IPs)
- Offline verification — no central authority needed
Distributed Policy
For disconnected or mesh deployments:
- CRDT convergence — Policies converge without a master
- Append-only logs — Signed hash chain (Scuttlebutt-inspired)
- Content-addressed — Request policy by hash, any node can serve
- Quorum verification — Multi-signer approval
- Conflict resolution — Most-restrictive policy wins by default
Telemetry Governance
Policy controls what telemetry data is collected and exported:
telemetry:
allow:
- type: system_metrics
- type: app_metrics
deny:
- type: process_metrics
exporter:
type: otlp
endpoint: "https://otel.example.com:4317"
Remote Execution
RavenFabric supports multiple execution modes, from fire-and-forget to fully orchestrated playbooks.
Fire and Forget
Execute a command without waiting for results:
rf exec --mode fire-and-forget --target web-01 "touch /tmp/heartbeat"
Standard Execution
Execute and wait for results:
rf exec --target web-01 "systemctl status nginx"
Output is streamed in real-time via yamux multiplexing.
Multi-Agent Execution
Execute across multiple agents:
rf exec --target "web-*" "apt update && apt upgrade -y"
Interactive Shell
Open an interactive PTY session:
rf shell --target web-01 --cols 120 --rows 40
Execution Modes
| Mode | Description |
|---|---|
fire-and-forget | No response expected |
fire-and-verify | Verify exit code only |
streaming | Real-time stdout/stderr |
orchestrated | Multi-step with rollback |
Resource Limits
Every execution is bounded by policy:
- Output size: Maximum bytes of stdout/stderr (default: 10 MB)
- Timeout: Maximum execution time (default: 300s)
- Concurrent: Maximum parallel executions per agent
Security
- Every command is checked against the policy engine before execution
- Commands run via
sh -cwith the full command string policy-checked - Symlinks are resolved before path checks (prevents traversal)
- Output is bounded to prevent memory exhaustion
- Agent is always the final authority — controller cannot override
Policy Configuration
Creating a Policy File
Create a YAML file defining what actions are allowed:
# /etc/ravenfabric/policy.yaml
spec:
commands:
allow:
- pattern: "^systemctl (status|restart) nginx"
- pattern: "^journalctl -u nginx"
- pattern: "^cat /var/log/nginx/.*"
deny:
- pattern: ".*rm.*-rf.*"
- pattern: ".*>(>)?\\s*/dev/.*"
filesystem:
allow:
- path: /opt/app
- path: /var/log/nginx
deny:
- path: /etc/shadow
- path: /root
- path: /proc/kcore
resources:
maxOutputBytes: 10485760
timeoutSeconds: 300
Pattern Syntax
Command patterns use Rust regex syntax:
| Pattern | Matches |
|---|---|
^systemctl status .* | systemctl status nginx, systemctl status ssh |
^cat /var/log/.* | cat /var/log/syslog, cat /var/log/nginx/access.log |
.*rm.*-rf.* | Any command containing rm and -rf |
Deny Takes Precedence
If both allow and deny match a command, deny wins:
commands:
allow:
- pattern: ".*" # Allow everything
deny:
- pattern: ".*rm.*" # Except rm (this wins)
Filesystem Policies
Path-based access control with symlink resolution:
filesystem:
allow:
- path: /opt/app # Allow read/write under /opt/app
deny:
- path: /opt/app/secrets # Deny the secrets subdirectory
Symlinks are resolved to their real path before policy checks, preventing traversal attacks.
Resource Limits
resources:
maxOutputBytes: 10485760 # 10 MB max output
timeoutSeconds: 300 # 5 minute timeout
These limits are enforced by the executor and cannot be overridden by the client.
Hot Reload
Send SIGHUP to the agent process to reload policy without restarting:
kill -HUP $(pidof rf-agent)
Agent Enrollment
RavenFabric uses a one-time password (OTP) enrollment flow. No certificate authority. No centralized key server.
Enrollment Flow
Admin Agent TrustStore
│ │ │
│─── generate OTP ─────────┼──────────────────────► │
│ (returns token) │ │
│ │ │
│─── give token to agent ─►│ │
│ │ │
│ │── enroll(token) ────────►│
│ │ (generates keypair) │
│ │ │
│ │◄── enrolled(pubkey) ─────│
│ │ │
Generate an Enrollment Token
# Generate a one-time token (valid for 5 minutes)
rf admin enroll --ttl 300 --agent-id web-01
# Output: otp_abc123def456
Enroll an Agent
rf-agent --enroll \
--token otp_abc123def456 \
--relay wss://relay.example.com/meet \
--key-path /etc/ravenfabric/agent.key
The agent:
- Generates a new Ed25519 key pair locally
- Sends the public key to the relay with the OTP token
- TrustStore validates the OTP (single-use, hash-stored, TTL-enforced)
- On success, the agent is registered and can receive RPC calls
Security Properties
- OTP tokens are single-use — Once consumed, the token hash is marked used
- Hash-stored — Only the hash of the OTP is stored, not the plaintext
- TTL-enforced — Tokens expire after a configurable time window
- No secrets in transit — The agent generates its key pair locally
- No certificate authority — Identity is the key pair itself
Relay Setup
The relay is a stateless encrypted broker. It pairs agents with clients and forwards opaque bytes. It never sees plaintext or keys.
Quick Start
rf-relay --listen 0.0.0.0:9090 --secret "your-meet-secret"
Production Setup
Systemd Service
# /etc/systemd/system/rf-relay.service
[Unit]
Description=RavenFabric Relay
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
ExecStart=/usr/local/bin/rf-relay --listen 0.0.0.0:9090
Environment=RELAY_SECRET=your-secret-here
Restart=always
RestartSec=5
LimitNOFILE=65535
# Security hardening
NoNewPrivileges=yes
ProtectSystem=strict
ProtectHome=yes
PrivateTmp=yes
[Install]
WantedBy=multi-user.target
Behind Reverse Proxy
The relay uses WebSocket, so configure your reverse proxy accordingly:
Nginx:
server {
listen 443 ssl;
server_name relay.example.com;
location /meet {
proxy_pass http://127.0.0.1:9090;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 86400;
}
}
Geo-Distribution
Deploy multiple relays for latency optimization:
relay-eu.example.com (Frankfurt)
relay-us.example.com (Virginia)
relay-ap.example.com (Singapore)
Agents connect to the nearest relay. The relay selection is automatic based on latency probing.
Security Properties
- Stateless — No session data stored on disk
- Opaque — Relay sees only encrypted bytes, never plaintext
- No keys — Relay has no access to agent or client keys
- Ephemeral — Can be restarted/replaced without state loss
CLI Reference
rf exec
Execute a command on a remote agent.
rf exec [OPTIONS] <COMMAND>
Options
| Option | Description | Default |
|---|---|---|
--relay <URL> | Relay WebSocket URL | Required |
--token <TOKEN> | Authentication token | Required |
--target <ID> | Target agent ID or pattern | Required |
--timeout <SECS> | Execution timeout | 30 |
--mode <MODE> | Execution mode (see below) | streaming |
Modes
fire-and-forget— No responsefire-and-verify— Exit code onlystreaming— Real-time outputorchestrated— Multi-step
Examples
# Simple command
rf exec --relay wss://relay.example.com/meet \
--token abc123 "hostname"
# Multi-agent
rf exec --target "web-*" "systemctl status nginx"
# With timeout
rf exec --timeout 60 "apt update"
rf shell
Open an interactive shell session.
rf shell [OPTIONS]
Options
| Option | Description | Default |
|---|---|---|
--relay <URL> | Relay WebSocket URL | Required |
--token <TOKEN> | Authentication token | Required |
--target <ID> | Target agent ID | Required |
--cols <N> | Terminal columns | 80 |
--rows <N> | Terminal rows | 24 |
rf status
Query agent status.
rf status [OPTIONS]
rf completions
Generate shell completions.
rf completions <SHELL>
Supported shells: bash, zsh, fish, powershell, elvish.
# Generate and install zsh completions
rf completions zsh > ~/.zfunc/_rf
rf dev
Start a local development environment (relay + agent in one process).
rf dev [OPTIONS]
| Option | Description | Default |
|---|---|---|
--port <PORT> | Local relay port | 9090 |
--policy <PATH> | Policy file | permissive default |
Configuration File Reference
RavenFabric uses TOML configuration files.
Agent (/etc/ravenfabric/raven.toml)
[agent]
# Unique agent identifier
id = "web-01"
# Relay WebSocket URL
relay = "wss://relay.example.com/meet"
# Path to agent private key
key_path = "/etc/ravenfabric/agent.key"
# Path to policy file
policy_path = "/etc/ravenfabric/policy.yaml"
# Path to audit log (JSON lines)
audit_path = "/var/log/ravenfabric/audit.jsonl"
[transport]
# Transport driver: "websocket", "quic", "memory"
driver = "websocket"
# Reconnect interval (seconds)
reconnect_interval = 5
# Maximum reconnect attempts (0 = infinite)
max_retries = 0
[resources]
# Maximum output size per command (bytes)
max_output_bytes = 10485760 # 10 MB
# Maximum execution time per command (seconds)
timeout_seconds = 300
# Maximum concurrent executions
max_concurrent = 10
Relay
[relay]
# Listen address
listen = "0.0.0.0:9090"
# Meet secret (use env: prefix for environment variables)
meet_secret = "env:RELAY_SECRET"
[rate_limit]
# Requests per second per source IP
requests_per_second = 100
# Burst allowance
burst = 200
CLI (~/.config/ravenfabric/config.toml)
[cli]
# Default relay URL
default_relay = "wss://relay.example.com/meet"
# CLI key path
key_path = "~/.config/ravenfabric/cli.key"
# Default timeout (seconds)
timeout = 30
Environment Variables
All configuration values can be overridden via environment variables:
| Variable | Overrides |
|---|---|
RF_AGENT_ID | agent.id |
RF_RELAY | agent.relay |
RF_KEY_PATH | agent.key_path |
RF_POLICY | agent.policy_path |
RELAY_SECRET | relay.meet_secret (with env: prefix) |
RUST_LOG | Logging level |
Policy YAML Reference
Full Schema
spec:
# Command execution policies
commands:
allow:
- pattern: "<regex>" # Rust regex syntax
deny:
- pattern: "<regex>" # Deny takes precedence over allow
# Filesystem access policies
filesystem:
allow:
- path: "<absolute-path>"
deny:
- path: "<absolute-path>"
# Resource limits
resources:
maxOutputBytes: 10485760 # Max stdout+stderr (bytes)
timeoutSeconds: 300 # Max execution time (seconds)
Rules
- Deny-by-default: If no
allowrule matches, the action is denied - Deny wins: If both
allowanddenymatch, deny takes precedence - Regex matching: Command patterns use Rust regex syntax
- Path resolution: Symlinks are resolved before policy checks
- Immutable denies: Some deny rules cannot be overridden (e.g.,
/etc/shadow)
Examples
Web Server Administration
spec:
commands:
allow:
- pattern: "^systemctl (status|restart|reload) nginx"
- pattern: "^journalctl -u nginx.*"
- pattern: "^cat /var/log/nginx/.*"
- pattern: "^nginx -t"
deny:
- pattern: ".*rm.*-rf.*"
filesystem:
allow:
- path: /etc/nginx
- path: /var/log/nginx
deny:
- path: /etc/nginx/ssl
resources:
maxOutputBytes: 5242880
timeoutSeconds: 60
Read-Only Monitoring
spec:
commands:
allow:
- pattern: "^systemctl status .*"
- pattern: "^df -h"
- pattern: "^free -m"
- pattern: "^uptime"
- pattern: "^cat /proc/(meminfo|cpuinfo|loadavg)"
filesystem:
allow:
- path: /var/log
deny:
- path: /var/log/audit
resources:
maxOutputBytes: 1048576
timeoutSeconds: 30
Deny Everything (Lockdown)
spec:
commands:
allow: []
deny:
- pattern: ".*"
filesystem:
allow: []
deny:
- path: /
resources:
maxOutputBytes: 0
timeoutSeconds: 0
RPC Protocol Reference
Overview
RavenFabric RPC uses msgpack encoding over yamux-multiplexed Noise XX secure channels.
Request Types
| Action | Description | Fields |
|---|---|---|
Exec | Execute a command | command: String |
FileRead | Read a file | path: String |
FileWrite | Write a file | path: String, data: Vec<u8> |
Status | Query agent status | — |
Heartbeat | Keep-alive ping | — |
Shell | Open PTY session | cols: u16, rows: u16 |
Response Types
| Type | Description | Fields |
|---|---|---|
ExecResult | Command result | exit_code: i32, stdout: Vec<u8>, stderr: Vec<u8> |
FileData | File contents | data: Vec<u8> |
FileWritten | Write confirmation | bytes: u64 |
StatusInfo | Agent status | id: String, uptime: u64, ... |
Pong | Heartbeat response | — |
Error | Error response | message: String |
Message Format
Request {
id: u64, // Unique request ID
action: Action, // Enum variant
}
Response {
id: u64, // Matching request ID
result: Result, // Enum variant
}
Encoding
All messages use msgpack (MessagePack) via rmp-serde:
#![allow(unused)] fn main() { // Serialize let bytes = rmp_serde::to_vec(&request)?; // Deserialize let response: Response = rmp_serde::from_slice(&bytes)?; }
Streaming
For long-running commands, stdout/stderr are streamed over separate yamux streams. The client receives output in real-time without waiting for command completion.
DTN (Delay-Tolerant Networking)
For air-gapped or high-latency environments, requests can be queued as DTN bundles with:
- Priority levels (Low, Normal, High, Critical)
- TTL-based expiration
- Custody transfer (reliable delivery)
- Idempotency keys (deduplication)
Building from Source
Prerequisites
- Rust 1.85+ (Edition 2024)
- Git
# Install Rust
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
# Verify version
rustc --version # Must be 1.85.0 or later
Clone and Build
git clone https://github.com/egkristi/RavenFabric.git
cd RavenFabric
# Debug build
cargo build
# Release build (optimized, LTO, stripped)
cargo build --release
Cross-Compilation
Linux Static Binary (musl)
rustup target add x86_64-unknown-linux-musl
cargo build --release --target x86_64-unknown-linux-musl
Linux ARM64
rustup target add aarch64-unknown-linux-musl
cargo build --release --target aarch64-unknown-linux-musl
Linux ARMv7 (Raspberry Pi)
rustup target add armv7-unknown-linux-musleabihf
cargo build --release --target armv7-unknown-linux-musleabihf
Feature Flags
| Feature | Default | Description |
|---|---|---|
full | Yes | All features enabled |
minimal | No | No TUN, no sysinfo, no QUIC |
websocket | Yes | WebSocket transport |
quic | No | QUIC transport |
# Minimal build (smaller binary)
cargo build --release --no-default-features --features minimal
# With QUIC
cargo build --release --features quic
Verify Build
# Run all tests
cargo test
# Lint
cargo clippy
# Format check
cargo fmt --check
Testing
Running Tests
# Run all tests
cargo test
# Run tests for a specific crate
cargo test -p rf-crypto
cargo test -p rf-transport
cargo test -p rf-policy
# Run a specific test
cargo test -p rf-policy test_capability_check
# Run with output
cargo test -- --nocapture
Test Coverage
| Crate | Tests | Coverage Focus |
|---|---|---|
rf-crypto | 25 | Noise handshake, key management, resumption, PQ KEM |
rf-transport | 121 | NAT traversal, mesh, proxy, gossip, overlays, WireGuard, platforms |
rf-rpc | 61 | Codec roundtrips, DTN queuing, routing, SOCKS5, controller |
rf-policy | 31 | RBAC, capabilities, deny-by-default, distributed policy |
rf-executor | 48 | Command execution, streaming, PTY, log tailing, plugins |
rf-bootstrap | 11 | OTP enrollment, TrustStore |
| Integration | 2 | End-to-end relay + agent + client |
Total: 336 tests
Writing Tests
Unit Tests
Place unit tests in the same file as the code:
#![allow(unused)] fn main() { #[cfg(test)] mod tests { use super::*; #[test] fn test_deny_by_default() { let policy = Policy::empty(); assert!(policy.check("rm -rf /").is_denied()); } } }
Async Tests
Use #[tokio::test] for async tests:
#![allow(unused)] fn main() { #[tokio::test] async fn test_secure_channel() { let (client, server) = tokio::io::duplex(8192); // ... test over simulated connection } }
Security Tests
Security-critical paths need both positive AND negative tests:
#![allow(unused)] fn main() { #[test] fn test_policy_allows_valid_command() { let policy = load_policy("allow systemctl status"); assert!(policy.check("systemctl status nginx").is_allowed()); } #[test] fn test_policy_denies_invalid_command() { let policy = load_policy("allow systemctl status"); assert!(policy.check("rm -rf /").is_denied()); } }
CI
Tests run automatically on every push via GitHub Actions:
- All platforms: Linux, macOS, Windows
cargo test,cargo clippy,cargo fmt --check
Contributing
Thank you for your interest in contributing to RavenFabric.
Getting Started
- Fork the repository
- Clone your fork:
git clone https://github.com/YOUR_USER/RavenFabric.git - Create a feature branch:
git checkout -b feat/my-feature - Make your changes
- Run tests:
cargo test - Run linter:
cargo clippy - Format code:
cargo fmt - Push and open a Pull Request
Code Standards
- Language: All code, comments, and documentation in English
- Edition: Rust 2024, MSRV 1.85
- Error handling:
thiserrorin libraries,anyhowonly in binaries - Async: Use
async-traitfor async trait methods - Logging:
tracingcrate only — neverprintln!in libraries - Tests: Every public function must have at least one test
- Security: Every policy check, key validation, and crypto operation must have tests
Commit Messages
Use conventional commits:
feat: add QUIC transport driver
fix: resolve symlink before path policy check
refactor: extract connection manager from driver
docs: add relay setup guide
test: add roundtrip test for DTN bundle
Reference issues: feat: add QUIC transport #5
Security
- Security is always the top priority
- Never trade security for convenience
- No
unwrap()in library code — use?and proper error types - All types must be
Send + Sync - Deny-by-default — if unsure, deny
Reporting Security Issues
See SECURITY.md for responsible disclosure guidelines.