Web Browser APIs (W3C)

Revolutionary client integration patterns using HTMX, Cross-Origin iFrames, and Browser APIs to solve crypto UX problems across maximum devices with seamless ease of control

Web Browser APIs Integration

Scope

This document covers Sonr's client integration patterns using standard web technologies: HTMX for dynamic interfaces, Cross-Origin iFrames for secure isolation, and native Browser APIs for device capabilities. It includes implementation patterns, security models, and device compatibility. This document does not cover low-level protocol details—see the DWN module reference for Vault specifications.

Audience

Web developers building crypto-enabled applications. Frontend engineers integrating blockchain features. System architects designing secure client architectures. Prerequisites: HTML/CSS/JavaScript knowledge, understanding of web security concepts (CORS, CSP), and basic familiarity with browser APIs.

Summary

Sonr enables crypto integration using standard web technologies instead of blockchain SDKs. Developers use HTMX for server-driven UI, Cross-Origin iFrames for wallet isolation, and Browser APIs for authentication and storage. Applications work on any device with a web browser—no downloads, extensions, or blockchain knowledge required. The system maintains security through browser sandboxing and UCAN authorization.

Architecture Overview

Traditional vs. Sonr Approach

Traditional Web3 requires complex client-side libraries:

// Traditional: Complex wallet integration
import { ethers } from "ethers";
import { WalletConnectConnector } from "@web3-react/walletconnect";

// Different code for each wallet type
if (window.ethereum?.isMetaMask) {
  // MetaMask specific logic
} else if (window.ethereum?.isCoinbaseWallet) {
  // Coinbase specific logic
}

Sonr uses standard web patterns:

<!-- Sonr: Simple HTML form -->
<form hx-post="/api/sonr/payment" hx-target="#result">
  <input type="email" name="recipient" placeholder="alice@sonr.id" />
  <input type="number" name="amount" placeholder="25.00" />
  <button type="submit">Send Payment</button>
</form>
<div id="result"></div>

Component Architecture

HTMX Integration

Server-Driven Interfaces

HTMX enables dynamic crypto interfaces without JavaScript frameworks:

<!-- Payment form with HTMX -->
<form
  hx-post="/api/sonr/payment"
  hx-target="#payment-result"
  hx-indicator="#loading"
>
  <input type="email" name="recipient" required />
  <input type="number" name="amount" step="0.01" required />
  <select name="currency">
    <option value="USDC">USDC</option>
    <option value="ATOM">ATOM</option>
  </select>
  <button type="submit">
    Send Payment
    <span id="loading" class="htmx-indicator">⏳</span>
  </button>
</form>
<div id="payment-result"></div>

Server returns HTML responses:

<!-- Success response -->
<div class="success">
  <h3>✅ Payment Sent</h3>
  <p>Sent $25.00 USDC to alice@sonr.id</p>
  <p>Transaction: <code>tx_1a2b3c4d</code></p>
</div>

Real-Time Updates

Live blockchain data without WebSockets:

<!-- Auto-updating portfolio -->
<div hx-get="/api/sonr/portfolio" hx-trigger="every 30s" hx-swap="innerHTML">
  <h2>Portfolio: $12,543.21</h2>
  <!-- Server renders current balances -->
</div>

<!-- Live transaction feed -->
<div
  hx-get="/api/sonr/transactions"
  hx-trigger="load, every 10s"
  hx-target="#tx-list"
>
  <div id="tx-list">
    <!-- Server renders transaction history -->
  </div>
</div>

Progressive Enhancement

Forms work without JavaScript, enhance with HTMX:

<!-- Basic form -->
<form action="/api/sonr/stake" method="POST">
  <input type="number" name="amount" required />
  <button type="submit">Stake ATOM</button>
</form>

<!-- Enhanced with HTMX -->
<form
  hx-post="/api/sonr/stake"
  hx-target="#result"
  hx-confirm="Stake this amount?"
>
  <input
    type="number"
    name="amount"
    hx-get="/api/validate-stake"
    hx-trigger="keyup changed delay:500ms"
    hx-target="#validation"
  />
  <div id="validation"></div>
  <button type="submit">Stake ATOM</button>
</form>
<div id="result"></div>

Cross-Origin iFrames

Secure Wallet Isolation

Vaults run in isolated iFrames for security:

<!-- Host application (your-app.com) -->
<div class="app-content">
  <h1>Your DeFi App</h1>

  <!-- Sonr Vault iFrame -->
  <iframe
    id="sonr-vault"
    src="https://vault.sonr.id/embed"
    sandbox="allow-scripts allow-same-origin"
    style="width: 300px; height: 400px;"
  >
  </iframe>
</div>

