BridgeSafe runs on Coston2 and XRPL Testnet. This document covers two different audiences:
- Operator safety — what running this repo does to the machine it runs on. Read this first if you are about to run the stack.
- Product security model — what BridgeSafe does and does not guarantee as a
system. See also
docs/threat-model.md.
Every network this repo touches is a test network with valueless tokens:
| Component | Network | Token | Source |
|---|---|---|---|
| Flare contracts | Coston2 (chain ID 114) |
C2FLR | faucet |
| External payments | XRPL Testnet | test XRP | faucet |
There is no mainnet configuration in this repository. scripts/check-secrets.sh
fails the build if a Flare Mainnet or Songbird RPC URL, or an XRPL Mainnet
endpoint, appears anywhere in tracked files. Do not add one.
Never reuse a key that holds real funds for any variable in this project.
This is the only part of the stack that accepts traffic from the internet, and you should understand it before running it.
Flare's TEE infrastructure has to reach your extension proxy to deliver
instructions, so ext-proxy's external port (6674) must be publicly
reachable. scripts/tunnel.sh opens a cloudflared
quick tunnel that maps a random *.trycloudflare.com hostname to
127.0.0.1:6674.
What this means concretely:
- Only port 6674 is published. The tunnel is a userspace process. It does not modify your firewall, does not open any other port, and gives no inbound route to anything else on the machine. Nothing else in the stack is reachable.
- Anyone who learns the URL can call the proxy API. The hostname is random and unadvertised, but it is not a secret and not authenticated. Flare's own documentation carries the same warning. On a testnet proxy the realistic blast radius is nuisance traffic and junk instruction results — there are no funds and no private keys behind that port.
- It is not persistent. The tunnel lives only as long as the
cloudflaredprocess.scripts/tunnel.shruns in the foreground so closing it closes the exposure, andscripts/stop-services.shkills any stray tunnel.
Operator rule: start the tunnel when you begin a demo or test run, stop it when you finish. Do not leave it running unattended overnight.
If you would rather not expose anything at all, everything except the enclave
round trip can be exercised with no tunnel: cd contracts && forge test covers
the full contract lifecycle against mocked enclave results, and
cd extension/go && go test ./... covers the policy engine and the XRPL signer.
Only the live FCC path needs the proxy to be reachable.
All local infrastructure is published to 127.0.0.1 explicitly, never to
0.0.0.0. Nothing below is reachable from your LAN, let alone the internet:
| Service | Binding | Purpose |
|---|---|---|
| MySQL (C-chain indexer) | 127.0.0.1:3306 |
indexer storage |
| Redis | 127.0.0.1:6382 |
proxy queue |
| ext-proxy internal | 127.0.0.1:6673 |
container-to-container |
| ext-proxy external | 127.0.0.1:6674 |
tunnelled (see 1.2) |
| Extension TEE server | container-internal only | not published |
| TEE sign port | container-internal only | not published |
| Types server | 127.0.0.1:8100 |
decoding for the UI |
| Frontend dev server | 127.0.0.1:3000 |
UI |
scripts/check-bindings.sh resolves ${VAR:-default} in every compose file and
fails if any published port would land on anything but loopback. It runs as part
of scripts/preflight.sh and as a pre-push hook.
This check earned its keep immediately: Flare's upstream fce-sign compose
defaults the proxy ports to 0.0.0.0, which publishes them to the whole local
network. BridgeSafe binds them to 127.0.0.1 and reaches them through the
tunnel instead.
Four distinct keys exist. They are deliberately separated so no single one is both privileged and exposed:
| Key | Held by | Risk if leaked |
|---|---|---|
| Coston2 deployer | your .env, on disk |
loss of testnet C2FLR; attacker could own your test contracts |
| Proxy key | your .env, on disk |
can relay instructions for your extension on testnet |
| XRPL treasury key | generated inside the enclave; never leaves it | — |
| Broadcaster | your .env, on disk |
can submit already-signed blobs; cannot create payments |
The design point worth noting: the broadcaster never holds the treasury key. It receives an already-signed transaction blob and can only submit it. Stealing the broadcaster key does not let you move treasury funds.
Rules the repo enforces:
.envand every*.key/config/extension.env/ generated proxy TOML are gitignored (see.gitignore).- Only
.env.examplefiles — containing placeholders, never values — are tracked. scripts/check-secrets.shscans the staged diff for private-key-shaped strings (64-hex,0x-prefixed 64-hex, XRPL family seedss..., PEM headers) and blocks the commit. Install it as a pre-commit hook withscripts/install-hooks.sh.- Generate fresh keys with
scripts/new-testnet-keys.sh. Do not paste in an existing key.
Be aware of what executes on your machine:
- Flare's container images (
ext-proxy, TEE node) come from the Flare Foundation's registry viaextension/docker-compose.yaml. The FCC stack cannot run without them, and BridgeSafe does not control their contents. They are referenced by tag, as Flare publishes them — if you want stronger guarantees, resolve them to digests before a long-lived deployment. - The extension image is built locally from this repo's source — it is not pulled. That is also what makes its code hash reproducible.
- The C-chain indexer is built from Flare's published source, pinned by
INDEXER_REFininfra/versions.env. It currently tracksmain; pin it to a commit before relying on it. - Go and npm dependencies are pinned by
go.sumandpackage-lock.json.
Everything runs in Docker containers with no bind-mounts into your home directory — only into this project folder.
scripts/teardown.sh stops all containers, deletes the local volumes (indexer
DB, redis), removes locally-built images, and kills any tunnel. On-chain testnet
state cannot be deleted, but it holds nothing of value.
Policy-controlled and independently verifiable cross-chain execution, using Flare consensus for authorization and attested confidential compute for signing.
Operators should size the guarantee precisely, so four points are worth stating directly.
- The trust base is named, not assumed away. It includes TEE hardware, the
cloud platform, Flare's FCC relays, this application's code, XRPL availability,
and the contract implementation.
docs/threat-model.mdsets out what a compromise of each would and would not achieve. - Scope is treasury execution and settlement proof. There is no liquidity pool, no wrapped token and no solvency model, because BridgeSafe moves native XRP under policy rather than issuing a claim on it.
- Confidentiality is pre-execution. XRPL is a public ledger, so a settled payment's recipient and amount are observable afterwards. What stays sealed is the pending instruction, the treasury policy detail and batch contents — the disclosure that actually matters — together with key usage inside the enclave.
- Attestation mode is a deployment choice. The instruction flow, on-chain
registration and signature verification are identical either way.
SIMULATED_TEE=true(MODE=1) is the development default; genuine AMD SEV attestation runs on a GCP Confidential Space VM. Seedocs/threat-model.mdfor which guarantees that adds.
The contracts enforce these regardless of what the enclave or the relayer does:
- A request cannot reach
SETTLEDwithout an FDC proof of the actual XRPL payment. - The proof must match the expected source account, destination account, exact amount, and the BridgeSafe request ID carried in the payment memo.
- An XRPL transaction ID can settle at most one request, ever.
- Requests carry sequential nonces and hard expiries.
- Only the registered treasury owner can initiate a payment.
- The enclave signs only a typed XRPL
Paymentstructure. There is no arbitrary-message signing endpoint.
This is hackathon software with no production deployment. Open a GitHub issue for anything you find.