Home / Build vs fork

What to fork, and what to write

Before writing a custodial exchange from scratch, the obvious question is whether someone has already open-sourced one. This is the answer, with the licences checked rather than assumed.

1. The headline finding

There is no open-source platform that does this. Custodial vaulting plus an order book plus title transfer for physical collectibles does not exist as a forkable project. Every operator running this model runs closed, proprietary software.

That is worth stating plainly because it reframes the question. "Fork the platform" is not available. What is available is forking the hard parts individually — and some of those parts are genuinely worth taking rather than writing.

Who already runs this model

The commercial operators confirm the model works and that none of it is open:

OperatorWhat they doSource
PWCC Vault Long-running trading-card vault. Explicitly facilitates trading and transfers between vault clients — the same zero-movement settlement model. Free storage above a value threshold, insurance included. Closed
Arena Club Grading plus vaulting plus a marketplace where vaulted slabs are bought, sold and traded instantly. Accepts PSA, BGS, SGC, CGC and CSG slabs alongside its own. Closed
eBay Vault, Goldin, Fanatics Collect Vault-and-trade offerings attached to existing marketplaces. Closed

The one nominally adjacent open-source project — a self-hosted collectibles vault for comics and figures — is a personal inventory tracker with valuation lookups. It has no order book, no ledger, no custody model and no title transfer. It solves a different problem that happens to share a noun.

2. The pieces that are forkable

Decompose the product and each layer has mature open-source options. Licences below were read from each repository, not recalled — the difference between MIT and GPL decides whether a fork is viable for a commercial product at all.

LayerProjectLanguageRuns on LicenceCall
Ledger tigerbeetle/tigerbeetle
Purpose-built double-entry financial database. Deterministic execution, strict serializability, accounts that cannot go negative — enforced by the database, not by application code.
Zig
clients: Go, Java, Node, Python, .NET
Single static binary
own replication (VSR), no Postgres underneath
Apache 2.0 Adopt. Highest-value fork here.
formancehq/ledger
Programmable double-entry ledger. Immutable tamper-evident log, multi-asset, multi-ledger, Numscript DSL for modelling money movement.
Go 1.26 PostgreSQL
via pgx; Docker image published
MIT Strong alternative. Easier to operate, less extreme guarantees.
Matching engine mzheravin/exchange-core
Order matching plus risk control and journaling. ~2.6k stars, 972 forks.
Java
LMAX Disruptor, Maven
JVM, in-memory
event-sourced journal
Apache 2.0 Capable, but the layer least worth forking — see below.
enewhuis/liquibook
Low-level order matching components rather than a service.
C++ Library, embed it None stated No licence file in the repo. Hard no for a commercial fork.
Warehouse openwms/org.openwms
Extensible WMS with material flow control: locations, transport orders, put-away and picking strategies.
Java
Spring Boot, Maven
Microservices
RDBMS + message broker
Apache 2.0 Adopt. Enormous, boring, solved.
Storefront, identity, payments medusajs/medusa
Headless commerce. Modular monorepo — take the auth, customer and payment modules without the storefront.
TypeScript
Node
PostgreSQL + Redis MIT Good fit for accounts, KYC surface and checkout.
vendure-ecommerce/vendure
Headless commerce, GraphQL-first, plugin architecture.
TypeScript 5.8
NestJS, TypeORM, GraphQL 16
PostgreSQL / MySQL GPL Copyleft. Check before adopting, not after.

Languages and datastores were read from each repository's build manifest — build.zig, go.mod, pom.xml, package.json — not recalled. Licences were read from the licence file at repository head. Both can change; re-check before committing to a fork.

3. The recommendation

Write the matching engine

Counter-intuitive, but the engine is the cheapest layer here. Ours is a few hundred lines with price–time priority, partial fills, reservations and no self-crossing, and it is covered by tests. exchange-core is built for millions of orders per second on fungible instruments — we match slabs, in a market where a busy day is thousands of trades. Adopting it would mean running a JVM service to solve a problem we do not have.

Fork the ledger

The opposite call. A correct double-entry ledger under concurrency is genuinely hard, and getting it wrong is the failure mode that loses customer money silently. TigerBeetle enforces balance and non-negativity in the database, which no amount of application-level discipline matches. Our journal has the right shape — accounts, instruments, balanced entries — which makes it a migration, not a rewrite.

Fork the warehouse

Bin addressing, pick tasks, cycle counts, material flow. It is thousands of hours of unglamorous work that has been done well and permissively licensed. The only part worth writing ourselves is the custody state machine, because that is where our specific invariant lives: an item is tradable only when vaulted and unreserved.

The general rule this follows. Fork where correctness is hard and the domain is generic — ledgers, warehouses, payments. Write where the domain is specific and the code is small — the custody state machine, the SKU identity model, the swap semantics. The parts of TCG Keepr that would be hard to buy are exactly the parts that are cheap to write, and vice versa.

4. What migrating the ledger would involve

Our journal already uses the account and instrument shape these systems expect, which is why this is tractable:

Current model → TigerBeetle model
// ours
{ account: "holder:usr_4f2a17", instrument: "ASSET:itm_c8a1f9", dr: 1 }
{ account: "cash:usr_9b71c4",  instrument: "USD",             dr: 42945000 }

// TigerBeetle: accounts are typed by ledger id, transfers are the entries
account { id, ledger: USD | ASSET, code: holder | cash | revenue, flags }
transfer { debit_account_id, credit_account_id, amount, ledger, code }

// The asset leg and the cash leg become linked transfers in one atomic
// batch — the same all-or-nothing property our tx() wrapper provides, but
// enforced by the database instead of by a JSON snapshot and a try/catch.

Two things would have to be rethought rather than mapped. The hash chain is ours and would stay ours — these ledgers give immutability and tamper-evidence, but the chained digest that lets an outside party verify history against a head they already hold is an application concern. And non-fungible assets are an unusual fit: a ledger built for currency balances has to model one specific slab as a one-unit account, which works but wants care.

What this page is not. A decision to migrate today. The in-browser engine is a complete, tested implementation of the semantics, and it is the right thing to keep while the semantics are still moving. This is the record of what we looked at, what the licences actually say, and which way we would jump when a real backend is written — so that decision gets made from research rather than from whatever is top of mind that week.

The engine this replaces is already running

Matching, reservations, the double-entry journal and the hash chain are live in the terminal, in about a thousand lines, with 119 assertions behind them.