<script>
  // Secure communication
  const vault = document.getElementById("sonr-vault");

  function requestPayment(amount, recipient) {
    vault.contentWindow.postMessage(
      {
        type: "REQUEST_CAPABILITY",
        capability: "payment:send",
        params: { amount, recipient },
      },
      "https://vault.sonr.id",
    );
  }

  // Listen for responses
  window.addEventListener("message", (event) => {
    if (event.origin !== "https://vault.sonr.id") return;

    switch (event.data.type) {
      case "CAPABILITY_GRANTED":
        console.log("Payment authorized");
        break;
      case "TRANSACTION_COMPLETE":
        console.log("Transaction done");
        break;
    }
  });
</script>

Vault Implementation

The Vault handles user authorization:

// Inside vault.sonr.id
class VaultMessenger {
  handleMessage(event) {
    // Verify origin
    if (!this.isAllowedOrigin(event.origin)) return;

    switch (event.data.type) {
      case "REQUEST_CAPABILITY":
        this.showAuthDialog(event.data);
        break;
    }
  }

  showAuthDialog(request) {
    // Show user-friendly prompt
    const dialog = {
      title: `${request.origin} wants to send payments`,
      description: `Allow up to $${request.params.amount}?`,
      actions: ["Approve", "Deny"],
    };

    // User decision
    dialog.onApprove = () => {
      const token = this.generateUCAN(request);
      parent.postMessage(
        {
          type: "CAPABILITY_GRANTED",
          token: token,
        },
        request.origin,
      );
    };
  }
}

Browser API Integration

WebAuthn Authentication

Passwordless authentication using device biometrics:

// Create credential
async function createAccount(email) {
  const credential = await navigator.credentials.create({
    publicKey: {
      challenge: crypto.getRandomValues(new Uint8Array(32)),
      rp: { name: "Sonr Network" },
      user: {
        id: crypto.getRandomValues(new Uint8Array(32)),
        name: email,
        displayName: email,
      },
      pubKeyCredParams: [{ alg: -7, type: "public-key" }],
      authenticatorSelection: {
        authenticatorAttachment: "platform",
        userVerification: "required",
      },
    },
  });
  return credential;
}

// Authenticate
async function authenticate() {
  const credential = await navigator.credentials.get({
    publicKey: {
      challenge: new TextEncoder().encode("login-challenge"),
      userVerification: "required",
    },
  });
  return credential;
}

IndexedDB Storage

Encrypted local storage for Vault data:

class VaultStorage {
  async initialize() {
    const request = indexedDB.open("SonrVault", 1);

    request.onupgradeneeded = (event) => {
      const db = event.target.result;
      db.createObjectStore("vault_data", { keyPath: "id" });
      db.createObjectStore("transactions", { keyPath: "txId" });
    };

    this.db = await new Promise((resolve, reject) => {
      request.onsuccess = () => resolve(request.result);
      request.onerror = () => reject(request.error);
    });
  }

  async store(id, data) {
    // Encrypt before storage
    const encrypted = await this.encrypt(data);
    const tx = this.db.transaction(["vault_data"], "readwrite");
    return tx.objectStore("vault_data").put({ id, data: encrypted });
  }

  async encrypt(data) {
    const key = await this.getEncryptionKey();
    const iv = crypto.getRandomValues(new Uint8Array(12));
    const encrypted = await crypto.subtle.encrypt(
      { name: "AES-GCM", iv },
      key,
      new TextEncoder().encode(JSON.stringify(data)),
    );
    return { iv, data: encrypted };
  }
}

ServiceWorker Background Operations

Handle blockchain operations in the background:

// vault-worker.js
self.addEventListener("message", async (event) => {
  const { type, data } = event.data;

  switch (type) {
    case "EXECUTE_TRANSACTION":
      await handleTransaction(data, event.ports[0]);
      break;
    case "SYNC_STATE":
      await syncVaultState(data, event.ports[0]);
      break;
  }
});

async function handleTransaction(txData, port) {
  try {
    // Validate transaction
    if (!(await validateTransaction(txData))) {
      throw new Error("Invalid transaction");
    }

    // Execute on blockchain
    const result = await executeTransaction(txData);

    // Notify all clients
    const clients = await self.clients.matchAll();
    clients.forEach((client) => {
      client.postMessage({
        type: "TRANSACTION_COMPLETE",
        result,
      });
    });

    port.postMessage({ result });
  } catch (error) {
    port.postMessage({ error: error.message });
  }
}

Device Compatibility

Responsive Design

Sonr clients adapt to all screen sizes:

/* Mobile-first design */
.sonr-vault {
  width: 100%;
  height: 60vh;
}

/* Tablets */
@media (min-width: 768px) {
  .sonr-vault {
    width: 400px;
    height: 500px;
    border-radius: 12px;
  }
}

/* Desktop */
@media (min-width: 1024px) {
  .sonr-vault {
    width: 320px;
    height: 600px;
    position: fixed;
    right: 20px;
    top: 20px;
  }
}

Progressive Web App

Transform websites into app experiences:

