← Back to homepage

Getting Started

Install Ferrosa and run a single-node instance in minutes. Everything you need to connect existing CQL drivers and start querying.

Beta: Ferrosa is currently in beta. APIs and configuration may change between releases. Not yet recommended for production workloads without thorough testing.

On this page

Installation

Coming Soon: Ferrosa is under active development. Binary packages are not yet available for download. Check back for release announcements.

Run Ferrosa

Minimal startup

# Start with defaults — CQL on :9042, web console on :9090
./target/release/ferrosa

With graph engine enabled

FERROSA_GRAPH_ENABLED=true ./target/release/ferrosa

Custom ports and auth disabled (development)

FERROSA_CQL_BIND=127.0.0.1:9042 \
FERROSA_WEB_BIND=127.0.0.1:9090 \
FERROSA_AUTH_DISABLED=true \
FERROSA_GRAPH_ENABLED=true \
./target/release/ferrosa
Note: Ferrosa starts in standalone mode by default. Two-node pair mode is available via FERROSA_SEED configuration. Full cluster formation (3+ nodes) uses Raft consensus and a Murmur3 token ring; there is no separate FERROSA_CLUSTER_MODE switch.

Default ports

ServicePortProtocol
CQL Server9042CQL native protocol v5
Web Console9090HTTP (REST API + static UI + WebSocket)
Graph HTTP7474HTTP/JSON (Cypher queries)
Prometheus Metrics9090/metricsHTTP (text exposition)
Internode (cluster)7000Custom binary protocol

Connect a CQL Driver

Ferrosa speaks CQL protocol v5 — the same wire protocol as Apache Cassandra. Any standard CQL driver works unchanged.

Python

from cassandra.cluster import Cluster

cluster = Cluster(['127.0.0.1'])
session = cluster.connect()

# Create a keyspace
session.execute("""
    CREATE KEYSPACE demo WITH replication = {
        'class': 'SimpleStrategy',
        'replication_factor': 1
    }
""")

# Create a table
session.execute("""
    CREATE TABLE demo.users (
        user_id uuid PRIMARY KEY,
        name text,
        email text
    )
""")

# Insert and query
session.execute("""
    INSERT INTO demo.users (user_id, name, email)
    VALUES (uuid(), 'Alice', 'alice@example.com')
""")

rows = session.execute("SELECT * FROM demo.users")
for row in rows:
    print(row.name, row.email)

Java

// Same DataStax Java driver — just change the contact point
CqlSession session = CqlSession.builder()
    .addContactPoint(new InetSocketAddress("127.0.0.1", 9042))
    .withLocalDatacenter("datacenter1")
    .build();

ResultSet rs = session.execute("SELECT * FROM demo.users");

Go

cluster := gocql.NewCluster("127.0.0.1")
session, _ := cluster.CreateSession()
defer session.Close()

var name, email string
session.Query("SELECT name, email FROM demo.users LIMIT 1").Scan(&name, &email)

cqlsh

# Standard cqlsh connects directly
cqlsh 127.0.0.1 9042

Client bootstrap sequence

There is no extra Ferrosa-only client bootstrap ritual beyond normal Cassandra-compatible CQL behavior. A standard driver connects like this:

  1. Open a TCP connection to one configured contact point.
  2. Optionally send OPTIONS and read SUPPORTED.
  3. Send STARTUP, including compression preferences if the driver uses them.
  4. If auth is enabled, complete the standard SASL flow: AUTHENTICATEAUTH_RESPONSEAUTH_SUCCESS. If auth is disabled, the server replies with READY.
  5. After READY or AUTH_SUCCESS, the driver introspects system.local, system.peers, and system.peers_v2 to learn tokens, schema version, and peer CQL endpoints.
  6. The driver then opens or reroutes pooled connections to the advertised peer endpoints from those system tables.

In other words, topology discovery happens after the normal CQL handshake. If a client authenticates successfully and then stalls while dialing bad peer addresses, that is a topology advertisement problem, not a missing handshake step.

