# X-Multi Auth v1 — Multisig Authentication for the XRP Ledger

**Version:** 1.0.0-draft  
**Author:** X-Multi (xmulti.app)  
**Date:** April 2026  

---

## Problem

XRPL multisig wallets cannot log into dApps. Off-ledger message signing requires a single private key, but multisig accounts distribute signing authority across multiple signers. If the master key is blackholed (common for CTO projects), off-ledger signing is mathematically impossible.

This means community treasuries, DAO vaults, and CTO project wallets **cannot access creator dashboards, marketplaces, or any dApp that uses wallet-based authentication**.

## Solution

X-Multi Auth provides two complementary authentication methods:

### Method 1: On-Ledger Authentication

The vault sends a 0-drop XRP payment **to itself** with a structured Memo containing a session token. This transaction goes through the normal multisig approval flow — the required number of signers must approve it before it's submitted to the ledger.

Once on-chain, any dApp can verify the transaction to confirm the vault's signers authorized the session.

#### Transaction Format

```json
{
  "TransactionType": "Payment",
  "Account": "rVAULT_ADDRESS",
  "Destination": "rVAULT_ADDRESS",
  "Amount": "1",
  "Memos": [{
    "Memo": {
      "MemoType": "782d6d756c74692f61757468",
      "MemoData": "<hex-encoded JSON session>"
    }
  }]
}
```

- `MemoType`: hex encoding of `x-multi/auth`
- `MemoData`: hex encoding of a JSON object:

```json
{
  "session": "uuid-v4",
  "domain": "firstledger.net",
  "vault": "rVAULT_ADDRESS",
  "created": "2026-04-04T12:00:00Z",
  "expires": "2026-04-04T13:00:00Z"
}
```

#### Verification Flow

1. dApp receives a transaction hash from the user
2. dApp queries the XRPL: `{ "method": "tx", "params": [{ "transaction": "TX_HASH" }] }`
3. dApp checks:
   - `TransactionResult === "tesSUCCESS"` (confirmed on-chain)
   - `Account === Destination` (self-payment)
   - `Amount === "1"` (1 drop / 0.000001 XRP — minimum valid payment)
   - Memo type is `x-multi/auth`
   - Session domain matches the dApp's domain
   - Session has not expired
4. If all checks pass, the vault is authenticated

#### Public Verification API

X-Multi provides a free verification endpoint:

```
GET https://xmulti.app/api/verify/{txHash}
```

Response:
```json
{
  "verified": true,
  "vault_address": "rVAULT_ADDRESS",
  "self_payment": true,
  "session": "uuid-v4",
  "domain": "firstledger.net",
  "created": "2026-04-04T12:00:00Z",
  "expires": "2026-04-04T13:00:00Z",
  "expired": false,
  "signers": ["rSIGNER1", "rSIGNER2"],
  "tx_hash": "ABC123...",
  "ledger_index": 12345678
}
```

#### JavaScript Verification Snippet

Drop this into any dApp to verify X-Multi auth sessions:

```javascript
async function verifyXMultiAuth(txHash, expectedDomain) {
  // Option A: Use the X-Multi API
  const res = await fetch(`https://xmulti.app/api/verify/${txHash}`);
  const proof = await res.json();
  
  if (!proof.verified) return { authenticated: false, error: proof.error };
  if (proof.domain !== expectedDomain) return { authenticated: false, error: 'Domain mismatch' };
  if (proof.expired) return { authenticated: false, error: 'Session expired' };
  
  return { authenticated: true, vault: proof.vault_address, signers: proof.signers };
  
  // Option B: Verify directly against XRPL (no dependency on X-Multi)
  // const tx = await xrplClient.request({ command: 'tx', transaction: txHash });
  // ... parse Memos, check MemoType === hex('x-multi/auth'), validate session
}
```

### Method 2: Signer Proxy Authentication

A simpler approach where the dApp trusts individual signers based on their on-chain governance role.

#### Flow

1. User logs into the dApp with their **personal wallet** (standard off-ledger signature — works with Xaman, GemWallet, etc.)
2. User provides the vault address they want to access
3. dApp queries the vault's SignerList on-chain:
   ```
   { "method": "account_objects", "params": [{ "account": "rVAULT", "type": "signer_list" }] }
   ```
4. dApp checks if the user's address is in the SignerList
5. If yes, grant access with a role label:
   - `weight >= quorum` → Full access (can act alone)
   - `weight > 0` → Signer access (can view and propose)
   - Not in list → No access

#### Signer Proxy Verification Snippet

```javascript
async function verifySignerProxy(userAddress, vaultAddress) {
  const res = await fetch('https://xrplcluster.com', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({
      method: 'account_objects',
      params: [{ account: vaultAddress, type: 'signer_list', ledger_index: 'validated' }]
    }),
  });
  const data = await res.json();
  const signerList = data.result?.account_objects?.[0];
  
  if (!signerList) return { authorized: false, error: 'No signer list found' };
  
  const entry = signerList.SignerEntries?.find(
    e => e.SignerEntry.Account === userAddress
  );
  
  if (!entry) return { authorized: false, error: 'Not a signer on this vault' };
  
  return {
    authorized: true,
    address: userAddress,
    vault: vaultAddress,
    weight: entry.SignerEntry.SignerWeight,
    quorum: signerList.SignerQuorum,
    canActAlone: entry.SignerEntry.SignerWeight >= signerList.SignerQuorum,
  };
}
```

## Security Considerations

### On-Ledger Auth
- **Replay protection**: Each session has a unique UUID and expiry time
- **Domain binding**: Sessions are bound to a specific domain — dApps must check the domain matches
- **Cost**: Each auth session costs a tiny network fee (~0.0002 XRP) and requires quorum approval
- **Latency**: Requires multisig approval (minutes to hours depending on signer availability)

### Signer Proxy
- **Trust model**: The dApp trusts the on-chain SignerList as the source of truth
- **Real-time**: No on-chain transaction needed — instant verification
- **Limitation**: The signer is authenticated as themselves, not as the vault. dApps must decide what access level to grant based on signer weight.

## When to Use Which

| Scenario | Recommended Method |
|----------|-------------------|
| Vault needs to prove its identity (e.g., creator dashboard access) | On-Ledger Auth |
| Individual signer needs to access vault tools | Signer Proxy |
| dApp wants instant verification | Signer Proxy |
| dApp needs cryptographic proof of vault authorization | On-Ledger Auth |
| Master key is blackholed | On-Ledger Auth (only option) |

## Adoption

To adopt X-Multi Auth in your dApp:

1. **Minimal integration**: Call `GET https://xmulti.app/api/verify/{txHash}` — 5 lines of code
2. **Full integration**: Use the JavaScript snippets above to verify directly against the XRPL
3. **Signer Proxy**: Query the vault's SignerList on-chain — no X-Multi dependency at all

## Contact

- **Website**: https://xmulti.app
- **Spec**: https://xmulti.app/specs/xmulti-auth-v1.md
- **GitHub**: https://github.com/DpacJones/X-Multi
