Session
Protocol.
How royalties are calculated, how the ledger branches between compose-time equity and sale-time settlement, and how every USDC leg stays auditable from Kafka through Solana.
00 · Charter
One protocol. Two clocks.
Consequence runs two intertwined timelines: the compose clock (signals → algorithmic equity → Solana PDA updates) and the commerce clock (marketplace purchase → split calculation → Circle payouts). Session Protocol is the contract between them — what gets recorded, who may read it, and how capital always defers to the latest verified ownership vector.
Signals & equity
MIDI, arrangement, AI acceptance, session time — normalized to 10,000 basis points.
Branch authority
Mongo projections for speed; Solana PDAs for settlement truth; ClickHouse for analytics.
USDC legs
Platform fee, net pool, parallel creator transfers — confirmed <2s on Solana.
01 · Royalty calculation
From gross sale to creator legs
Every marketplace purchase emits a single gross figure in USDC. The settlement service applies a platform fee, reads ownership from the composition PDA on Solana, and allocates the net pool proportionally. No renegotiation at checkout — the chain snapshot wins.
Live split model
$15.00 USDC gross
Platform
−$1.50
10% fee
Net pool
$13.50
Creator A · 60%
$8.10
Creator B · 30%
$4.05
Creator C · 10%
$1.35
Splits read from on-chain ownership at settlement time — not cached listing metadata. Platform fee accrues to treasury twin before net legs execute on Circle → Solana.
02 · Ledger branch
Ledger Branch — where truth lives
The ledger is not one database — it is a branch. Hot twins in Mongo answer product surfaces in milliseconds. Solana holds the ownership vector settlement must cite. ClickHouse mirrors events for Monte Carlo and audit. Reconciliation jobs detect drift; settlement never proceeds on stale equity.
- Composition PDA — stores normalized basis points per creator; updated after each equity recomputation.
- Equity snapshots — Mongo timeline of every revision with signal evidence and rule overrides.
- Settlement records — gross, fee, legs, Circle + Solana refs — immutable once confirmed.
- Event bus — Kafka partitions by composition; purchase events enriched and on bus in <100ms.
03 · Algorithmic equity
The equity formula
Contribution signals are normalized per channel, weighted, and summed. Log scaling prevents spam; pre-agreed floors and caps apply when creators negotiate fixed splits. Output is always 10,000 basis points — the only vector settlement trusts.
export interface CreatorSignals { creatorId: string; participationSeconds: number; midiNotesCreated: number; arrangementImprovements: number; aiSuggestionsGenerated: number; aiSuggestionsAccepted: number; structuralChanges: number;} export const SIGNAL_WEIGHTS = { participation: 0.25, midi: 0.3, arrangement: 0.2, ai: 0.15, structural: 0.1,} as const; function log1pScaled(n: number): number { return Math.log1p(Math.max(0, n));} function normalizedParticipation(signals: CreatorSignals[], i: number): number { const total = signals.reduce((s, c) => s + c.participationSeconds, 0); if (total === 0) return signals.length ? 1 / signals.length : 0; return signals[i].participationSeconds / total;} function normalizedMidi(signals: CreatorSignals[], i: number): number { const raw = signals.map((c) => log1pScaled(c.midiNotesCreated)); const sum = raw.reduce((a, b) => a + b, 0); if (sum === 0) return signals.length ? 1 / signals.length : 0; return raw[i] / sum;} function blendAiCredit(signals: CreatorSignals[], i: number): number { const gen = log1pScaled(signals[i].aiSuggestionsGenerated); const acc = log1pScaled(signals[i].aiSuggestionsAccepted); const score = 0.3 * gen + 0.7 * acc; const scores = signals.map( (c) => 0.3 * log1pScaled(c.aiSuggestionsGenerated) + 0.7 * log1pScaled(c.aiSuggestionsAccepted) ); const sum = scores.reduce((a, b) => a + b, 0); return sum === 0 ? (signals.length ? 1 / signals.length : 0) : scores[i] / sum;} function normArrangement(signals: CreatorSignals[], i: number): number { const raw = signals.map((c) => log1pScaled(c.arrangementImprovements)); const sum = raw.reduce((a, b) => a + b, 0); if (sum === 0) return signals.length ? 1 / signals.length : 0; return raw[i] / sum;} function normStructural(signals: CreatorSignals[], i: number): number { const raw = signals.map((c) => log1pScaled(c.structuralChanges)); const sum = raw.reduce((a, b) => a + b, 0); if (sum === 0) return signals.length ? 1 / signals.length : 0; return raw[i] / sum;} export function computeAlgorithmicEquity(signals: CreatorSignals[]): Map<string, number> { if (signals.length === 0) return new Map(); const weighted = signals.map((_, i) => { const p = normalizedParticipation(signals, i) * SIGNAL_WEIGHTS.participation; const m = normalizedMidi(signals, i) * SIGNAL_WEIGHTS.midi; const ar = normArrangement(signals, i) * SIGNAL_WEIGHTS.arrangement; const ai = blendAiCredit(signals, i) * SIGNAL_WEIGHTS.ai; const st = normStructural(signals, i) * SIGNAL_WEIGHTS.structural; return p + m + ar + ai + st; }); const total = weighted.reduce((a, b) => a + b, 0); const out = new Map<string, number>(); if (total === 0) { const even = Math.floor(10_000 / signals.length); signals.forEach((c, idx) => out.set(c.creatorId, even + (idx === 0 ? 10_000 - even * signals.length : 0)) ); return out; } let allocated = 0; signals.forEach((c, i) => { const bp = Math.round((weighted[i]! / total) * 10_000); allocated += bp; out.set(c.creatorId, bp); }); const drift = 10_000 - allocated; if (drift !== 0 && signals[0]) { const id = signals[0].creatorId; out.set(id, (out.get(id) ?? 0) + drift); } return out;}04 · Settlement rail
Circle → Solana execution
After splits are computed, Circle Payouts stages parallel USDC legs. The program verifies signatures against the PDA, writes settlement snapshots, and twins refresh lifetime revenue and available balances — auditable end-to-end.
import type { Connection, PublicKey } from "@solana/web3.js"; export interface SettlementQuote { saleGrossUsdc: bigint; platformFeeBps: number;} /** * Settlement always hydrates authoritative ownership from the Solana PDA — * never from a Mongo cache — before computing per-creator payouts. */export async function executeSettlementWithEquity(params: { rpc: Connection; compositionPda: PublicKey; quote: SettlementQuote; /** Platform treasury receives fee leg */ platformTreasury: PublicKey; creatorWallets: Map<string, PublicKey>;}): Promise<{ netToCreator: Map<string, bigint>; platformFee: bigint; equitySnapshotSig: string;}> { const acct = await params.rpc.getAccountInfo(params.compositionPda, "confirmed"); if (!acct?.data) throw new Error("missing composition account"); const { ownership, lastSnapshotSig } = decodeCompositionState(acct.data); const totalBps = ownership.reduce((s, o) => s + o.basisPoints, 0); if (totalBps !== 10_000) throw new Error("invalid equity sum"); const gross = params.quote.saleGrossUsdc; const fee = (gross * BigInt(params.quote.platformFeeBps)) / 10_000n; const net = gross - fee; const netToCreator = new Map<string, bigint>(); for (const row of ownership) { const wallet = params.creatorWallets.get(row.creatorId); if (!wallet) continue; const share = (net * BigInt(row.basisPoints)) / 10_000n; netToCreator.set(row.creatorId, share); await transferUsdc(params.rpc, params.platformTreasury, wallet, share); } await recordSettlementOnChain({ composition: params.compositionPda, gross, fee, netSplits: netToCreator, equitySnapshotSig: lastSnapshotSig, }); return { netToCreator, platformFee: fee, equitySnapshotSig: lastSnapshotSig };} function decodeCompositionState(_data: Buffer): { ownership: { creatorId: string; basisPoints: number }[]; lastSnapshotSig: string;} { throw new Error("decode Borsh layout for your program version");} async function transferUsdc( _rpc: Connection, _from: PublicKey, _to: PublicKey, _amount: bigint): Promise<void> { // SPL transfer_checked} async function recordSettlementOnChain(_args: unknown): Promise<void> { // record_settlement instruction}05 · Full trace
Velvet Clip · $15 USDC
From first bar to final leg — the same narrative the royalty carousel steps through, at architectural scale.