Important: An outside client does not send a special OPTIONS payload, startup flag, or Ferrosa-only command to request public addresses. External versus internal peer addresses are chosen automatically from the server side based on the source IP of the connection that is querying system.local / system.peers / system.peers_v2.

Transactions

Ferrosa supports strict serializable distributed transactions via the Accord protocol. Lightweight transactions (LWT) work with any CQL driver:

# Conditional insert — only succeeds if the row doesn't exist
session.execute("""
    INSERT INTO demo.users (user_id, name, email)
    VALUES (uuid(), 'Bob', 'bob@example.com')
    IF NOT EXISTS
""")

# Compare-and-set update
session.execute("""
    UPDATE demo.users SET email = 'newemail@example.com'
    WHERE user_id = ?
    IF email = 'bob@example.com'
""", [user_id])

# Multi-partition transaction
session.execute("""
    BEGIN TRANSACTION
      UPDATE accounts SET balance = balance - 100
        WHERE id = 'acct-1' IF balance >= 100;
      UPDATE accounts SET balance = balance + 100
        WHERE id = 'acct-2';
    COMMIT TRANSACTION
""")

Configuration Reference

Ferrosa is configured via environment variables and an optional TOML file. Environment variables override file values. The tables below cover the customer-facing runtime variables currently honored by the server.

Startup, Auth, and CQL

VariableDefaultDescription
FERROSA_CONFIG/etc/ferrosa/ferrosa.tomlOptional TOML config file loaded at startup
FERROSA_MODEdevelopmentDeployment mode used for production-safety checks
FERROSA_HOST_IDauto-generatedStable node UUID persisted under the data directory
FERROSA_CQL_BIND0.0.0.0:9042CQL server bind address
FERROSA_CQL_BROADCASTbind address/portPublic CQL address advertised to clients via system.local / system.peers_v2
FERROSA_CQL_INTERNAL_CLIENT_CIDRSComma-separated CIDRs whose clients should receive the internal topology view instead of the public CQL broadcast
FERROSA_CQL_MAX_CONNECTIONS1024Maximum concurrent CQL connections
FERROSA_CQL_MAX_CONNECTIONS_PER_IP64Per-client-IP connection cap
FERROSA_CQL_TLS_CERTPEM certificate path for CQL TLS
FERROSA_CQL_TLS_KEYPEM private-key path for CQL TLS
FERROSA_CQL_REQUIRE_TLSfalseFail startup if the CQL listener does not have TLS configured
FERROSA_WEB_BIND0.0.0.0:9090Web console bind address
FERROSA_AUTH_ENABLEDfalseEnable CQL role auth and permission enforcement
FERROSA_AUTH_WARNfalseSoak mode: log auth failures as warnings while still allowing requests
FERROSA_AUTH_DISABLEDfalseDevelopment escape hatch that skips the CQL SASL handshake entirely
FERROSA_SUPERUSER_PASSWORDOptional override for the seeded ferrosa_admin password during bootstrap
FERROSA_GRAPH_ENABLEDfalseEnable graph engine + HTTP endpoint
FERROSA_BOLT_PORT7687Bolt listener port when the graph engine is enabled
FERROSA_SPARQL_ENABLEDfalseEnable the SPARQL HTTP endpoint
FERROSA_SPARQL_BIND0.0.0.0:8080SPARQL HTTP listen address
RUST_LOGinfoTracing filter (e.g. debug, ferrosa_cql=trace)

Cluster, Internode, and Consensus

