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.
#projectlayerdescriptionstacklicensesource
01Ferment FamilysoundMixing and mastering pluginsC++MITgithub02CachebaystateNormalized GraphQL data layerTypeScript · SwiftMITgithub03ReplicaMansyncOffline-first replicationRust · Swift · KotlinMITgithub04ProcessorMancomputeDistributed graph runtimeRuby · SwiftMITsoon05KinerenderMotion graphics as a documentRust · Ruby · SwiftMIT or Apache-2.0github06ClickManmeasureSelf-hosted app analyticsRust · Swift · KotlinMITgithub~/ferment $ man ferment-family
- 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 $ man cachebay
- 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. let client = CachebayClient(options: .init(
transport: Transport(http: http, ws: ws),
storage: SQLiteStorage.factory(options: .init(path: cachePath)),
))
// GetPost is generated by cachebay-cli from your .graphql
// files — no operation strings and no parsing at runtime.
let result = try await client.execute(
GetPost.self,
variables: .init(id: "p1"),
) ~/ferment $ man replicaman
- 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() } import io.replicaman.sync.*
// The app supplies the stream, transport and durable store.
val config = ClientConfiguration(
load = { stream -> store.load(stream) },
commit = { transition -> store.commitAtomically(transition) },
)
val replica = SyncClient.open(stream, config, transport)
replica.start(applicationCoroutineScope)
// Report lifecycle and connectivity changes.
replica.foreground()
replica.connectivity(available = true)
// After a local edit, ask the sender to pick up the work.
replica.refresh() ~/ferment $ man processorman
- 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)) let graph = Graph.build(
name: "pmck/Element/\(elementId)",
refs: OperatorRef.self,
context: AudioContext.self
) { b in
b.op(.denoised, DenoiseSpeechProcessor.self,
.init(source: elementId))
b.op(.waveform, GenerateWaveformProcessor.self) { wiring in
wiring.wire(.denoised, as: .audio)
}
}
host.execute(graph, context: context) ~/ferment $ man kine
- 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 -
# 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
- 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() import com.lockvoid.clickman.android.ClickManAndroid
// Start on the main thread at application launch.
val analytics = ClickManAndroid.start(
context = applicationContext,
endpoint = "https://analytics.example.com",
writeKey = "your-project-write-key",
)
analytics.identify("creator-42")
analytics.track("edit_exported", mapOf("format" to "reel"))
// Request delivery without blocking the UI.
analytics.flushInBackground() use clickman::{ClickMan, Config};
use serde_json::json;
// The application supplies its HTTP client and async runtime.
let analytics = ClickMan::open(
Config::new(
"https://analytics.example.com",
"your-project-write-key",
"data/clickman/queue.sqlite",
),
http,
)?;
analytics.identify("creator-42")?;
analytics.track("edit_exported", json!({"format": "reel"}))?;
// Call periodically from the application's runtime.
analytics.send_due().await?;