Skip to content

Quickstart

Get Felix up and running in under 5 minutes.

Prerequisites

  • Rust: 1.92.0 or later (install rustup)
  • Git: For cloning the repository
  • Optional: Task for convenience commands

Clone and Build

# Clone the repository
git clone https://github.com/gabloe/felix.git
cd felix

# Build the entire workspace in release mode
cargo build --workspace --release

The release build is recommended for performance testing. Development builds have significantly higher overhead.

Start the Broker

Run the Felix broker in a terminal:

cargo run --release -p broker

You should see structured log output:

2026-01-25T10:00:00.000Z INFO felix_broker: Starting Felix broker
2026-01-25T10:00:00.001Z INFO felix_broker: QUIC listening on 0.0.0.0:5000
2026-01-25T10:00:00.001Z INFO felix_broker: Metrics server on 0.0.0.0:8080

The broker is now ready to accept connections!

Default ports:

  • 5000: QUIC data plane (publish, subscribe, cache)
  • 8080: Metrics/health endpoint

Run a Demo

Felix includes several self-contained demos that start an in-process broker and QUIC server. You do not need to run the broker separately for these demos.

cargo run --release -p broker --bin pubsub-demo-simple

This demo:

  1. Creates a client connection to the broker
  2. Subscribes to a test stream
  3. Publishes two messages to that stream
  4. Displays the received events

Sample output:

== Felix QUIC Pub/Sub Demo ==
Step 1/6: booting in-process broker + QUIC server.
Step 2/6: connecting QUIC client.
Step 3/6: opening a subscription stream.
Step 4/6: publishing two messages on the same stream.
Step 5/6: receiving events.
Event on demo-topic: hello
Event on demo-topic: world
Demo complete.

More demos:

cargo run --release -p broker --bin cache-demo
cargo run --release -p broker --bin latency-demo
cargo run --release -p broker --bin pubsub-demo-notifications
cargo run --release -p broker --bin pubsub-demo-orders
cargo run --manifest-path demos/rbac-live/Cargo.toml
cargo run --manifest-path demos/cross_tenant_isolation/Cargo.toml

See the Demos Overview for details on what each demo does and what to expect.

Note: the cross-tenant isolation demo uses a Postgres-backed control plane.

Try the Cache

Run the cache demonstration:

cargo run --release -p broker --bin cache-demo

This benchmarks cache operations (put, get_hit, get_miss) across various payload sizes and measures latency/throughput.

Using the Client SDK

Here's a minimal example of using the Felix Rust client:

Publish and Subscribe

use felix_client::{Client, ClientConfig};
use felix_wire::AckMode;
use std::net::SocketAddr;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    // Configure client
    let quinn = quinn::ClientConfig::with_platform_verifier();
    let config = ClientConfig::optimized_defaults(quinn);
    let addr: SocketAddr = "127.0.0.1:5000".parse()?;

    // Connect to broker
    let client = Client::connect(addr, "localhost", config).await?;
    let publisher = client.publisher().await?;

    // Subscribe to a stream
    let mut subscription = client
        .subscribe("my-tenant", "my-namespace", "my-stream")
        .await?;

    // Spawn a task to receive events
    tokio::spawn(async move {
        while let Some(event) = subscription.next_event().await.unwrap() {
            println!("Received: {:?}", event.payload);
        }
    });

    // Publish messages
    for i in 0..10 {
        let payload = format!("Message {}", i);
        publisher
            .publish(
                "my-tenant",
                "my-namespace",
                "my-stream",
                payload.into_bytes(),
                AckMode::None,
            )
            .await?;
    }

    Ok(())
}

Cache Operations

use bytes::Bytes;
use felix_client::{Client, ClientConfig};
use std::net::SocketAddr;

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let quinn = quinn::ClientConfig::with_platform_verifier();
    let config = ClientConfig::optimized_defaults(quinn);
    let addr: SocketAddr = "127.0.0.1:5000".parse()?;
    let client = Client::connect(addr, "localhost", config).await?;

    // Put a value with 60-second TTL
    client
        .cache_put(
            "my-tenant",
            "my-namespace",
            "users",
            "user:123",
            Bytes::from_static(b"alice"),
            Some(60_000),
        )
        .await?;

    // Get the value
    if let Some(value) = client
        .cache_get("my-tenant", "my-namespace", "users", "user:123")
        .await?
    {
        println!("Cached value: {:?}", value);
    }

    Ok(())
}

API Surface

The exact client API is evolving. Check crates/felix-client/src/ for the current implementation. The examples above represent the intended ergonomics.

Performance Testing

Latency Benchmark

Run the latency demo with various configurations:

# Basic run with defaults
cargo run --release -p broker --bin latency-demo

# Custom configuration
cargo run --release -p broker --bin latency-demo -- \
    --binary \
    --fanout 10 \
    --batch 64 \
    --payload 4096 \
    --total 10000 \
    --warmup 500

Parameters:

  • --binary: Use binary batch format (higher throughput)
  • --fanout N: Number of concurrent subscribers
  • --batch N: Batch size for publishing
  • --payload N: Payload size in bytes
  • --total N: Total messages to send
  • --warmup N: Warmup messages before measurement

Cache Benchmark

cargo run --release -p broker --bin cache-demo

Measures cache operations at various payload sizes with configurable concurrency.

Configuration

Felix can be configured via environment variables or a YAML config file.

Environment Variables

Key performance tuning variables:

# Event delivery (pub/sub)
export FELIX_EVENT_CONN_POOL=8
export FELIX_EVENT_BATCH_MAX_DELAY_US=250

# Cache operations
export FELIX_CACHE_CONN_POOL=8
export FELIX_CACHE_STREAMS_PER_CONN=4

# Publishing
export FELIX_PUBLISH_CHUNK_BYTES=16384

Config File

Create /tmp/felix-config.yml:

quic_bind: "0.0.0.0:5000"
metrics_bind: "0.0.0.0:8080"
event_batch_max_events: 64
event_batch_max_delay_us: 250
cache_conn_recv_window: 268435456

Run with custom config:

FELIX_BROKER_CONFIG=/tmp/felix-config.yml cargo run --release -p broker

See Configuration Reference for all options.

Using Task

If you have Task installed, you can use convenience commands:

# Build
task build

# Run tests
task test

# Format code
task fmt

# Run linter
task lint

# Run demos
task demo:pubsub
task demo:cache
task demo:latency
task demo:notifications
task demo:orders
task demo:rbac-live
task demo:cross-tenant-isolation

See Taskfile.yml in the repository root for all available tasks.

Next Steps

Now that you have Felix running:

Troubleshooting

Port Already in Use

If port 5000 or 8080 is in use:

# Change broker port
export FELIX_QUIC_BIND="0.0.0.0:5001"
export FELIX_METRICS_BIND="0.0.0.0:8081"

cargo run --release -p broker

Build Errors

Ensure you have Rust 1.92.0 or later:

rustc --version
# Should show: rustc 1.92.0 or higher

Update if needed:

rustup update

Connection Refused

Make sure the broker is running and listening:

# Check if broker is running
lsof -i :5000

# Check broker logs for errors

See Troubleshooting Guide for more help.