PartyLayer
Try Demo

Advanced

This section covers advanced topics including telemetry, session persistence, registry internals, security best practices, and production deployment.

Telemetry & Metrics

PartyLayer includes an opt-in telemetry system for collecting anonymous usage metrics. Telemetry is disabled by default and respects user privacy.

Enabling Telemetry

typescript
import { createPartyLayer } from '@partylayer/sdk';

const client = createPartyLayer({
  network: 'mainnet',
  app: { name: 'My dApp' },
  telemetry: {
    enabled: true,
    endpoint: 'https://metrics.my-dapp.com/collect',
    sampleRate: 0.1,        // 10% sampling
    batchSize: 10,           // Send in batches of 10
    flushIntervalMs: 30000,  // Flush every 30s
  },
});

Telemetry Configuration

PropTypeDefaultDescription
enabledbooleanfalseEnable/disable telemetry collection.
endpointstring—URL to send telemetry data to.
sampleRatenumber1.0Sampling rate (0.0 to 1.0).
appIdstring—App identifier (SHA-256 hashed before sending).
includeOriginbooleanfalseInclude dApp origin URL in metrics.
batchSizenumber10Number of events to batch before sending.
flushIntervalMsnumber30000Interval between flushes (ms).

Collected Metrics

The SDK collects 9 canonical metrics:

  • wallet_connect_attempts, Total connection attempts
  • wallet_connect_success, Successful connections
  • sessions_created, New sessions created
  • sessions_restored, Sessions restored from storage
  • restore_attempts, Session restore attempts
  • registry_fetch, Registry fetch operations
  • registry_cache_hit, Registry cache hits
  • registry_stale, Stale registry data usage
  • error_<CODE>, Error counts by error code
💡 Privacy
No PII is ever collected. App IDs are SHA-256 hashed before transmission. All payloads are validated before sending.

Session Persistence

Session persistence has three distinct layers: the wallet-owned connection, the SDK cache and the React live session view. A stored cache or cookie is never proof of wallet authorization or application login.

How It Works

  • Official native discovery owns its provider-ID hint. The SDK restores a native session only after checking the live provider, party, network, origin and expiry.
  • Old native caches without that provider hint require one explicit reconnect; failed probes do not display a cached connected account.
  • The SDK cache uses its configured storage/crypto adapters. The default SDK storage is browser localStorage; its origin-derived crypto encoding is not a secret vault and can fall back to base64. Do not put authentication secrets or keys in this cache.
  • The React kit uses an in-memory live projection by default. Explicit sessionOptions.storage is a UI cache; it does not bypass wallet checks.
  • Standalone @partylayer/session stores default to encrypted IndexedDB where supported and memory otherwise. This standalone default is distinct from the kit and SDK defaults.

Per-Wallet Restore Support

Restoration depends on wallet support and live authentication. Official popup providers are probed before restoration; OneSwap requires a fresh explicit connection. A wallet logout, expired session or changed party/network must not be hidden by a saved UI state. Inspect the current session and capabilities instead of assuming every wallet silently reconnects.

Explicit logout across tabs

Coordinated React logout requires React 3.1.2+ and session 1.1.10+.

Enable sessionOptions={{ broadcast: true }} on PartyLayerKit to coordinate sessions across tabs on the same origin. Use ConnectButton or the React disconnect hooks, including @partylayer/react/query, for explicit logout. They cancel reconnect and synchronize the shared store. Receiving tabs disconnect their own provider once and clear their SDK-backed account view; they do not rebroadcast that logout. Provider failures remain visible as session errors.

A direct SDK call manages that client's lifecycle. Applications that orchestrate their own session store must coordinate its lifecycle too. Cross-tab synchronization does not turn a cached identity into wallet authorization.

Custom Storage

Provide a custom storage adapter for non-browser environments:

typescript
import { createPartyLayer } from '@partylayer/sdk';

