> ## Documentation Index
> Fetch the complete documentation index at: https://docs.getsly.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Settlement rails

> Sly is rail-neutral by design. Two production rails today — Base (EVM L2) and Stellar (Soroban) — with selectRail() as the auditable decision function that picks between them.

<Note>
  **Both rails are live in sandbox today.** Base Sepolia (`eip155:84532`) and
  Stellar testnet (`stellar:testnet`) settle real USDC through the same Sly
  governance loop. Mainnet for both rails (Base + Stellar pubnet) ships with
  production access — [contact us](mailto:hello@getsly.ai) once you've worked
  through the [production access](/get-started/production-access) checklist.
</Note>

## What "rail-neutral" actually means

The four Sly primitives — **Identity, Governance, Policy, Receipts** — work the same on every rail. A receipt minted on Base and a receipt minted on Stellar carry the same identity fields, the same canonical encoding, the same HMAC algorithm, and the same offline verifier. The rail is a setting, not an integration.

```
   Identity        Governance       Policy         Receipts
       │               │               │              │
       ▼               ▼               ▼              ▼
┌─────────────────────────────────────────────────────────┐
│         Sly rail-neutral protocol surface               │
└─────────────────────────────────────────────────────────┘
       │                                       │
       ▼                                       ▼
  ┌────────┐                              ┌─────────┐
  │  Base  │                              │ Stellar │
  │   L2   │                              │ Soroban │
  └────────┘                              └─────────┘
   EIP-191                                   SEP-10
   USDC                                      USDC
   basescan                                  stellar.expert
```

The same `/v1/agents/:id/...` endpoints, the same dashboard surfaces, the same receipt shape. Only `chain` and the address format differ.

## The two production rails

<CardGroup cols={2}>
  <Card title="Base (EVM L2)" icon="ethereum" href="/settlement/base">
    Coinbase's L2 on Optimism Stack. Mid-large settlements. EVM ecosystem. \~2s finality. EIP-191 key-control proofs.
  </Card>

  <Card title="Stellar (Soroban)" icon="star" href="/settlement/stellar">
    Stellar Development Foundation's smart-contract chain. Agent micro-payments. Fee-sponsored settlement. \~5s finality. SEP-10 key-control proofs.
  </Card>
</CardGroup>

## Side by side

| Property               | Base                                        | Stellar                                                           |
| ---------------------- | ------------------------------------------- | ----------------------------------------------------------------- |
| **Sandbox network**    | `eip155:84532` (Base Sepolia)               | `stellar:testnet`                                                 |
| **Mainnet network**    | `eip155:8453` (Base)                        | `stellar:pubnet`                                                  |
| **x402 SDK**           | `@x402/evm`                                 | `@x402/stellar`                                                   |
| **Settlement latency** | \~2 s                                       | \~5 s                                                             |
| **Per-tx fee**         | \~\$0.001 gas (paid in ETH)                 | \~\$0.00001 — **fees sponsored** by facilitator                   |
| **USDC contract**      | `0x036…CbB4` Sepolia · `0x833…9913` mainnet | SAC `CBIELTK…DAMA` testnet · SAC `CCW67TSZ…SJMI75` mainnet        |
| **Address format**     | `0x` + 20 bytes (EIP-55 checksum)           | `G…` 56-char base32 (Ed25519 pubkey)                              |
| **Key-control proof**  | EIP-191 signed challenge                    | SEP-10 web auth challenge                                         |
| **Block explorer**     | basescan.org / sepolia.basescan.org         | stellar.expert                                                    |
| **Receipt anchoring**  | EAS (Ethereum Attestation Service)          | AttestProtocol on Soroban — wired, pending upstream contract init |
| **Fiat off-ramp**      | Coinbase, US-centric                        | 475K+ MoneyGram locations · Yellowcard · Flutterwave              |
| **Best for**           | Settlement size \$0.10+, EVM ecosystem      | Sub-cent agent calls, EMEA/Africa fiat edge                       |

**Picking a rail by hand?** The short rule: Base for mid/large settles in EVM-heavy stacks; Stellar for sub-cent agent micropayments and emerging-markets edges. Better: let `selectRail()` pick for you.

## `selectRail()` — the decision function

Rail-neutrality at the platform level becomes a single pure function at the code level. `selectRail()` takes an intent, the tenant's allow-list, the agent's allow-list, and current network conditions, intersects them, scores the survivors, and returns the chosen rail plus the human-readable reasons it won.

```ts theme={null}
import { selectRail } from '@sly_ai/sdk';

const decision = selectRail({
  intent: { kind: 'x402.pay', target_currency: 'USDC', max_amount: 0.01 },
  tenant: { allowed_rails: ['stellar:testnet', 'base-sepolia', 'base'] },
  agent:  { allowed_rails: ['stellar:testnet', 'base-sepolia'] },
});

// decision = {
//   chosen: 'stellar:testnet',
//   reasons: ['picked:fee_sponsored', 'picked:cheaper_fee', 'picked:faster_finality'],
//   rejected: []
// }
```

The reasons land in the receipt's `rail_selection` field. The receipt explains itself.

