@permaweb/web-wallet
v0.2.0
Published
Secure iframe client transport for the PermawebOS wallet
Readme
@permaweb/web-wallet
This package embeds PermawebOS as a constrained sandboxed iframe when the browser extension provider is not available. It owns the versioned transport, exact source/origin validation, binary serialization, request timeouts, wallet events, interactive-request presentation state, and wallet-origin top-level approval lifecycle.
Production clients and the hosted wallet must use separate HTTPS origins. Local development may use loopback HTTP
origins such as http://localhost:3000 and http://localhost:5173.
import {
createWebWalletClientProvider,
openWebWallet,
resolveWebWalletConnectionUrl,
} from '@permaweb/web-wallet';
import { WebWalletWindow } from '@permaweb/web-wallet/react';
const provider = createWebWalletClientProvider({
walletUrl: 'https://wallet.example',
});
export function WalletFallback() {
const source = resolveWebWalletConnectionUrl(window.location, 'https://wallet.example').href;
return <WebWalletWindow provider={provider} source={source} title="PermawebOS wallet" />;
}
export function OpenWalletButton() {
return <button onClick={() => openWebWallet(provider)}>Open wallet</button>;
}WebWalletWindow stays mounted for wallet session continuity and automatically appears for interactive requests. Its
iframe defaults to the extension's 390 × 600 viewport, with a compact drag bar above it. The window starts 16px from
the application's top-right corner, can be dragged with a pointer or the keyboard, can be resized from its native resize
handle, and includes a close button. Its header follows validated live theme colors sent by the hosted wallet. Closing it
dismisses the current view without approving or rejecting the pending wallet request; a later request opens it again.
Clients can also call openWebWallet(provider) (or provider.open()) from an explicit user action to show the wallet
without starting a signing or connection request.
Sensitive provider calls must begin in a user gesture. They open a separate wallet-origin approval window directly over
the embedded wallet's live screen rectangle, matching its outer position and size, including the header. Wallet-origin
approval and management popups request that same validated live geometry before moving over the iframe, and resize
themselves after opening so browser chrome does not make them taller than the embedded window. They fail closed if the
browser blocks or closes them. While that top-level
window is active, WebWalletWindow covers and disables
only its embedded wallet surface with an approval notice; the surrounding client application remains interactive. The
notice is removed as soon as the request completes or the secure window closes. The iframe
cannot grant itself HID, USB, downloads, forms, clipboard-read, or network capabilities through component props; its
sandbox and permission policy are wallet-controlled constants.
Wallet management actions that handle keys, passwords, Ledger devices, signing, or settings likewise move to a trusted
top-level wallet window while retaining the iframe's canonical runtime and vault.
Every sensitive request still needs explicit approval in that top-level window. After a JWK wallet is unlocked, later approvals reuse only its wallet-origin in-memory session, so they do not repeat the password prompt until the wallet is locked, the session expires, or the iframe is reloaded or removed.
The hosted wallet must set VITE_WEB_WALLET_ALLOWED_ORIGINS and send matching CSP frame-ancestors response headers.
Use WebWalletFrame and useWebWalletPresentation from the same React entry point when an application needs a fully
custom presentation. WebWalletWindow also accepts isOpen and onClose for controlled usage, plus dimension,
position-inset, z-index, class, and style overrides.
Use the same provider object wherever the application expects an Arweave browser wallet. Prefer a validated
window.permawebConnect provider when it exists, and mount the iframe fallback only when the extension is absent.