const client = createPartyLayer({
  network: 'mainnet',
  app: { name: 'My dApp' },
  storage: {
    get: (key) => myCustomStorage.get(key),
    set: (key, value) => myCustomStorage.set(key, value),
    remove: (key) => myCustomStorage.delete(key),
    clear: () => myCustomStorage.clear(),
  },
});

Registry Internals

The PartyLayer wallet registry is a JSON manifest of wallet metadata, published from a public repository and served from a single HTTPS origin. It is validated against a JSON Schema on every build. It is not signed today: see Integrity below.

How the Registry Works

  • Fetch, On init, the SDK fetches the registry from registry.partylayer.xyz
  • Validate, The payload is checked against the registry schema; entries that do not match are rejected
  • Cache, Validated data is cached with ETag support for efficient updates
  • Fallback, If the registry is unreachable, the SDK falls back to adapter-only discovery

Integrity

What protects the registry today is schema validation on every build, a gate that blocks the removal of a wallet's CIP-0103 native flag, a public commit history, and TLS to a single origin. Ed25519 signing is implemented in the client but is not enabled in production. Verification only runs when a consumer passes registryPublicKeys, and no signature is published for either channel, so the verification path does not execute. Do not treat registry contents as cryptographically attested.

Custom Registry

typescript
<PartyLayerKit
  network="mainnet"
  appName="My dApp"
  registryUrl="https://my-registry.example.com"
  channel="beta"
>

Registry Status

Monitor registry health with useRegistryStatus or the registry:status event:

typescript
client.on('registry:status', (event) => {
  const { status } = event;

  if (status.error) {
    console.warn('Registry error:', status.error.message);
    console.log('Using cached data:', status.source === 'cache');
  }

  console.log('Verified:', status.verified);
  console.log('Stale:', status.stale);
});

Security

Content Security Policy (CSP)

If your dApp uses CSP headers, ensure these are allowed:

text
connect-src 'self' https://registry.partylayer.xyz;
frame-src 'self';  /* For popup-based wallets */

Origin Validation

PartyLayer validates the dApp origin during wallet connections. The origin is included in session metadata and verified by wallets. This prevents session hijacking across domains.

Transport Security

  • PostMessage, Origin is validated on every message exchange
  • Deep Links, HTTPS-only with app-link verification
  • Injected, Direct in-process communication (no network)
  • QR/Popup, Encrypted channel with session key exchange

Production Checklist

Before deploying to production:

  • Set network to "mainnet"
  • Use the "stable" registry channel (default)
  • Test with all supported wallets
  • Add error handling for all operations (connect, sign, submit)
  • Subscribe to the error event for global error reporting
  • Configure CSP headers for your domain
  • Call client.destroy() on app unmount (automatic with PartyLayerKit)
  • Test session restoration (page reload should maintain connection)
  • Verify registry fallback works (test with network offline)
  • If using telemetry, configure your endpoint and sampling rate
⚠️ Warning
Never expose registry public keys or telemetry endpoints in client-side code if they contain sensitive information. Use environment variables and server-side configuration.

Lower-Level Provider (PartyLayerProvider)

If you need more control than PartyLayerKit provides, use the lower-level PartyLayerProvider directly:

tsx
import { PartyLayerProvider, ThemeProvider } from '@partylayer/react';
import { createPartyLayer } from '@partylayer/sdk';

// Create and configure the client manually
const client = createPartyLayer({
  network: 'mainnet',
  app: { name: 'My dApp', origin: 'https://my-dapp.com' },
  registryUrl: 'https://my-registry.example.com',
  storage: customStorageAdapter,
  telemetry: { enabled: true, endpoint: '...' },
  logger: customLogger,
});

function App() {
  return (
    <ThemeProvider theme="dark">
      <PartyLayerProvider client={client} network="mainnet">
        {/* Full manual control */}
      </PartyLayerProvider>
    </ThemeProvider>
  );
}
PreviousSwap IntegrationNextPattern Cookbook