Reconciliation between tokenized securities on Stellar and the official books kept by transfer agents, custodians and fund administrators.
When a security lives both on-chain and in a register, someone has to prove the two agree, every day. Dividends, splits, freezes and clawbacks have to land on both sides. Today that work is manual. Concord does it from ledger events.
Status: working, and ready to put in front of people. The indexer, reconciler,
corporate-actions engine, controls log and audit export run end to end behind sign-in, with a
private workspace per organisation. It has been exercised against Stellar mainnet: watching
Franklin Templeton's BENJI, Concord's unit total matched Horizon's own figure to the last decimal,
and a register built from the chain's holdings reconciled clean across all 1,005 positions. No
customer has used it. The product brief and kill criteria are in BRIEF.md.
Needs Node 24+ and pnpm 9+. Nothing else: storage is SQLite through node:sqlite.
pnpm install
pnpm build
pnpm start # API and dashboard on http://127.0.0.1:4400Open the address, create a workspace, and watch an asset. Mainnet and testnet are both served; the switch is at the top left. A new workspace is empty. Concord ships no sample data: the chain side comes from the ledger, and the register side is the file you upload.
pnpm test runs the unit and service tests. pnpm dev runs the API and the dashboard with reload.
Configuration is by environment variable, or a .env file beside package.json; see
.env.example. With nothing set, mainnet uses free public RPC endpoints, which
suit light use. Point CONCORD_MAINNET_RPC_URLS at a provider for anything more.
- A workspace holds the assets an organisation watches and the books it uploads. Each one is
a SQLite file of its own under
data/orgs/, so nothing a workspace stores can be reached from another. Accounts, memberships and sessions live indata/control.db. - People sign in with email and password, or with Google when
CONCORD_GOOGLE_CLIENT_IDandCONCORD_GOOGLE_CLIENT_SECRETare set. Concord sends no email: an owner creates an invitation link and passes it on, and a forgotten password is reset with a link frompnpm concord reset-link EMAIL. - Owners invite and remove people, stop watching assets and can delete the workspace. Members do everything else. Whoever signs off a break is recorded from their session.
CONCORD_SIGNUP=openlets anyone create a workspace.CONCORD_SIGNUP=coderequires a code frompnpm concord signup-code.
Everything on the dashboard except sign-off and team management is also on the command line. It works on the same data directory as the server, and may run while the server does.
pnpm concord orgs
export CONCORD_ORG="Harbourline Transfer" # or pass --org to each command
pnpm concord watch FUND:GISSUER... --name "Example Fund" --ignore GTREASURY...
pnpm concord register FUND ./register.csv --as-of 2026-10-06T21:00:00Z
pnpm concord journal FUND ./journal.csv
pnpm concord instructions FUND ./instructions.csv
pnpm concord action FUND dividend --record 2026-10-01T21:00:00Z --rate 0.05 --pay-asset USDC --payer GAGENT...
pnpm concord action FUND split --record 2026-10-01T21:00:00Z --ratio 2:1
pnpm concord controls FUND
pnpm concord export FUND --out audit.json
pnpm concord backup ./backup-2026-10-08
pnpm concord --helpThe Dockerfile builds one image that serves the API and the dashboard. Give it
a volume at /data, put TLS in front, and set CONCORD_PUBLIC_URL to the https address people
open.
docker build -t concord .
docker run -p 4400:4400 -v concord-data:/data -e CONCORD_PUBLIC_URL=https://concord.example.com concordConcord is a single process with a background indexer and SQLite files, so it needs one always-on instance with a local disk. It does not fit serverless hosts.
render.yaml deploys it to Render's free plan for a preview. That plan has
no persistent disk: every account, upload and sign-off is erased when the service restarts or
spins down after fifteen idle minutes. CONCORD_EPHEMERAL=1 makes the dashboard say so on every
page. For anything people should come back to, use a paid instance with a disk at /data (or
Fly.io, or a VPS) and remove that variable.
| Path | What it is |
|---|---|
packages/core |
The accounting, with no I/O: holdings replay, position and movement reconciliation, entitlements, controls matching |
services/api |
RPC indexer, SQLite stores, accounts and workspaces, HTTP API, CLI |
apps/web |
Dashboard: sign-in, breaks and sign-off, holdings, corporate actions, controls log, audit export, team |
All CSV with a header row. Amounts are decimals in whole units; more precision than the asset carries is rejected, not rounded. Rows that cannot be read are listed with their line number and the reason, and the rest of the file is loaded. Each import form offers a blank template.
| File | Columns |
|---|---|
| Register | holder_id, units, and optionally holder_name, account, status (active or frozen). Several holders may share one account (omnibus). |
| Journal | ref, date, type (issue, transfer, redeem, clawback), units, and optionally from_account, to_account, tx_hash |
| Instructions | ref, type (authorize, freeze, unfreeze, clawback), account, issued_at, and optionally units (required for a clawback), approved_by, reason |
- Positions: quantity mismatch, holder on chain but not on the register, holder on the register but not on chain, register holder with no address, frozen/active disagreement, total supply mismatch.
- Movements (when a journal is loaded): on-chain movement missing from the journal, journal entry with no ledger event, entry whose cited transaction moved a different amount.
- Corporate actions: per holder, entitlement at the record time against what the ledger showed in the execution window: missing, short, excess, or paid to someone not entitled. An action recorded ahead of its record time is reconciled by itself once the chain passes it.
- Controls: a freeze, authorisation or clawback with no instruction issued in the 72 hours before it, and an instruction that was never executed.
A break can be acknowledged or resolved with a note. The sign-off holds only while the figures it was written against are unchanged; if the break moves, it reopens.
A reconciliation runs when a register is imported and whenever someone re-runs it. Nothing reconciles on a timer: a new day's comparison needs that day's register.
- History. Stellar RPC keeps about seven days of events. A classic asset is therefore watched from opening balances: every trustline's balance from Horizon, then events from that ledger on. Horizon does not list balances held by liquidity pools, claimable balances or contracts, so opening balances understate supply by whatever those hold. Holdings before the opening balances cannot be queried.
- Contract tokens. A SEP-41 token that is not the contract of a classic asset has no opening
balances to read, so its holdings count only movements from the oldest ledger the RPC keeps.
The dashboard says so on every page of such an asset. Tokens that do not emit the standard
set_authorizedandclawbackevents will show an empty controls log. - Gaps. If the indexer is down for longer than RPC retention, the events in between are lost. Concord detects this, stops indexing the asset and says why; Rebuild history in the asset's settings starts it again from fresh opening balances and keeps the uploaded books.
- Decimals. Classic assets carry seven. A contract token's are read from the contract; one that does not state them has to be told when it is watched.
- Email. None is sent. Addresses are not verified at sign-up, invitations travel by link, and password resets go through the operator.
- Scale. One process, one SQLite file per workspace, holdings replayed from events in memory. It has run an asset with a thousand holders and seven thousand events comfortably; it has not been load-tested beyond that.
- Splits. Fractions of the smallest unit are dropped; cash in lieu is not modelled.
pnpm demo:testnet issues a regulated fund share on testnet (auth required, revocable, clawback
enabled) and runs subscriptions, a cash dividend, a transfer, a freeze, a clawback, a redemption
and a 2:1 split against it. It then writes a register, journal and instruction file that disagree
with the chain in known ways to data/demo/, and prints what to watch. The script never opens a
Concord database: you watch the asset with the network set to Testnet and upload the files by
hand, the way a user would. The books it writes are invented, and exist for development only.
The brief's first kill criterion is that someone already ships this for Stellar. A desk check found no vendor selling register-against-chain or corporate-action reconciliation for Stellar. It was a web search, with several sources read only as summaries, so treat it as a first pass.
- StellarExpert, Mercury/Zephyr and SDF Hubble provide data and indexing, not reconciliation.
- Securitize and Vertalo reconcile inside their own transfer-agent stacks; no Stellar product found.
- Allium is the closest: a Stellar RWA transfers table and content aimed at transfer agents, but no balances, control events, register comparison or corporate actions.
- Franklin Templeton's transfer agent reportedly reconciles BENJI daily with in-house tooling, which confirms the workflow exists.
- DTCC's release promises "corporate actions and reporting" for its H1 2027 Stellar connection. If that includes participant-facing reconciliation, the criterion triggers for DTC-eligible assets.
- DTCC's tokenization service plans to connect to Stellar in H1 2027. First assets: Russell 1000 stocks, major index ETFs, US Treasuries (DTCC press release, 2026-05-27).
- Stellar already holds about $4B of tokenized RWAs (SDF Dune dashboard, 2026-08-29). Spiko, Franklin Templeton (BENJI), Ondo and WisdomTree all keep official registers alongside the chain.
- CAP-67 (Final, Protocol 23) makes classic Stellar operations emit
transfer,mint,burn,clawbackandset_authorizedevents in the same format as Soroban. For the first time, regulated-asset activity can be indexed without decoding every operation type by hand.
| Module | Job |
|---|---|
| Indexer | Read CAP-67 events for a watched set of regulated assets (classic and SEP-41) |
| Reconciler | Match on-chain holdings and movements against each register export as it is imported, and on demand; flag breaks |
| Corporate actions | Record-date snapshots; dividend, split and redemption reconciliation |
| Controls log | Every freeze, authorisation change and clawback, tied to the instruction that caused it |
| Audit export | Regulator- and auditor-ready reports |
Read-only. Concord never holds keys or moves money.
Transfer agents, custodians and fund administrators serving issuers on Stellar. Priced as software: per asset and per seat.
Contributions are welcome. Start with CONTRIBUTING.md for setup and the
rules that matter, and docs/ARCHITECTURE.md for how the code fits
together. Report security problems privately, as described in SECURITY.md.
Everyone taking part is expected to follow the Code of Conduct.
MIT.
Concord is a stand-alone company. It was split from the Ballast idea set on 2026-09-16 because
it shares no buyer, balance sheet or regulatory load with Ballast. See
../ballast/README.md for the reasoning.