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 replicationRuby · Swift · RustMITsoon04ProcessorMancomputeDistributed graph runtimeRuby · SwiftMITsoon05KinerenderMotion graphics as a documentRust · Ruby · SwiftMIT or Apache-2.0github~/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.
- Cuts 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 Cuts on iOS and wrapped for the desktop, so what runs on the phone and what loads in your DAW are one file. The judgement Cuts 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
- A stream declared on the server becomes a convergent replica on the device.
- An app that has to work on a plane needs a replica it can query cold, a merge instead of last-write-wins, and a typed client. ReplicaMan puts the merge protocol in the stream declaration: a CRDT lane for state people co-author, a row lane for state with exactly one writer. The schema you already wrote generates the client.
- PROPERTIES
- Lanes declared, not special-cased: Loro CRDT for authored documents, no-history rows for everything else — one wire, one cursor
- Your ActiveRecord schema is introspected into a committed manifest; the manifest generates the typed client
- Gap-free cursors on Postgres xid8, gated on snapshot xmin — page cuts land on transaction boundaries, never mid-commit
- Atomic checkpoints: a crash mid-pull leaves the previous checkpoint whole, never half a world
- Verdicts, never silent retries — a rejection reverts the local write from a captured preimage
- One verb set per lane — save and delete on rows, create and delta on documents, find, where and watch on both
- PLATFORMS
- Rails 8 · PostgreSQL 13+ · iOS 18 · macOS 15
- LICENSE
- MIT
- SOURCE
- github.com/lockvoid/replicaman soon
- github.com/lockvoid/replicaman-swift soon
# Document lane — Loro is the merge protocol, the row
# columns are a projection of the folded doc.
class Replica::Projects < ReplicaMan::Stream
scope ->(user) { { user_id: user.id } }
door ProjectNormalizer
attribute :id, :user_id, :name
end
# Row lane with no door = server-authored. Pushes are
# refused, and codegen emits no write verbs at all —
# the client cannot express the mistake.
class Replica::Networks < ReplicaMan::Stream
scope ->(user) { { user_id: user.id } }
attribute :id, :key, :version, :data
end
mount FermentReplica => "/replica" // Generated from the server's manifest. Nothing here
// knows whether the device is online: the write lands
// locally and durably, the journal owes it to the server.
try await replica.elements.save(element)
let clips = try replica.elements.where(
["projectId": .string(project.id)]
)
for try await clips in replica.elements.watch() {
render(clips)
} ~/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" } } }
]