The open source mosaic underneath.

Ferment builds its own foundation. These are the general-purpose parts of it. The same code we run in production, under MIT.

github.com/lockvoid

~/ferment $ man ferment-family

Pixel-art sound wave in pink and orange, mirrored in its own reflection
NAME
ferment-family — mixing and mastering plugins
DESCRIPTION
A mastering chain written from the maths, that sets itself from what it hears.
Ferment finishes every track and every voice through a plugin chain. To know exactly what that chain does at every sample, we wrote it — plain C++ with no JUCE in it, vendored straight into Ferment on iOS and wrapped for the desktop, so what runs on the phone and what loads in your DAW are one file. The judgement Ferment makes on its own ships here too: Percept reads the track, Master sets the chain from what it read.
PROPERTIES
A pure C++ DSP core under a thin JUCE shell — the same engines run headless on iOS, so what plays on the phone and what loads in your DAW are one file
Parameter indices are a frozen ABI: they never reorder and new ones only append, so a saved session keeps working
Every DSP claim in the repo is backed by a test you can run — fifteen suites under ctest
Ten seconds of Learn measures the track and writes the whole chain onto ordinary knobs — undoable, automatable, saved with the session, and the same values again on the same material
One design language across the family: dark face, amber arcs, and an EQ curve verified against the DSP to a tenth of a decibel
PLATFORMS
macOS (Apple silicon) — VST3, AU · Windows (x64) — VST3 · CLAP and Standalone from source
LICENSE
MIT

~/ferment $ ferment-family --help

PLUGINS

charge
One-knob leveller — a filtered detector into a symmetric gain cell, then saturation and shelf-bell-shelf character
eq
Eight bands of RBJ biquads — bell, shelf, cut and notch, 20 Hz to 20 kHz at ±15 dB
glue
Feed-forward bus compressor — 2, 4 or 10 to one, sidechain high-pass, auto release and a soft-clipped ceiling
limit
Dual-stage true-peak brickwall — a sliding-minimum transient bound that cannot overshoot, riding a sustain stage on crest-factor auto-release
clip
Mastering clipper — an ADAA morphing knee inside 4× polyphase oversampling, with tilt, bias for even harmonics and RMS-matched auto gain
utility
Gain, phase, channel mode, balance, width and mid/side solo, plus a DC filter and bass mono with a settable crossover
percept
The ear before the chain — gated LUFS, true peak, crest, spectral tilt, six-band share and mono-fold loss, read as verdicts and targets; the passthrough is bit-exact
master
Stage, Charge, Tone, Clip and Limit as one plugin, each latency-matched and crossfaded — and one Learn button that sets all five from the loud section
Ferment Charge — the plugin interface

~/ferment $ man cachebay

Pixel-art palm bay at night
NAME
cachebay — normalized graphql data layer
DESCRIPTION
A GraphQL cache that exists twice, in two languages, with one architecture.
The app and the server talk over the same API, so they should hold it in memory the same way. Cachebay is written once for the browser and once for Swift, cross-checked file by file against the same test corpus — normalize, materialize, paginate, revert. Learning it on one platform is learning it on both.
PROPERTIES
Normalized entities with interface-aware identity and precise watcher dependency tracking
Relay-style connections — edge dedup by node key, and O(1) inserts on Swift
Layered optimistic updates with commit and revert; Swift adds an explicit dispose
SSR that hydrates without a duplicate request
IndexedDB persistence with cross-tab journal sync on the web, SQLite on Apple platforms
PLATFORMS
Web (Vue, Svelte) · iOS 18 · macOS 15 · tvOS 18 · watchOS 11 · visionOS 2
LICENSE
MIT
const { data, meta } = await cache.executeQuery({
  query: `query ($id: ID!) { post(id: $id) { id title } }`,
  variables: { id: "p1" },
  cachePolicy: "cache-and-network",
});

// meta?.source: "cache" | "network"
//
// Renders from cache, revalidates, and every watcher holding
// post:p1 updates once — batched into a single microtask.

~/ferment $ man replicaman

Three pixel-art trees on a waterline, each mirrored in the water below
NAME
replicaman — offline-first replication
DESCRIPTION
Ordered events and snapshots keep application data in sync across reconnects.
ReplicaMan handles delivery, retries, cancellation and recovery between a Rust server and Swift or Kotlin clients. A lost response can be retried with the same operation ID; an expired cursor recovers from a snapshot. Your application owns authentication, storage and merge rules, and commits protocol state together with its data.
PROPERTIES
Push, pull and snapshot share one versioned protocol across Rust, Swift and Kotlin
Stable operation IDs let a client retry a lost response without applying the same change twice
Ordered events within each stream; an expired cursor recovers from a complete snapshot
Application data and protocol state advance in one durable transaction
Client ownership and account scopes prevent late replies from changing a replacement client's state
Embeddable Axum server, shared wire fixtures, conformance tests and bounded TLA+ models
PLATFORMS
Rust 1.94+ · Kotlin/JVM (JDK 17) · iOS 17 · macOS 14
LICENSE
MIT
import ReplicamanSync

