Skip to main content
OpenKey can replace browser wallet extensions (MetaMask, WalletConnect, etc.) as the signer for TinyCloud Web SDK. This means users can authenticate to TinyCloud using their OpenKey-managed Ethereum keys, with passkey-based login instead of a browser extension.

Why Use OpenKey with TinyCloud

  • No browser extension required: Users do not need MetaMask or any wallet installed
  • Passkey-based authentication: Biometric or hardware key login instead of seed phrases
  • TEE key security: Private keys are sealed inside a hardware enclave
  • Lower onboarding friction: Non-crypto-native users can get started without understanding wallets
  • Same cryptographic guarantees: OpenKey produces standard Ethereum signatures that TinyCloud verifies the same way as any wallet

Architecture

Instead of the wallet extension signing the SIWE message, OpenKey’s TEE-managed key signs it. From TinyCloud’s perspective, the signature is indistinguishable from one produced by MetaMask.

Setup

1

Install dependencies

npm install @tinycloudlabs/web-sdk @openkey/sdk
2

Initialize OpenKey and TinyCloud

import { OpenKey } from '@openkey/sdk';
import { TinyCloudWeb } from '@tinycloudlabs/web-sdk';

const openkey = new OpenKey({
  appName: 'My TinyCloud App',
});

const tc = new TinyCloudWeb({
  notifications: { popups: true },
  persistence: { autoResumeSession: true },
});
3

Connect with OpenKey

Use OpenKey to get the user’s Ethereum address and key ID before signing in to TinyCloud.
// Connect with OpenKey (passkey auth + key selection)
const { address, keyId } = await openkey.connect();
console.log('OpenKey address:', address);
4

Sign the SIWE message with OpenKey

Provide a custom signer to TinyCloud that delegates signing to OpenKey instead of a browser wallet.
// Sign in to TinyCloud using OpenKey as the signer
const session = await tc.signIn({
  // Custom signer that uses OpenKey
  signer: {
    getAddress: async () => address,
    signMessage: async (message: string) => {
      const { signature } = await openkey.signMessage({
        message,
        keyId,
      });
      return signature;
    },
  },
});

console.log('TinyCloud session:', session.address);
5

Use TinyCloud normally

Once signed in, all TinyCloud operations work exactly the same as with a browser wallet.
// Store data in the user's space
await tc.storage.put('profile', {
  name: 'Alice',
  wallet: 'openkey',
});

// Retrieve data
const profile = await tc.storage.get('profile');
console.log(profile);

Complete Example

import { OpenKey } from '@openkey/sdk';
import { TinyCloudWeb } from '@tinycloudlabs/web-sdk';

async function main() {
  const openkey = new OpenKey({ appName: 'My App' });
  const tc = new TinyCloudWeb({
    notifications: { popups: true },
    persistence: { autoResumeSession: true },
  });

  // Step 1: Connect with OpenKey
  const { address, keyId } = await openkey.connect();
  console.log('Connected via OpenKey:', address);

  // Step 2: Sign in to TinyCloud with OpenKey signer
  const session = await tc.signIn({
    signer: {
      getAddress: async () => address,
      signMessage: async (message: string) => {
        const { signature } = await openkey.signMessage({ message, keyId });
        return signature;
      },
    },
  });
  console.log('TinyCloud session active:', session.address);

  // Step 3: Use TinyCloud storage
  await tc.storage.put('settings', { theme: 'dark', lang: 'en' });
  const settings = await tc.storage.get('settings');
  console.log('Settings:', settings);
}

main().catch(console.error);

Session Persistence

When TinyCloud is configured with autoResumeSession: true, the session persists across page reloads. On the next visit, TinyCloud resumes the session without requiring OpenKey sign-in again (as long as the session has not expired).
const tc = new TinyCloudWeb({
  persistence: { autoResumeSession: true },
});

// On page load, try to resume
const resumed = await tc.tryResumeSession();

if (!resumed) {
  // No valid session, initiate OpenKey sign-in
  const { address, keyId } = await openkey.connect();
  await tc.signIn({
    signer: {
      getAddress: async () => address,
      signMessage: async (message: string) => {
        const { signature } = await openkey.signMessage({ message, keyId });
        return signature;
      },
    },
  });
}

Delegations with OpenKey

Delegations work the same way regardless of whether the signer is OpenKey or a browser wallet. The PKH DID is derived from the Ethereum address, which is the same whether the key is in MetaMask or OpenKey.
// Alice (using OpenKey) delegates to Bob
const delegation = await tc.createDelegation({
  delegateDID: bob.pkhDid, // Always use PKH DID
  actions: ['read'],
  paths: ['shared/*'],
  expiresIn: '7d',
});
Always use pkhDid for delegations, not did. This applies regardless of the signer being used. See the Delegations guide for details.

Comparison: OpenKey vs. Browser Wallet

AspectBrowser Wallet (MetaMask)OpenKey
InstallationBrowser extension requiredNone (web-based)
Key storageUser’s device (local)TEE (server-side, sealed)
AuthenticationWallet unlock (password)Passkey (biometric)
Seed phraseUser must back upNot applicable
Signing UXExtension popupOpenKey popup/iframe
Works on mobileRequires mobile wallet appWorks in any browser
TinyCloud compatibilityNativeVia custom signer

Next Steps