VariableDefaultDescription
FERROSA_CLUSTER_NAMEferrosaCluster name (must match across nodes)
FERROSA_SEEDComma-separated seed addresses; each entry may be an IP:port or resolvable hostname:port
FERROSA_INTERNODE_BIND0.0.0.0:7000Internode protocol bind address
FERROSA_INTERNODE_BROADCAST127.0.0.1:7000Address other nodes use to reach this node; also forms the default internal topology view. May be an IP:port or resolvable hostname:port.
FERROSA_INTERNODE_PSKPre-shared key for internode auth (HMAC-SHA256)
FERROSA_INTERNODE_TLS_CERTPEM certificate path for internode TLS
FERROSA_INTERNODE_TLS_KEYPEM private-key path for internode TLS
FERROSA_INTERNODE_TLS_CACA certificate for mutual-TLS internode verification
FERROSA_INTERNODE_REQUIRE_TLSfalseFail startup if internode TLS is required but not configured
FERROSA_HEARTBEAT_INTERVAL_MS500Heartbeat interval between peers
FERROSA_HEARTBEAT_TIMEOUT_MS1500Timeout before marking a peer as suspected down
FERROSA_MAX_INTERNODE_CONNECTIONS512Maximum concurrent internode connections
FERROSA_HANDSHAKE_TIMEOUT_SECS5Internode handshake timeout
FERROSA_MAX_FRAME_BODY_SIZE268435456Maximum internode frame size in bytes
FERROSA_MAX_STREAMS_PER_LANE128Maximum concurrent streams per internode connection lane
FERROSA_DATA_CENTERdatacenter1Data center name
FERROSA_RACKrack1Rack name within the data center
FERROSA_NUM_TOKENS256Number of tokens on the ring
FERROSA_DEFAULT_CLQUORUMDefault consistency level
FERROSA_AUTO_JOINtrueAutomatically join the ring on startup (dev mode)
FERROSA_HINTED_HANDOFF_DIRdata/hintsLocal directory for hinted handoff files
FERROSA_HINTED_HANDOFF_MAX_MB1024Max disk space for hinted handoff per peer (MB)
FERROSA_FORMATION_TIMEOUT_SECS60Maximum time to wait in forming state before falling back
FERROSA_NODE_ROLEbothNode role: data, indexer, or both
FERROSA_RAFT_HEARTBEAT_MS300Raft heartbeat interval
FERROSA_RAFT_ELECTION_MIN_MS3000Lower bound for Raft election timeout
FERROSA_RAFT_ELECTION_MAX_MS6000Upper bound for Raft election timeout
FERROSA_CLOCK_MAX_SKEW_SECS5Maximum tolerated clock skew for Accord timestamp validation

Storage and Durability

VariableDefaultDescription
FERROSA_DATA_DIR/var/lib/ferrosaLocal data directory for SSTables and commit log
FERROSA_CACHE_MAX_BYTES10737418240Local LRU cache size (10 GB)
FERROSA_FLUSH_THRESHOLD_BYTES67108864Memtable flush threshold (64 MB)
FERROSA_FLUSH_MAX_AGE_SECS30Flush memtables after this age even if the size threshold is not reached
FERROSA_FLUSH_INTERVAL_SECS30Background maintenance interval for flush and schema sync
FERROSA_WRITE_VERIFYtrueVerify committed writes during the storage pipeline
FERROSA_S3_ENDPOINTS3-compatible endpoint URL
FERROSA_S3_BUCKETS3 bucket for durable storage
FERROSA_S3_REGIONus-east-1AWS region for S3
FERROSA_S3_ACCESS_KEY_IDAccess key (falls back to instance profile)
FERROSA_S3_SECRET_ACCESS_KEYSecret access key
FERROSA_S3_ALLOW_HTTPfalseAllow non-TLS for local dev (MinIO)
FERROSA_S3_PREFIXKey prefix for multi-tenant separation
FERROSA_S3_UPLOAD_QUEUE_DEPTH16Upload backpressure queue depth
FERROSA_ARCHIVE_ENABLEDfalseEnable commit-log archiving
FERROSA_ARCHIVE_POLL_INTERVAL_SECS5Archive poll interval in seconds
FERROSA_ARCHIVE_RETENTION_DAYS7Retention period for archived commit-log segments

Secondary Index and Search Backends