let transport = try URLSessionTransport(endpoint: serverURL) {
  await credentials.currentHeaders()
}
let config = ClientConfiguration(
  load: { stream in try await store.load(stream) },
  commit: { transition in try await store.commit(transition) }
)
let replica = try await SyncClient(
  stream: scope.stream,
  configuration: config,
  transport: transport
)

// The app's store commits data and protocol state atomically.
// Keep these tasks in the app's lifecycle.
let reads = Task { await replica.runReads() }
let sends = Task { await replica.runSender() }

~/ferment $ man processorman

Pixel-art patch graph: operator boxes wired by dotted cables, each previewing its own output — noise, a ramp, a curve, a shaded sphere
NAME
processorman — distributed graph runtime
DESCRIPTION
A runtime for processing graphs, where nothing is computed twice.
Every piece of work in Ferment is a graph of operators over media — decode, denoise, detect, render. ProcessorMan runs those graphs: dependencies resolve, runnable waves dispatch in parallel, and each result is addressed by the path of the operator that made it. The same key is computed on the server and on the phone.
PROPERTIES
Declarative networks — operators, options and wires, changed without a redeploy
Cook keys are paths — pmck/<type>/<id>/<operator> — minted byte-identically by both runtimes, so a result found by one is not recomputed by the other
No master — workers pull as dependencies clear, so adding processes adds throughput
Server ↔ device rendezvous: work deferred by one runtime is picked up by the other
PLATFORMS
Rails 8 · iOS 26 · macOS 15
LICENSE
MIT
SOURCE
github.com/lockvoid/processorman soon
github.com/lockvoid/processorman-swift soon
class MyNetworks
  include ProcessorMan::Builder

  def self.media(id)
    scoped("media.#{id}") do
      op "source", "DownloadFileProcessor", source_ref: id

      op "denoised", "DenoiseSpeechProcessor", source: id do
        wire "source", as: "audio"
      end

      op "waveform", "GenerateWaveformProcessor" do
        wire "denoised", as: "audio"
      end
    end
  end
end

network.dispatch(MyNetworks.media(123))

~/ferment $ man kine

Pixel-art motion study
NAME
kine — motion graphics as a document
DESCRIPTION
A motion document is a value, not a program.
A Kine document is JSON: a pure function of (inputs) → scene, with no clocks, no state and no scripts. One Rust core renders it byte-identically on a Linux server and on an iPhone, so the preview on the device is the render from the server. The format exists so that programs can write motion, not just play it.
PROPERTIES
Typed, defaulted inputs — every document renders standalone; signals are optional
Text animation per glyph, word and line, over HarfRust shaping, RTL included
Colour derivation inside the document, in Oklab — alpha, contrast, mix; one seed becomes a palette
Springs baked to pure curves; animated GIF / WebP / APNG composited on the same clock
Deterministic: identical inputs, identical pixels, every platform. The goldens are the conformance suite
The C ABI catches panics — bad input returns an error instead of crashing
PLATFORMS
macOS · iOS · Linux — over a C ABI
LICENSE
MIT or Apache-2.0

~/ferment $ kine render docs/hero.json --out -

A karaoke title card rendered by Kine — per-word activations over a derived palette

# inline image — iTerm2 / kitty graphics protocol

"colors": [
  { "key": "active",  "value": { "input": "accent" } },
  { "key": "pending", "value": { "fn": "mix",
      "a": { "input": "foreground" },
      "b": { "input": "background" }, "t": 0.62 } },
  { "key": "border",  "value": { "fn": "contrast",
      "of": { "input": "background" } } }
],
"animators": [
  { "target": "line.glyphs", "property": "color",
    "weight": { "stagger": { "driver": "progress",
                "total": 0.85, "from": "start" } } }
]

~/ferment $ man clickman

Pixel-art constellation of connected stars, reflected in the water below
NAME
clickman — self-hosted app analytics
DESCRIPTION
Record events offline. Explore them on your own server.
Analytics starts on the device, even when the connection is gone. ClickMan keeps events in a SQLite queue and delivers compressed batches to a PostgreSQL-backed server. Host it on its own or embed it in an Axum app; the dashboard covers activity, retention, funnels and event breakdowns.
PROPERTIES
Durable SQLite queues keep events across restarts and offline periods
Gzip batches and retries that respect server retry deadlines
Swift, Kotlin and Rust clients share one protocol and queue fixtures
Project-scoped ingestion keys, with separate dashboard authorization
Activity, retention, funnels and event breakdowns in a Svelte dashboard
Standalone API and worker, or an embeddable Rust/Axum engine
PLATFORMS
iOS 16 · macOS 13 · Android API 28+ · JVM · PostgreSQL 14+
LICENSE
MIT
import ClickMan
import Foundation

let analytics = try ClickMan(configuration: .init(
  endpoint: URL(string: "https://analytics.example.com")!,
  writeKey: "your-project-write-key"
))
analytics.identify("creator-42")
analytics.track("edit_exported", properties: ["format": "reel"])

// Tracking writes locally; delivery runs in the background.
await analytics.flush()
QR code — download Ferment

Get the app

Scan the QR code with your phone