Skip to content

Offline Key Export

You can export private keys while completely offline. The SDK includes a service worker that works without network connectivity.

Why Offline Export?

WebAuthn relies on the wallet origin being reachable online. So if there is a DNS outage and the domain (e.g web3authn.org) goes down, users would be unable to access their wallet.

This is a WebAuthn constraint:

"WebAuthn ties credentials to a specific domain name (the RP ID). If the browser can't reach the DNS server to verify the domain or resolve it to the correct IP, the authentication process cannot be completed."

We therefore added an offline key export to ensure users can export wallet keys. The offline export flow is implemented as a service‑worker‑backed route under the wallet origin.

Once users visit the wallet and register while online, the offline service worker is automatically registered and begins caching assets in the background.

Users can:

  1. Put the device in airplane mode (or otherwise disable Wifi access).
  2. Navigate to https://wallet.example.com/offline-export/ (or the wallet host’s /offline-export/) while offline. NOTE: that the it's /offline-export/ with the trailing slash /.
  3. The service worker will serve the app from cache and it can read the encrypted key material from IndexedDB and perform the export locally.

In that scenario, whether DNS for web3authn.org still resolves or the host is "down" on the network is irrelevant: the browser never needs to reach the server.

WARNING

The user must not clear site data (cookies/storage/caches for that origin).

If the wallet domain goes down, and the user clears site data, then the service worker will be gone (until the site comes up again, and they revisit the site)

Verifying Offline Export

  1. Open wallet origin https://wallet.example.com/offline-export/
  2. Open DevTools → Application → Service Workers
  3. Verify OFFLINE_EXPORT_v4 is active and controlling page
  4. Turn off Wifi on your device
  5. Refresh page
  6. All assets load from Service Worker (no red network errors)

Chrome DevTools → Application → Cache Storage → OFFLINE_EXPORT_v4: Should contain:

  • /offline-export/offline-export-app.js
  • /offline-export/workers/*.js and *.wasm
  • /sdk/export-private-key-viewer.js
  • /sdk/export-viewer.css
  • Various SDK chunks (e.g., common-*.js, lit-events-*.js)

The service worker and offline files are automatically generated by:

  • Vite: the tatchiBuildHeaders (or tatchiWallet({ emitHeaders: true })) plugin.
  • Next.js: the nextEmitOfflineExportAssets helper (see below).

Setting Up Offline Export (Automatic)

In development, the SDK dev plugins automatically serve offline export for wallet hosts:

  • Vite wallet host: use tatchiWalletServer / tatchiDev wrappers (see SDK plugin docs).
  • You can verify by visiting http://wallet.example.localhost/offline-export/ (where wallet.example.localhost is your Caddy route to the wallet server).

In production, the build plugin automatically emits offline export assets. Here's a Vite example (wallet host build):

typescript
import { tatchiBuildHeaders } from '@tatchi-xyz/sdk/plugins/vite'

export default defineConfig({
  plugins: [
    tatchiBuildHeaders({ walletOrigin: 'https://wallet.example.com' })
  ]
})

Build output includes:

  • dist/offline-export/index.html
  • dist/offline-export/sw.js (Service Worker)
  • dist/offline-export/manifest.webmanifest (PWA manifest)
  • dist/offline-export/workers/*.js and *.wasm

Production (Next.js)

Use postbuild helper:

typescript
// scripts/postbuild-offline.ts
import { nextEmitOfflineExportAssets } from '@tatchi-xyz/sdk/plugins/next'

nextEmitOfflineExportAssets({
  outDir: './public',
  sdkBasePath: '/sdk'
})
json
// package.json
{
  "scripts": {
    "postbuild": "node scripts/postbuild-offline.ts"
  }
}

Headers

Production deployment requires specific headers:

bash
# /offline-export/sw.js and /offline-export/index.html
Cache-Control: no-cache

# Other /offline-export/* assets
Cache-Control: public, max-age=31536000, immutable

# All .wasm files
Content-Type: application/wasm

# Offline export route
Cross-Origin-Embedder-Policy: require-corp
Cross-Origin-Resource-Policy: cross-origin
Permissions-Policy: publickey-credentials-get=(self)

Troubleshooting

504 "Offline asset not pre-cached":

Service Worker cache incomplete. Unregister SW in DevTools, reload online once, try again offline.

Service Worker not activating:

Check browser console for registration errors. Ensure /offline-export/sw.js is served with Cache-Control: no-cache.

Assets load from network when offline:

Service Worker not controlling page. Check DevTools → Application → Service Workers shows "activated and is controlling this page".

WASM fails to load:

WASM files must be served with Content-Type: application/wasm. Check build config includes this MIME type.

Export works online but not offline:

Likely a missing asset in cache. Check Cache Storage in DevTools, compare against precache manifest at /offline-export/precache.manifest.json.

See Also