```json theme={null}
{
  "receipt_id": "rcpt_…",
  "chain": "stellar:testnet",
  "rail_selection": {
    "chosen": "stellar:testnet",
    "reasons": ["picked:fee_sponsored", "picked:cheaper_fee", "picked:faster_finality"],
    "rejected": []
  },
  …
}
```

### The decision tree

```
intent
  │
  ▼
intersect tenant.allowed_rails ∩ agent.allowed_rails
  │
  ▼
filter candidates by intent.target_currency
  │       ┌─ USDC available on both? continue
  │       └─ only one supports it? short-circuit, reason: only_currency_support
  ▼
score each remaining candidate:
  • +1 if facilitator sponsors fees on this network
  • +1 if known-cheaper per-tx fee for this intent size
  • +1 if known-faster finality
  • -1 if mainnet but tenant lacks production access
  │
  ▼
pick highest score (deterministic tiebreak by network alphabetical)
  │
  ▼
return { chosen, reasons, rejected }
```

Full source: `apps/api/src/services/x402/select-rail.ts`. Pure function, fully unit-tested, 0 side effects — replayable forever.

## Allow-lists — two levers

Every agent's effective rail allow-list is `tenant.allowed_rails ∩ agent.allowed_rails`. Two operators can independently dial down (never up) the rails their agents can settle on.

```bash theme={null}
# Tenant-wide allow-list (Sly Operator role required)
PATCH /v1/tenant/allowed-rails
{ "allowed_rails": ["stellar:testnet", "base-sepolia", "base"] }

# Per-agent allow-list (Operator + Owner roles)
PATCH /v1/agents/:id
{ "allowed_rails": ["stellar:testnet"] }
```

Empty array (`[]`) means "no restriction" — defaults to platform-wide allow. Both fields render as **Allowed Rails** chip panels in the dashboard for the relevant scope.

## Receipt shape, across rails

Every Sly governed payment — Base or Stellar — produces the same receipt envelope. Only `chain`, address format, and a couple of optional rail-specific fields vary.

```json theme={null}
{
  "receipt_id":         "rcpt_…",
  "chain":              "<eip155:84532 | stellar:testnet>",
  "asset":              "USDC",
  "amount":             "0.0100000",
  "from_address":       "<0x… | G…>",
  "to_address":         "<0x… | G…>",
  "agent_id":           "…",
  "agent_name":         "stellar-demo-buyer",
  "agent_kya_tier":     2,
  "agent_chain_proof":  "<sep10 | eip191 | asserted>",
  "agent_custody_provider": "<env_key | oz_soroban | safe_evm>",
  "rail_selection":     { "chosen": "…", "reasons": [...], "rejected": [...] },
  "policy_decision":    { "decision": "approve", … },
  "signature_mode":     "witness-hmac",
  "canonical_encoding": "json-sort-keys-v1",
  "signature":          "…HMAC-SHA256(json-sort-keys-v1)"
}
```

Every field above is in the canonical encoding. Every field is in the HMAC. Tampering with the rail, the address, the agent, the custody provider, or the decision invalidates the signature on either rail.

## Offline verification works the same

The `verify-offline.mjs` script in `examples/stellar-demo/` is rail-agnostic — it re-derives the canonical encoding and HMAC, then byte-compares. Works for both Base and Stellar receipts because the canonical encoding is `json-sort-keys-v1` regardless of `chain`.

```bash theme={null}
node verify-offline.mjs base-receipt.json
# → ✓ SIGNATURE VALID — receipt is verifiable offline.

node verify-offline.mjs stellar-receipt.json
# → ✓ SIGNATURE VALID — receipt is verifiable offline.
```

This is the layer that lets you audit a Sly settlement without trusting Sly's API at the moment of audit. Cryptography over wire.

## What's NOT yet at parity

Three places where Base is ahead of Stellar today, by design:

| Capability              | Base                                      | Stellar                                                                                                      | Status                               |
| ----------------------- | ----------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------ |
| Mainnet settlement      | Ships with production access              | Ships with production access                                                                                 | [Contact us](mailto:hello@getsly.ai) |
| Smart-account custody   | ERC-4337 (Safe / Pimlico paths) — partial | OZ Soroban smart accounts — receipt-side abstraction shipped, on-chain custody contract still being deployed | Work in progress                     |
| On-chain receipt anchor | EAS — production                          | AttestProtocol — wired but pending upstream contract init                                                    | Work in progress                     |

All three are coded against an abstraction; flipping one over is a config change, not a rebuild.

## Where to go from here

<CardGroup cols={2}>
  <Card title="Settling on Base" icon="ethereum" href="/settlement/base">
    Full guide — endpoints, EIP-191 binding, witness receipts, dashboard surface, EAS anchoring.
  </Card>

  <Card title="Settling on Stellar" icon="star" href="/settlement/stellar">
    Full guide — endpoints, SEP-10 binding, custody providers, AttestProtocol anchoring, the three identity layers.
  </Card>

  <Card title="x402 protocol" icon="bolt" href="/protocols/x402">
    The HTTP 402 micropayment protocol that both rails serve under the same Sly governance loop.
  </Card>

  <Card title="Production access" icon="key" href="/get-started/production-access">
    How to graduate from sandbox (both rails) to live mainnet settlement.
  </Card>
</CardGroup>