VariableDefaultDescription
FERROSA_INDEX_BACKENDlocalSecondary-index backend: local, remote, or off
FERROSA_INDEX_SIDECAR_ENDPOINTSComma-separated remote index sidecar URLs when FERROSA_INDEX_BACKEND=remote
FERROSA_INDEX_SIDECAR_TIMEOUT_MS30000Per-request timeout for the remote index sidecar
FERROSA_INDEX_SIDECAR_MAX_RETRIES2Retries per sidecar endpoint
FERROSA_INDEX_CB_THRESHOLD5Circuit-breaker failure threshold for remote indexing
FERROSA_INDEX_CB_RECOVERY_MS60000Circuit-breaker recovery interval in milliseconds

Telemetry and Debug Endpoints

VariableDefaultDescription
FERROSA_TELEMETRY_ENABLEDfalseEnable sampled tracing telemetry in the observability stack
FERROSA_TELEMETRY_SAMPLE_RATE0.01Fraction of spans to sample when telemetry is enabled
FERROSA_DEBUG_AUTH_TOKENBearer token protecting the debug endpoints
FERROSA_DEBUG_IP_WHITELISTComma-separated IP allowlist for debug endpoints

Compaction (STCS — default)

Size-Tiered Compaction is the default strategy when no per-table configuration is specified.

VariableDefaultDescription
FERROSA_COMPACTION_MIN_THRESHOLD4Min SSTables to trigger compaction
FERROSA_COMPACTION_MAX_THRESHOLD32Max SSTables per compaction task
FERROSA_COMPACTION_BUCKET_LOW0.5Lower size ratio for bucket membership
FERROSA_COMPACTION_BUCKET_HIGH1.5Upper size ratio for bucket membership

Compaction (UCS — per-table)

Unified Compaction Strategy (Cassandra 5.0) uses density-based levels with a configurable fan factor. Configure per-table via DDL:

CREATE TABLE ks.events (id uuid PRIMARY KEY, data text)
  WITH compaction = {
    'class': 'UnifiedCompactionStrategy',
    'fan_factor': '4'
  };
ParameterDefaultDescription
fan_factor4SSTables per level before compaction triggers (W=2 aggressive, W=32 lazy)
min_sstable_size104857600Minimum SSTable size (bytes) for density calculation

CLI Tools

ferrosa-ctl

Admin CLI for querying and monitoring a running Ferrosa node.

# Run a CQL query
ferrosa-ctl query "SELECT * FROM demo.users"

# Describe schema
ferrosa-ctl describe

# Show Prometheus metrics
ferrosa-ctl metrics

# Node health status
ferrosa-ctl status

# Active CQL connections
ferrosa-ctl connections --sort client_addr

# Currently running queries (long-running only)
ferrosa-ctl queries --long-running

# Storage engine stats
ferrosa-ctl storage

# Live TUI dashboard (ratatui)
ferrosa-ctl monitor

# Cluster management
ferrosa-ctl add-node 10.0.0.5:7000        # Pre-approve a node for cluster join
ferrosa-ctl decommission 10.0.0.3:7000   # Gracefully remove a node
ferrosa-ctl ring                          # Show token ring distribution
ferrosa-ctl rebalance                     # Trigger token rebalancing

The monitor command launches a terminal dashboard with 5 panels showing connections, active queries, storage stats, and more. Navigate with arrow keys, refresh is automatic. Connect to a specific host with --host 10.0.0.1:9042.

Prometheus metrics: The /metrics endpoint is available on the web console port (default 9090) without authentication. Scrape it directly from Prometheus or any compatible monitoring system.

Architecture Overview

Ferrosa is composed of 12 independent Rust crates that can be used together or embedded individually.

ferrosa-common

Shared types: Token, PartitionKey, DecoratedKey, CellValue, Murmur3 partitioner.

ferrosa-sstable

Read/write BTI SSTables. On-disk trie, Bloom filter, LZ4/Zstd compression.

ferrosa-storage

Memtable, commit log with CDC, STCS + UCS compaction, S3 upload manager, NVMe pinning, local LRU cache.

ferrosa-schema

