Sscalascript.dev

What ScalaScript can do — capabilities & the provider-independence pattern

A map of our own capabilities, written to make the project's design philosophy concrete and to point at where it can go next. Everything below is grounded in code that exists today; file pointers are given so claims are checkable.


1. The one big idea: portable reference + pluggable native provider

The organizing principle of the whole codebase is a single repeating shape:

> A portable reference implementation (pure ScalaScript / BigInteger / JDK, compiles to every platform) > plus a registry SPI where a platform may register(...) a native implementation that is then used > transparently — selected at runtime (or compile-time). The reference is the always-available correctness > fallback; the native impl is the fast path. Where no native provider exists, the reference still runs > everywhere, so the pattern degrades gracefully.

This is realized at every layer — compiler, runtime services, concurrency, crypto, blockchain, payments, UI — and it's the same shape you can apply to anything new. FROST-Ed25519 is the textbook example (see §6).


2. Compile targets (one source → many platforms)

One normalized IR, many Backends (runtime/backend/spi/.../Backend.scala), each contributing its own intrinsics; discovered via META-INF/services (in-process) or .sscpkg (subprocess).

TargetWhat it is
InterpreterDirect-style evaluator + numeric JIT; the reference semantics every codegen target must match; powers REPL / serve / ssc run.
JVMEmits Scala for scala-cli/JVM; CPS transform for effects.
JS / Node / Scala.js SPAEmits JS/ESM (+ a JS runtime preamble: signals, graphql, indexeddb, mcp).
RustEmits Rust; one-shot algebraic effects via tagless-final traits.
WASMScala.js → .wasm; lowers @wasm(...) externs.
Spark / Kafka-Streams / Flink (+Beam)The same stream program emitted to multiple distributed engines via a shared distributed-streams abstraction.
Source embeddersscala/javascript/rust/xml/sql/html/css/transaction fenced blocks.

one reference, many native execution providers is already true at the compiler boundary.


3. Language / runtime features

free-monad trampoline (multi-shot resume), each backend lowers it natively (one-shot on Rust).

as pluggable BlockForm handlers over a typed SpiValue, not hardcoded in the interpreter.

detection, Bully + Raft leader election.

theming, routing.