{
  "name": "DeFi App with Sonr",
  "short_name": "DeFi+Sonr",
  "display": "standalone",
  "start_url": "/",
  "icons": [
    {
      "src": "/icon-192.png",
      "sizes": "192x192",
      "type": "image/png"
    }
  ],
  "shortcuts": [
    {
      "name": "Send Payment",
      "url": "/pay",
      "icons": [{ "src": "/pay.png", "sizes": "96x96" }]
    }
  ]
}

Integration Examples

E-commerce Checkout

Add crypto payments to any store:

<!-- Checkout form -->
<div class="checkout">
  <h2>Order Total: $29.99</h2>

  <button hx-post="/api/crypto-checkout" hx-target="#checkout-result">
    💎 Pay with Crypto
  </button>

  <div id="checkout-result"></div>
</div>

<!-- Server returns payment interface -->
<iframe
  src="https://pay.sonr.id/checkout?amount=29.99"
  width="100%"
  height="400"
>
</iframe>

Content Tipping

Enable micropayments on content:

<!-- Tip buttons -->
<div class="tip-section">
  <p>Enjoyed this article?</p>

  <button
    hx-post="/api/tip"
    hx-vals='{"amount": "1", "currency": "USDC"}'
    hx-target="#tip-result"
  >
    $1 💙
  </button>

  <button
    hx-post="/api/tip"
    hx-vals='{"amount": "5", "currency": "USDC"}'
    hx-target="#tip-result"
  >
    $5 ❤️
  </button>

  <div id="tip-result"></div>
</div>

Drop-in Widget

Add Sonr to any site with one line:

<!-- Add wallet widget -->
<script
  src="https://cdn.sonr.id/wallet-widget.js"
  data-position="bottom-right"
  data-theme="light"
></script>

Security Considerations

Content Security Policy

Configure CSP headers for iFrame security:

Content-Security-Policy:
  frame-src https://vault.sonr.id;
  connect-src https://api.sonr.id;
  script-src 'self' https://cdn.sonr.id;

Origin Verification

Always verify message origins:

window.addEventListener("message", (event) => {
  // Strict origin check
  if (event.origin !== "https://vault.sonr.id") {
    return;
  }

  // Validate message structure
  if (!event.data.type || !event.data.signature) {
    return;
  }

  // Process trusted message
  handleVaultMessage(event.data);
});

Permission Scoping

Request minimal capabilities:

// Good: Specific permission
vault.requestCapability({
  action: "transfer",
  constraints: {
    maxAmount: "100",
    allowedTokens: ["USDC"],
    expires: Date.now() + 3600000, // 1 hour
  },
});

// Bad: Unlimited permission
vault.requestCapability({
  action: "transfer",
  constraints: {}, // No limits
});

Performance Optimization

Lazy Loading

Load features on demand:

// Register features
const features = new Map([
  ["trading", () => import("./features/trading.js")],
  ["nft", () => import("./features/nft.js")],
  ["defi", () => import("./features/defi.js")],
]);

// Load when needed
async function loadFeature(name) {
  const loader = features.get(name);
  if (loader) {
    const module = await loader();
    return module.initialize();
  }
}

// Intersection observer for auto-loading
const observer = new IntersectionObserver((entries) => {
  entries.forEach((entry) => {
    if (entry.isIntersecting) {
      const feature = entry.target.dataset.feature;
      loadFeature(feature);
    }
  });
});

Caching Strategy

Cache blockchain data intelligently:

class SonrCache {
  async get(key) {
    // Check memory first
    const cached = this.memory.get(key);
    if (cached && cached.expiry > Date.now()) {
      return cached.data;
    }

    // Check IndexedDB
    const stored = await this.getFromDB(key);
    if (stored && stored.expiry > Date.now()) {
      return stored.data;
    }

    return null;
  }

  async set(key, data, ttl = 60000) {
    const entry = {
      data,
      expiry: Date.now() + ttl,
    };

    // Store in memory
    this.memory.set(key, entry);

    // Persist to IndexedDB
    await this.saveToDB(key, entry);
  }
}

Best Practices

For Developers

  1. Use semantic HTML: Structure content properly
  2. Progressive enhancement: Ensure functionality without JavaScript
  3. Secure communications: Always verify origins and validate messages
  4. Optimize performance: Implement caching and lazy loading
  5. Test across devices: Ensure compatibility with various screen sizes

For Security

  1. Strict CSP policies: Limit resource loading
  2. Origin validation: Never trust unverified messages
  3. Minimal permissions: Request only necessary capabilities
  4. Regular updates: Keep dependencies current
  5. Audit regularly: Review security practices

Next Steps

Ready to Build?

Developers: Start with the Quick Start Guide to build your first Sonr application.

Frontend Engineers: Explore HTMX Patterns for advanced techniques.

Architects: Review Security Best Practices for production deployments.

Learn how browser integration connects with other Sonr components by exploring the Wallet Architecture or Service Integration documentation.