Integration adapters connecting Stellar compliance primitives to SEP-10 auth, sanctions oracles, and Horizon/RPC event streams.
Having on-chain compliance controls — allowlists, denylists, jurisdiction flags — is only half the problem. To actually run a compliant app on Stellar, teams still need to verify who's making a request, feed real-world watchlist data into those on-chain controls, and react when compliance state changes. That's a lot of repetitive off-chain glue: SEP-10 authentication, syncing external sanctions data, listening for contract events. This repo provides ready-made, well-tested adapters for that glue layer so app developers don't have to hand-roll it for every project.
This repo consumes the on-chain Soroban contracts defined in
compliance-primitives —
allowlist-token, denylist-gate, and jurisdiction-flag. It does not vendor or reimplement
those contracts; adapters here call into deployed instances of them (or listen for events they
emit) via @stellar/stellar-sdk. If you're looking for the on-chain contract logic itself, that
lives in the other repo.
This is a monorepo with three independent packages and a runnable examples directory:
sep10-auth— builds and verifies SEP-10 web authentication challenges, so you can confirm a request is really coming from the Stellar address it claims to be from before running any compliance check. Includes an example Express middleware.sanctions-oracle— aSanctionsProviderinterface plus a mock implementation and a sync script that pushes flagged addresses into a deployeddenylist-gatecontract instance.horizon-listener— an example service that polls Soroban RPC fordenylist-gate/allowlist-tokencontract events and re-emits them to a webhook, as a reference pattern for reacting to on-chain compliance state changes.
Each package has its own README.md, tests, and package.json.
Runnable examples live under examples/. Each is a self-contained npm workspace
that wires the adapters together in a realistic scenario.
An Express server exposing a single gated route (GET /gated) that composes sep10-auth and
sanctions-oracle end-to-end:
createSep10Middlewarefromsep10-authvalidates the caller's SEP-10 Bearer token and attaches the verified Stellar address toreq.stellarAddress. Unauthenticated requests get a 401 before the sanctions check ever runs.- The verified address is checked against a
SanctionsProvider. A flagged address gets a 403; a clean address gets a 200 with the verified address echoed back.
To run it locally (requires a real SERVER_ACCOUNT_ID env var pointing to a Stellar G... key):
npm install --workspace=sep10-sanctions-gate
SERVER_ACCOUNT_ID=G... HOME_DOMAIN=localhost:3000 npm run dev --workspace=sep10-sanctions-gateTo run its tests:
npm test --workspace=sep10-sanctions-gateThe diagram below shows how the three packages fit together in a typical deployment alongside the on-chain contracts from compliance-primitives.
flowchart TD
Client(["Client\n(Stellar wallet)"])
App["Your API / App Server"]
Sep10["sep10-auth\n(challenge · verify · middleware)"]
Oracle["sanctions-oracle\n(SanctionsProvider · syncSanctionsToDenylist)"]
Listener["horizon-listener\n(RpcEventSource · HorizonListener)"]
Watchlist[["External watchlist\n/ sanctions data source"]]
Webhook["Your webhook handler\n(re-acts to on-chain state changes)"]
Soroban(["Soroban RPC\n(soroban-testnet / pubnet)"])
Contracts["compliance-primitives contracts\n(denylist-gate · allowlist-token · jurisdiction-flag)"]
Client -->|"1 · request challenge"| Sep10
Sep10 -->|"2 · unsigned challenge XDR"| Client
Client -->|"3 · signed challenge XDR"| Sep10
Sep10 -->|"4 · verified Stellar address\n(req.stellarAddress)"| App
App -->|"5 · compliance check\n(is address denied / allowed?)"| Contracts
Watchlist -->|"flagged addresses"| Oracle
Oracle -->|"add_to_denylist(address)\nvia Stellar SDK"| Soroban
Soroban -->|"transaction applied"| Contracts
Contracts -->|"emit AddedToDenylist /\nAddedToAllowlist events"| Soroban
Listener -->|"poll getEvents (cursor-based)"| Soroban
Listener -->|"HTTP POST event payload"| Webhook
Webhook -->|"trigger re-sync /\nalert / audit log"| Oracle
Flow summary:
sep10-authauthenticates the caller's Stellar address before any compliance check runs.- Your app server queries the on-chain
denylist-gate/allowlist-token/jurisdiction-flagcontracts to decide whether the authenticated address is permitted. sanctions-oracleperiodically pulls flagged addresses from an external watchlist and pushes them intodenylist-gatevia Soroban RPC.horizon-listenerpolls Soroban RPC for contract events (e.g.AddedToDenylist) and forwards them to a webhook, allowing downstream services to react to on-chain state changes in near-real-time — for example by triggering a freshsanctions-oraclesync.
Requires Node 20+.
git clone https://github.com/stellar-compliance-kit/compliance-adapters.git
cd compliance-adapters
npm install
# run every package's test suite
npm test
# run a single package's tests
npm test --workspace=sep10-auth
# build all packages
npm run build
# build a single package
npm run build --workspace=sep10-authThe root-level npm run build script runs the TypeScript compiler across all three packages
(sep10-auth, sanctions-oracle, and horizon-listener) using npm workspaces. Each package
produces compiled output in its dist/ directory. This is equivalent to running tsc -p . in
each package independently.
If you only need to work on one adapter:
# Install and test sep10-auth
npm install --workspace=sep10-auth
npm test --workspace=sep10-auth
# Install and test sanctions-oracle
npm install --workspace=sanctions-oracle
npm test --workspace=sanctions-oracle
# Install and test horizon-listener
npm install --workspace=horizon-listener
npm test --workspace=horizon-listenerA runnable example app lives at examples/full-stack-demo. It demonstrates the end-to-end flow:
sep10-authprotects a/privateroute with SEP-10 bearer token verificationhorizon-listenerpolls Soroban RPC contract events and forwards them to an internal webhooksanctions-oraclesync logic is exposed via a/syncendpoint using the mock provider
To run it:
npm install
npm run build --workspaces --if-present
cd examples/full-stack-demo
npm install
npm startThe demo starts an Express server on http://localhost:3000 and exposes:
GET /publicGET /privateGET /syncGET /challenge
You can configure runtime values with environment variables such as SERVER_ACCOUNT_ID, RPC_URL, and CONTRACT_ID.
To try the sanctions sync script against testnet in dry-run mode (no transactions submitted, just logs what it would do):
npm run build --workspace=sanctions-oracle
npx compliance-adapters sync-sanctions --dry-run --addresses ./sanctions-oracle/test/fixtures/addresses.jsonRun npx compliance-adapters --help for available commands and
npx compliance-adapters sync-sanctions --help for command-specific options.
See each package's README for its full API and configuration options.
See CHANGELOG.md for release notes and version history.
Contributions welcome — see CONTRIBUTING.md for setup and workflow details.
This monorepo uses Changesets for coordinated package releases and changelog generation.
To create a release note for changed packages:
npm run changesetThen to version packages and generate changelog entries:
npm run versionAfter review, publish releases with:
npm run releaseChangesets will manage consistent version bumps across sep10-auth, sanctions-oracle, and horizon-listener.
This repo participates in the Drips Wave Stellar Program and is
deliberately kept full of small, well-scoped issues. Open issues are labeled by complexity
(complexity: trivial, complexity: medium, complexity: high) so you can pick work that
matches your available time — trivial issues are also tagged good first issue and don't require
Soroban expertise.
See SECURITY.md for the trust model behind each package and how to report a vulnerability.
The sanctions-oracle package's bundled mock provider is a placeholder for development and
testing only. It contains no real sanctions or watchlist data and must not be used as an
actual compliance data source in production. Integrate a real, licensed sanctions/watchlist
data provider before relying on this package for real compliance decisions.
This repo uses package-scoped labels to make triage easier. The intended labels are:
package: sep10-auth— issues specific to thesep10-authpackagepackage: sanctions-oracle— issues specific to thesanctions-oraclepackagepackage: horizon-listener— issues specific to thehorizon-listenerpackage
A helper script scripts/apply-package-labels.js can create these labels and apply them to open
issues using simple heuristics. It requires a GITHUB_TOKEN with repo access to run.