4. The financial / crypto stack (what's already here)

A broad, plugin-architected stack — and almost all of it is SPI + pure reference/mock + thin SDK-free adapters.

CryptographyCryptoBackend SPI (payments/crypto/spi/): sign/verify/derivePublic/recoverPublic (secp256k1 ecrecover), hash/hmac, BIP-32/SLIP-0010 HD derivation, pbkdf2/argon2id/hkdf, AES-GCM, ChaCha20-Poly1305, X25519. Curves: secp256k1, Ed25519, P-256 (Sr25519/BLS12-381 enumerated, not yet implemented). Two interchangeable, byte-for-byte-compatible backends: BouncyCastle on JVM (ServiceLoader) and @noble/* on JS (register). Plus FROST-Ed25519 — our own pure threshold-signing module (cryptoFrost, no main dependency; see frost-ed25519-usage.md).

WalletsVault/RawSigner SPI; our own BIP-39 (embedded wordlist), encrypted local vault (Argon2id + AES-256-GCM through the crypto SPI), EOA + ERC-4337 account abstraction (own UserOp hashing, passkey/WebAuthn owner), Ledger (own APDU/app codecs; vendor lib only for the USB byte pipe), Trezor, and connectors (EIP-1193, WalletConnect v2 with own envelope crypto over X25519+ChaCha20, Wallet Standard). MPC vaults (Fireblocks/Coinbase/Lit/ZenGo) are thin clients to remote external providers behind a RemoteSigningClient SPI — and walletVaultMpcFrost is the in-house implementation of that same seam: our own FROST-Ed25519 threshold signing run locally (no external provider), so McpVault("…", new FrostSigningClient(Seq(quorum))) is a complete threshold wallet with no new vault type.

BlockchainsChainAdapter SPI (payments/blockchain/spi/), one impl per chain, all signing through the crypto SPI:

BouncyCastle (Blake2b / secp256k1-DER / RIPEMD-160) directly, so not yet backend-agnostic.

broadcast client yet.

Payments / servicesPaymentProvider SPI (Stripe/PayPal/Braintree/Adyen/Checkout/Square + a mock, all ujson-over-HTTP, no vendor SDKs); BankRailsProvider (~15 rails: SEPA/ACH/FedNow/Pix/SWIFT/UK-FPS/etc.); x402 HTTP-402 micropayments (core + server + client + facilitators + pluggable nonce/queue stores); off-chain micropayment channels (threshold batching, probabilistic lottery tickets, EVM state channels, Cardano Hydra); FX (ECB / OpenExchangeRates); Money + WebhookReceiver substrate; W3C Payment Request (Apple/Google Pay token decryption). Adjacent: tax, compliance/AML.


5. The provider-independence machinery (how substitution works)

The same SPI shape recurs; to add provider-independence to anything, copy one of these:

SeamMechanismReference impl
Backendintrinsics per target; ServiceLoader / .sscpkg discoverythe interpreter is the canonical semantics
CryptoBackendregister / get registry; ServiceLoader (JVM) / init-block register (JS)BouncyCastle (JVM), @noble (JS)
Ed25519Ops (FROST)register / current / reset; even randomBytes/sha512 on the seampure BigInteger + own Sha512, zero deps
HttpServerSpiclasspath fallback + opt-in providers + setHttpServerBackendJDK com.sun.net.httpserver (zero-dep)
Vault / RawSignerper-vault impls behind one traitin-memory + encrypted-local
ChainAdapter / BlockchainCAIP-2 registry, ServiceLoader / registerEVM, Solana (pure Scala)
PaymentProvider / BankRailsProvider / FxProviderServiceLoader-discoveredmock PSP, in-memory stores
@jvm/@js/@rust/@wasm externs + .ssclib glueone annotation → native host expr per targetthe ScalaScript body is the fallback

ssc emit-lib turns the idea outward: a ScalaScript feature is emitted as a native host package — npm ESM, sbt jar, Rust crate, Java/Maven — so even non-ScalaScript consumers get the portable implementation with no ScalaScript runtime at their edge (first feature shipped: optics, all four hosts). And the package registry (pkg: + LocalRegistry + the new RemoteRegistry/FileRegistry/RegistryHttpServer) is the distribution channel for those .sscpkg libraries.


6. The FROST template — and where to copy it

FROST is the cleanest instance of the pattern and the model for new "own implementation, provider-independent" features:

FrostKeygen + FrostSign (the protocol) — all over BigInteger/Long.

register(native) swaps in BouncyCastle/@noble/Rust transparently, reset() restores the reference.

To make any capability provider-independent the FROST way: (1) write a small pure reference; (2) define a trait for its primitives + a register/current/reset registry with the reference as default; (3) route all callers through current; (4) gate correctness against an existing implementation; (5) optionally add native backends per platform.


7. Future / brainstorm — what we could do directly, with minimal dependencies

Forward-looking. The thread: prefer our own dependency-light reference behind a provider-independent seam, with native backends only as an opt-in fast path.

> This brainstorm now has a committed, grouped home. The full plan — what each item is, why, where it > applies, and the slice-by-slice engineering plan — lives in one place so it isn't scattered: > - explainer (what / why / where / benefit): crypto-finance-roadmap.md > - engineering plan (slices, file pointers, acceptance): ../specs/crypto-finance-roadmap.md > - queued work: the "Crypto/finance roadmap" sections of SPRINT.md (near-term) and BACKLOG.md (later). > > The summary below stays as a quick map; the two files above are authoritative. Two facts were corrected > against the code while grouping this: Keccak-256 and RIPEMD-160 already exist in the crypto SPI — Blake2b > is the one missing hash; and Solana already broadcasts (via the generic RPC seam) — its only gap is a > turnkey client module like EVM's.

Cryptocurrencies we could support with little/no heavy deps

The offline surface (addresses, tx building, signing payloads) of most chains is just encoding + a signature, and we already prove this is doable in pure Scala for EVM and Solana (both cross-compile to JS today). The heavy parts are only (a) a few curve/hash primitives and (b) RPC nodes.

remaining gap is a Solana RPC client (EVM has one). Low effort, high leverage.

Blake2b-224 / secp256k1-DER / RIPEMD-160 / Ed25519. Route those through the CryptoBackend SPI (add Blake2b

and they'd cross-compile and shed the hard dependency.

RIPEMD-160, secp256k1 scalar/point math, BIP-32 HD on JS (currently JVM-only). Each is a bounded, vector-gateable reference that removes a vendor dependency and unlocks a platform.

(threshold Schnorr for Bitcoin Taproot), threshold ECDSA (GG/Lindell for legacy Bitcoin/Ethereum), MuSig2 (Bitcoin multisig as a single key), VRFs and BLS (validator/aggregate signatures). All fit the *Ops seam pattern with a pure reference + optional native fast path.

mostly "another ChainAdapter" once the primitive is in the crypto SPI.

Services / protocols we could implement directly (not only currencies)

The same "own reference behind a seam" approach applies beyond chains:

PASETO / JWT / COSE token signing+verify — all small, pure, vector-gateable.

them); a pure Noise framework is a short hop.

for arbitrary secrets (we already have the scalar-field Shamir in FROST — generalize it).

inventing a new scheme is "another ChannelProvider."

layer** (we have cluster + actors) — all reference-first, provider-pluggable.

Why this is realistic here

Three things make the above cheap: (1) the SPIs already exist, so new work is "implement a trait," not "design an architecture"; (2) cross-compilation is free once the code avoids platform-only APIs (the FROST SHA-512/RNG lesson); (3) every reference can be gated against an existing implementation for correctness, then shipped with native backends as an opt-in. The endgame is a self-contained, vendor-optional financial / crypto runtime that runs identically on JVM, JS, Rust, and WASM — with native acceleration wherever it exists.