Oracle3
Open-source trading engine and MCP server for prediction markets. Fee-aware no-arbitrage trading across related contracts, live on Kalshi, Polymarket and Solana or on paper.
Dassie is a peer-to-peer payment network you run yourself. Any currency, any ledger, anyone.
FinanceRust · TypeScriptGitHub @justmoonOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
The maker's introduction consists of the line above. Read the repository README for more details. Repository README ↗
Loading the full README.
Read the full README ↗A multi-currency, zero-config, peer-to-peer, Interledger-enabled payment network
Dassie is a payment node you run on your own server. It finds other Dassie nodes, peers with them on its own, and routes Interledger payments through them. Peers keep track of what they owe each other and settle it on a real ledger, today the XRP Ledger testnet. You run your node from a web wallet that it serves itself, and apps can pay through it with Open Payments or BTP.
Status: Dassie is early and works with test money only. A new node joins the public testnet through its five bootstrap nodes, boot1–boot5.dassie.xyz (bootstrap-network).
This is the second implementation of Dassie, written in Rust, Node and Solid 2. It replaced the original TypeScript implementation, which you can still find at the tag original. The specification describes Dassie as it is on main; start with its README, which says which docs to read for the code you touch. It has one doc per area, such as the wallet or the connector, which states that area's rules; and one file per decision, which records why a rule is what it is.
You need a Linux server with systemd, on x86-64 or arm64, with a public IP address, a domain name that points to it, TCP ports 80 and 443 open, and UDP port 443 open for QUIC, over which nodes talk to each other. The installer is tested on Debian 12, Ubuntu 24.04, Fedora 42 and CentOS Stream 9. On the server, run:
curl --proto =https --tlsv1.2 -sSf https://sh.dassie.land | sh
dassie verify-install
sudo dassie init
The installer fetches the newest release from get.dassie.land, checks its signature before unpacking anything, installs it in /opt/dassie and sets up the systemd units. It also adds you to the group dassie-users, whose members may talk to the node; log in again for that to take effect.
dassie init asks for your domain name and an email address, gets a certificate from Let's Encrypt, and prints a URL where you finish the setup in your browser. If you miss it, sudo dassie setup-url prints it again. For a node nobody sets up in a browser, sudo dassie setup --passphrase-file <file> sets it up from a passphrase you keep in a file on the server, --scheme <id> adds a settlement scheme, and --hub <scheme> joins the default settlement hub of that scheme's asset, such as hub-xrpl-testnet-dsusd (on the testnet it joins hub-xrpl-testnet-xrp unless told otherwise, and --hub none joins none); the passphrase stays on the server, and only the node's key reaches the node. dassie setup sets no hostname: the hostname is your configuration, kept in the node's database, so after you wipe the node's state directory, run sudo dassie init --hostname <name> again.
Dassie updates itself: once a day, a systemd timer checks for a newer release, verifies it and switches to it, and goes back if the new version does not start. sudo dassie update does the same right away. To uninstall, run the installer with sh -s -- uninstall. The node's data stays in /var/lib/private/dassie, and your systemd drop-ins in /etc/systemd/system/dassie*.d, in case you change your mind.
The Quick Start on dassie.land walks through the same steps.
On NixOS, each release publishes a flake instead, https://get.dassie.land/<version>/flake.tar.gz, and https://get.dassie.land/flake/latest.tar.gz is the newest one, which nix flake update follows. It holds a module that runs the release's signed binary under its own systemd units, without the updater. Dassie on NixOS shows how to use it.
Once set up, the wallet asks you for a first ledger:
addSettlementScheme with xrpl-testnet-xrp; the account's server and faucet are its own config, setAssetConfig on xrpl-testnet-account/0 (or dassie setup --asset).xrpl-testnet-dsusd, settles in DSUSD, Dassie's own testnet dollar, from the same account, which pays its fees in XRP; you buy DSUSD with test XRP on the testnet's XRP/DSUSD pool. It stands in on the testnet for Ripple's RLUSD, whose testnet scheme, xrpl-testnet-rlusd, is disabled: a node that ran it starts without it, and its test RLUSD stays on its line.From there, the wallet sends and receives payments, shows their status and your activity, and manages everything below.
Open Payments. Each ledger has a wallet address at https://<your host>/pay/<ledger>, for example /pay/xrp, and $<your host> names the default one. Other wallets and apps pay into it, and with a grant you approve in the wallet, they may also pay out of it, up to the limits you agreed to. The wallet's Send takes a payment pointer or a payment request, and its grants page lists who may do what.
A node without a hostname answers Open Payments at https://<node ID>.dassiepay.net instead, through the front door there, which reaches it over a connection the node keeps to it while it has no hostname. That front door is the default relay, kept only while the node has no hostname; DASSIE_RELAY names another, as the entry its front door logs, or, set empty, none (default-relay).
BTP. A BTP token, made in the wallet's settings, lets an ILP client such as ilp-plugin-btp spend through the node, at most its spending cap per window if you give it one. Over RPC, createBtpToken answers with the btp+wss:// URL to connect with.
Exchange rates. When your ledgers hold different currencies, the node converts between them at rates it fetches from Coinbase, Kraken, Bitstamp and the European Central Bank, taking the median of the three that cover each currency. You can change the sources and the currency the wallet shows totals in, in the wallet's settings or with setRateSources and setDisplayCurrency.
Peers. Nodes find each other through bootstrap nodes: the testnet's, boot1–boot5.dassie.xyz, by default. A node needs a hostname to take part; it then registers with them by itself, and peers with nodes that share one of its settlement schemes. Four of the bootstrap nodes, boot1–boot4, are the validators of the testnet's XRP hub and DSUSD hub, the default hubs a node joins, and all of them peer over both.
DASSIE_BOOTSTRAP_NODES replaces the list, as JSON: [{"id": "<node ID>", "url": "https://host:port", "publicKey": "<base64url>", "slhDsaPublicKey": "<base64url>"}], or [] to run alone: the node's ID, its URL, and its two public keys, Ed25519 and SLH-DSA, which the ID must be the hash of. DASSIE_RELAY takes one entry in the same form. Under systemd, set it in a drop-in, then sudo systemctl daemon-reload && sudo systemctl restart dassie; dassie update leaves drop-ins alone.
A node without a hostname still peers, as a periphery node: it asks core nodes to be its uplinks and is reached through them. DASSIE_UPLINKS pins them, as node IDs separated by commas (<node ID>,<node ID>): the node then asks only those, and looks for as many uplinks as it names, which is what a market maker's nodes need to trade through one core node. A node with a hostname ignores it, and says so when it starts.
listNodes, listPeers, listRoutes and getRegistrationStatus show what your node knows, and the wallet's network page shows the same.
Peers settle over a settlement hub they share: the credit each extends the other is what it holds toward it in the hub, and nothing more. A node asks a peer to settle once its room for the peer is down to one and a half of the largest packets, checking every 4 seconds; asked, a node settles half the way to its own point of asking; listSettlementChannels shows the settlements on their way. Each node keeps its own books, Fulfills it sent included, whatever its peer counted (own-fulfills): a peer can cost your node at most the credit it extends, since packets past it are refused. The wallet's settings, or getPeerSettings and setPeerSettings, show and set every peer setting at once: the fee and the exchange spread your node charges for passing payments on, how far its holdings move the rate it converts at, so that payments bring them back toward balance, whether it offers swaps to its peers and swaps with them, and a periphery node's swap targets. getSwapStatus shows what its swaps did, and swapWithPeer swaps with a peer at a rate you name.
terminatePeering ends a peering. Your node first collects, without telling the peer: it takes nothing new from it, asks it to settle once more, and waits for that settlement; then it tells the peer, and the peering drains and ends on both sides. Nothing else is settled for its end (ending-a-peering): what either side still owes stays on its books, for a later peering to settle. banNode and unbanNode keep nodes away, and listBans lists those banned. GET /.well-known/dassie/node tells anyone the node's ID, key and rate feed port.
Logs. DASSIE_LOG sets what the node logs: a level (trace, debug, info, warn, error or off; info by default), or directives per target in the form of tracing's EnvFilter, such as info,dassie::peer=debug. The node's targets are dassie::*. The wallet's debug pages show the recent log as well.
Event log. To record your own node's traffic, for debugging, a demo or research, set DASSIE_EVENT_LOG to a file (a relative path is taken in the state directory, /var/lib/dassie under systemd, so DASSIE_EVENT_LOG=events.jsonl works with the unit's sandbox). The node then appends every packet it handles and every event of its settlement hubs, one JSON object per line with the node's ID and a Unix-millisecond timestamp ts, and a lost line counting any it had to drop. The file is renamed to .1 once it reaches DASSIE_EVENT_LOG_MAX_BYTES (64 MiB by default), keeping four older files. It records amounts, addresses and peers of the payments that pass through your node, so it is off unless you set it: no release, installer or dassie setup turns it on. The line format is in the spec.
| Path | What |
|---|---|
crates/dassie-oer | Canonical X.696 OER, the subset the protocols use |
crates/dassie-identity | Recovery phrases with their own error correction, seed paths, node keys, node IDs |
crates/dassie-ilp | ILPv4 packets, addresses, timestamps, ILDCP, BTP 2.0 |
crates/dassie-stream | STREAM as sans-IO connection and server state machines |
crates/dassie-ledger | The internal double-entry ledger and its transfer journal |
crates/dassie-settlement | The settlement scheme interface, the settlement policy, activation reconciliation, the stub scheme |
crates/dassie-xrpl | The XRP Ledger: wallets, the Payment codec, the rippled API, the testnet settlement scheme, and a fake ledger for tests |
crates/dassie-connector | The ILP connector: endpoints, peers and the route table, ILDCP, amounts, exchange, fees and spread, spending caps, the packet trace |
crates/dassie-peer | Peer protocol v1: wire format, authentication, node table, link state, membership, registration, peering and its lifecycle, settlement delivery, routing |
crates/dassie-payments | Open Payments without IO: resources, GNAP grants, HTTP message signatures, payment pointers, intervals, quote math |
crates/dassie-feed | A node's signed snapshots of its quotes without IO: the datagrams, their numbers, the padded request and the budget on answering it, when to publish, and a reader's side that verifies a snapshot and prices a swap by it |
crates/dassie-node-core | The node's decisions without IO: config, setup, sessions, origins, rate limits, ACME and TLS policy |
crates/dassie-node | The daemon: database, HTTP(S) and IPC listeners, TLS, ACME, owner login, RPC with its live streams, the engine (ledger, peer protocol, Open Payments, bans), the QUIC endpoint and the peer client, the BTP server, the STREAM receiver, the rate fetcher, the discovery document, the log ring, and a fake XRP Ledger for tests |
crates/dassie-cli | The dassie binary: daemon, init, update, |
The easiest way to get every tool at the right version is Nix: nix develop gives you a shell with all of them, and nix flake check runs what CI runs (CI builds those checks on the Vorstand Nix cloud), offline; CI also checks the lockfile against the npm and JSR registries, which pnpm install does too.
Without Nix, you need:
rust-toolchain.toml, with the wasm32-unknown-unknown target;wasm-bindgen CLI, at the version in Cargo.lock;pebble, pebble-challtestsrv, systemd-socket-activate and mkcert. Without them, those tests are skipped outside CI.cargo fmt --check && cargo clippy --all-targets -- -D warnings && cargo test
pnpm install # the npm and JSR packages, as pnpm-lock.yaml pins them
node --run build:wasm # WASM modules and bindings into packages/wasm/gen
node --run build:wallet # the wallet into apps/wallet/dist
node --run build:dashboard # the dev dashboard into apps/dev-dashboard/dist
node --run build:website # dassie.land into apps/website/dist
node --run build:e2e # target/e2e/dassie with the wallet built in, the fake XRP Ledger and the release tool
node --run check # fmt (dprint), lint (oxlint), type check (tsc)
node --run test # the apps', packages' and scripts' tests
node --run test:e2e # WASM vectors, STREAM interop, browser, end to end
node --run ci runs all the Node steps in order, building what they need first. If you change proto/, run node --run gen in packages/rpc; a test fails until the generated code matches.
The XRPL end-to-end tests run against the node's fake ledger only; those on the hub scenario found a settlement hub of their own on every run, which must stay off the public testnet.
The updater's tests need the test key built in: cargo test -p dassie-cli --features test-key (the Nix check does this). The install tests run the installer and the updater on four distributions in Docker. They need privileged containers and mkcert:
nix build .#install-test-mirror -o install-test-mirror
DASSIE_INSTALL_MIRROR=$PWD/install-test-mirror DASSIE_INSTALL_DISTROS=debian \
node --test tests/e2e/install_test.ts
You can run a node on high ports as yourself, and configure it from another terminal:
DASSIE_STATE_DIRECTORY=/tmp/dassie DASSIE_IPC_SOCKET_PATH=/tmp/dassie.sock \
target/e2e/dassie daemon --http-port 8080 --https-port 8443
DASSIE_IPC_SOCKET_PATH=/tmp/dassie.sock target/e2e/dassie init --hostname localhost --skip-acme
Then open the setup URL it prints. The certificate stays self-signed until dassie init obtains a real one, which needs a public hostname with port 80 reachable.
To make a few such nodes find each other locally, set DASSIE_PEER_ALLOW_PRIVATE=1 so they may reach each other on localhost, DASSIE_EXTRA_CA_FILE to the CA their certificates come from, and DASSIE_DEV_TIMINGS=1 so the protocol polls and settles every second. Keep such nodes on loopback with --listen 127.0.0.1; the daemon warns about either flag when it listens anywhere else. node --run dev, below, does all of this for you.
Releases are built by Nix on x86_64 Linux (the build):
nix build .#dassie-x64 (or just nix build) or .#dassie-arm64 makes a static binary with the wallet built in;nix build .#release makes the tree a release uploads to the mirror, with its flake for NixOS, without its signature.The wallet's build fetches its packages one by one with the hashes in pnpm-lock.yaml, so a change to the lockfile needs no hash updated by hand. For every tag v<version>, the release workflow runs the install tests on each distribution, boots the NixOS module in a VM (nix build .#nixos-test) and checks the static binaries (nix build .#static-binaries), then signs and publishes the release. CI on pull requests and main runs neither.
node --run dev (in nix develop, or with mkcert installed) runs a small network of nodes on your machine, with a dashboard to watch it:
crates/ or proto/ change, restarting the nodes with their data. The wallet and the dashboard are built with vite build --watch in development mode, into dist-dev/. The nodes serve that wallet (DASSIE_DEV_WALLET_DIR), so a reload shows your change.d1, d2, … at https://dN.localhost (or :500N directly). They use the original's vanity identities, mkcert certificates, and fixed exchange rates from a local rates server. Their data is in local/dev/. node --run cli -- dN <command> runs the CLI against one, for example node --run cli -- d1 init.two-nodes (the default), six-nodes and one-node run on the stub ledger, which never settles; hub founds a settlement hub on the XRP Ledger and settles over it, cross-currency pays between the stub and XRP in a hub of its own through a third node, and dsusd-owner-book and live-owner-books hold test DSUSD, or mainnet's XRP and RLUSD on a fake mainnet, in owner books that peer with nobody; two-hubs founds an XRP hub and a DSUSD hub and pays across them, market-maker and market-maker-two-targets run a market maker's node, in both hubs, against core nodes in both, and peer-swaps has two core nodes in both hubs that swap with each other once their holdings are skewed. The XRPL ones run on the fake XRP Ledger, which the dev environment builds with the node. The scenario you chose last runs again next time, or pick one with --scenario <id>.r restarts the nodes, q or ctrl+c quits.The first time, run mkcert -install so your browser trusts the certificates. On Linux, port 443 needs sudo sysctl -w net.ipv4.ip_unprivileged_port_start=0; without it, the dev server tells you and runs without the proxy. --port-offset, --state-dir, --no-proxy, --no-watch, --headless and --dassie <binary> are there for tests and special cases (see apps/dev/main.ts).
To work on the wallet alone, run node --run dev in apps/wallet and start a node with DASSIE_DEV_WALLET_DIR set to the absolute path of apps/wallet/dist-dev. DASSIE_E2E_WALLET_DIR does the same for the wallet's browser test, and DASSIE_E2E_SCREENSHOTS keeps a picture of each step of the wallet's and the dev environment's browser tests.
To work on the website, run node --run dev in apps/website, which rebuilds dist/ on every change, and node --run preview next to it to serve it. A push to main that changes what the site is built from publishes it to a bucket on GCP (deployment). The headers every response gets, the CSP among them, are in its headers.json.
The code follows a pragmatic TigerStyle and is built for deterministic simulation testing (tigerstyle, Engineering): the core crates never read a clock or the OS RNG, bound every table and queue, and check their invariants after every step of their simulations.
Apache-2.0, see LICENSE.
verify-installcrates/dassie-release | What a release is and how to trust one: versions and channels, the mirror's layout, SHA256SUMS and its minisign signature |
crates/dassie-sim | Runs the simulations from any seed, to replay and explain failures: dassie-sim run peer 0..1000, dassie-sim explain peer 42 (random-seed-simulations); a developer tool, never released |
nix/release.nix | The release builds: the wallet, static binaries for x64 and arm64, their bundles, the mirror tree, and the install tests' releases |
resources | What a release ships beside the binary: the systemd units (the node, its sockets and the daily updater) and install.sh, and the release key (minisign.pub) |
scripts/build-wasm.ts | Builds the WASM modules and their bindings (node --run build:wasm) |
scripts/release.ts | Signs a built release for the mirror and checks it with dassie-release verify, as the release workflow does |
proto/dassie/owner/v1 | The RPC contract between the node and its owner's clients |
proto/dassie/dev/v1 | The dev environment's control channel, between the dev server and its dashboard |
apps/wallet | The wallet (Solid 2), embedded in the binary: every page of the original, with live data, plus grants and consent for Open Payments |
apps/dev | node --run dev: the scenarios on real binaries, certificates, vanity identities, the dev server with its SNI proxy and relay, a rates server; also the scenario library the end-to-end tests use |
apps/dev-dashboard | The dev dashboard (Solid 2): the network, every node's log, the scenarios, each node's debug pages |
apps/website | dassie.land: the home page, the Quick Start, the NixOS guide, and the STREAM toy, which runs dassie-stream in WASM over a simulated network; main publishes it to GCP |
packages/ui | The Solid components the wallet, the dev dashboard and the STREAM toy share: amounts, the log viewer, the network graph, the debug pages, the router, live data, the theme |
packages/wasm | The core crates in WASM (bound by crates/dassie-wasm), one build for browsers and Node: identity, and STREAM with ILP's packets, as two modules |
packages/rpc | The client for the RPC contract, over WebSocket (browsers) or the IPC socket (Node) |
tests/interop | STREAM interop with ilp-protocol-stream, natively and in WASM; WASM vectors; the browser test |
tests/e2e | End-to-end tests on real binaries: a fresh install through dassie init and browser setup, with Pebble as the ACME CA; a BTP client paying the node's STREAM receiver; the two-nodes and six-nodes scenarios; Open Payments, including a third party's full flow with consent in the wallet; settling over a settlement hub on the XRP Ledger, owner books and cross-currency payments; the wallet's pages in a browser; node --run dev and its dashboard; the installer and the updater on Debian, Ubuntu, Fedora and CentOS, in containers; dassie.land and its STREAM toy in a browser, under the site's CSP |
testdata/original | Generator for the OER, ILP and STREAM golden vectors, run against the original |
testdata/identity | Generator for the identity golden vectors, phrases as read and every key a phrase derives, computed apart from Dassie's code |
testdata/feed | The snapshot's golden vectors, from cargo run -p dassie-feed --example snapshot_vectors |
testdata/hub | A settlement hub's golden vectors, from cargo run -p dassie-hub --example hub_vectors |
testdata/xrpl | Generator for the XRPL golden vectors, run against xrpl.js |
testdata/tls | The tests' TLS certificate for localhost and its CA, which Pebble (Let's Encrypt's test server) serves its API with, as do the nodes and mirrors of the tests; and Pebble's configuration |
testdata/release | The release test key and what minisign signs with it |