Schema registry with ArcSwap, RBAC auth, column-level permissions, audit logging.

ferrosa-index

Secondary index framework: B-tree, hash, composite, phonetic, vector (HNSW + IVFFlat). Async builds, staleness tracking.

ferrosa-cql

CQL v5 framing, LL(2) parser, prepared cache, LZ4/Snappy compression, SUBSCRIBE.

ferrosa-udf

WebAssembly UDF executor: WIT contract, Wasmtime sandbox, per-function CPU/memory limits. In development.

ferrosa-graph

Cypher parser, logical/physical planner, expand executor, HTTP/JSON endpoint.

ferrosa-net

Internode protocol: 3 priority lanes, PSK handshake, RPC, heartbeat failure detection.

ferrosa-cluster

Pair mode HA, Raft consensus (openraft + sled), token ring, coordinator with tunable CL.

ferrosa-ctl

CLI admin tool: query, describe, metrics, monitor (ratatui TUI dashboard).

ferrosa (binary)

Composes all crates. CQL on :9042, graph on :7474, web console on :9090 (auth + WebSocket).

Operational Notes

S3 Storage Resilience

When S3 is configured as the durable backend, Ferrosa writes asynchronously to S3 via an upload queue. If S3 becomes temporarily unreachable:

Data loss window: If local disk is lost while S3 is unreachable, data written since the last successful S3 upload may be lost. For production deployments, monitor the S3 upload queue depth and alert on sustained failures.

Schema Durability

Ferrosa persists all schema changes (keyspaces, tables, indexes, types, functions) to local disk on every flush and to S3 during periodic sync. When a node restarts — even after a binary upgrade with preserved data — the schema is restored automatically. No manual DDL replay is needed.

Compaction and S3

The storage pipeline handles compaction end-to-end:

  1. Memtable flushes to local SSTable
  2. Compaction strategy (STCS default, or UCS per-table) selects SSTables to merge
  3. Compacted output uploads to S3 with confirmation before manifest update
  4. Manifest uses compare-and-swap to prevent concurrent update loss
  5. Superseded input SSTables are deleted from S3 and local disk

Multi-Node Cluster (Experimental)

Three-node cluster formation uses Raft consensus for metadata and tunable consistency for data. Current limitations:

Beta: Multi-node cluster mode is experimental. For production HA, two-node pair mode with automatic failover is the recommended configuration.

Cluster FAQ

How do I run one cluster for both host clients and in-network clients?

Use two address families:

Clients whose source IP falls inside FERROSA_CQL_INTERNAL_CLIENT_CIDRS receive internal addresses from system.local and system.peers_v2. Other clients receive the public CQL broadcast address.

There is no client-side override for this selection. If an outside client is being handed internal addresses, the fix is to correct Ferrosa's advertised public endpoint or the CIDR classification, not to change the driver's handshake messages.

Example: host Mac clients plus Podman-network clients

FERROSA_CQL_BIND=0.0.0.0:9042
FERROSA_CQL_BROADCAST=127.0.0.1:19042
FERROSA_INTERNODE_BROADCAST=10.89.1.48:7000
FERROSA_CQL_INTERNAL_CLIENT_CIDRS=10.89.0.0/16

In that setup, host-side drivers reconnect to 127.0.0.1:19042 after topology discovery, while clients running on the Podman network see 10.89.x.x:9042.

Can split-horizon DNS replace this configuration?

Not by itself. Cassandra-compatible topology tables expose inet columns, so drivers ultimately consume concrete IP addresses after topology discovery. Split DNS helps only if both environments can converge on the same reachable advertised IP, such as a shared VIP or proxy.

For the full architecture spec, see the the project repository.

What's Next

Examples & Tutorials →

Step-by-step walkthroughs for IoT, analytics, e-commerce, and 10 more use cases — with runnable CQL scripts.

CQL Compatibility →

Full reference of supported CQL statements, types, and functions.

Migration Guide →

Move from Apache Cassandra to Ferrosa without rewriting your application.