npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@terminal3/t3n-sdk

v3.0.0

Published

T3n TypeScript SDK - A minimal SDK that mirrors the server's RPC handler approach

Readme

T3n TypeScript SDK

A minimal TypeScript SDK that mirrors the server's RPC handler approach, keeping all state machine logic hidden in WASM and providing a clean, agnostic wrapper that doesn't expose authentication methods or internal states.

Features

  • Simple API: Clean, minimal interface that's easy to use
  • Method Agnostic: Supports multiple authentication methods (Ethereum, OIDC) without exposing implementation details
  • WASM-Powered: All cryptographic complexity and state machine logic isolated in WASM components
  • Type Safe: Full TypeScript support with comprehensive type definitions
  • Secure: Encrypted communication with T3n nodes
  • Extensible: Easy to add new authentication methods without changing TypeScript code

Installation

pnpm add @terminal3/t3n-sdk

Quick Start

Basic Usage

import {
  T3nClient,
  loadWasmComponent,
  createEthAuthInput,
  eth_get_address,
  eth_sign,
} from "@terminal3/t3n-sdk";

const wasmComponent = await loadWasmComponent();
const privateKey = process.env.T3N_DEMO_KEY!;

const client = new T3nClient({
  baseUrl: "https://t3n-node.example.com",
  wasmComponent,
  handlers: {
    EthSign: eth_sign(privateKey),
  },
});

await client.startHandshake();

const did = await client.authenticate(
  createEthAuthInput(eth_get_address(privateKey))
);

Ethereum Authentication

import {
  T3nClient,
  createEthAuthInput,
  eth_get_address,
  eth_sign,
} from "@terminal3/t3n-sdk";

const privateKey = "0x...";

const client = new T3nClient({
  wasmComponent: await loadWasmComponent(),
  handlers: {
    EthSign: eth_sign(privateKey),
  },
});

await client.startHandshake();
const did = await client.authenticate(
  createEthAuthInput(eth_get_address(privateKey))
);

OIDC Authentication

import { createOidcAuthInput } from "@terminal3/t3n-sdk";

const oidcCredentials = {
  provider: "google",
  idToken: "jwt_token_here",
};

const did = await client.authenticate(createOidcAuthInput(oidcCredentials));

Migrating from 1.x

@terminal3/[email protected] cuts over to tee:user/[email protected] (MAT-1374). The implicit-dispatch monolith user-upsert on the user contract was split into three explicit functions on the same contract:

  • otp-request — request + dispatch an OTP code.
  • otp-verify — redeem an OTP and bind the contact.
  • user-upsert (slim) — Level 1 user-input ingest only. Rejects callers without a verified email with the typed UserUpsertError { kind: "EmailNotVerified" } (wire form email_not_verified:<detail>).

If you used the typed T3nClient methods to wrap executeAction, the SDK now ships client.otpRequest / client.otpVerify / client.submitUserInput (plus a convenience client.runOtpThenUserInput) so you can migrate one call site at a time:

| Pre-2.0.0 (tee:[email protected]) | 2.x (tee:[email protected], contract ≥ 2.1.0 for discriminated OTP) | | ---------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | | executeAction({ function_name: "user-upsert", input: { profile: { email_address } } }) | client.otpRequest({ emailChannel: { emailAddress } }) | | executeAction({ function_name: "user-upsert", input: { profile, otp_code } }) | client.otpVerify({ otpCode, request: { emailChannel: { emailAddress } } }) | | executeAction({ function_name: "user-upsert", input: { profile, keys: { generic_api: { otp_channel: "sms" } } } }) | client.otpRequest({ smsChannel: { phoneNumber } }) | | executeAction({ function_name: "user-upsert", input: { profile } }) (post-OTP) | client.submitUserInput({ profile }) |

Worked example: full email + L1 ingest

import { T3nClient, UserUpsertError } from "@terminal3/t3n-sdk";

// 1) Bind the user's email via OTP.
const requested = await client.otpRequest({
  emailChannel: { emailAddress: "[email protected]" },
});
const code = await prompt(`Code sent to ${requested.contact}: `);
await client.otpVerify({
  otpCode: code,
  request: { emailChannel: { emailAddress: "[email protected]" } },
});

// 2) Slim user-upsert: Level 1 user-input ingest. Rejects with
// UserUpsertError(kind: "EmailNotVerified") if step 1 was skipped.
try {
  const result = await client.submitUserInput({
    profile: {
      first_name: "Alice",
      last_name: "Smith",
      country_of_residence: "US",
      // ...other Level-1 fields
    },
  });
  console.log("tx:", result.txHash);
} catch (err) {
  if (err instanceof UserUpsertError && err.kind === "EmailNotVerified") {
    // run otp-request + otp-verify, then retry.
  }
  throw err;
}

For tests that "just want it to work", runOtpThenUserInput chains the three calls behind a single getOtpCode callback.

Hard-error fields

Passing any of these to the wrong function returns the typed UserUpsertError(kind: "LegacyField") (wire form legacy_field:<detail>):

  • otp_code to anything other than otp-verify.
  • keys.generic_api.otp_channel to anything (channel is now a top-level field).

Raw executeAction callers

If you bypass the typed wrappers, see the migration table above: the function names (otp-request / otp-verify / user-upsert) and JSON input shapes line up 1:1 with the wrappers' camelCase fields converted to snake_case.

Architecture

The T3n SDK follows the same architectural principles as the server's rpc.rs:

  • Completely agnostic about authentication methods (Ethereum, OIDC, etc.)
  • No knowledge of internal state machine phases or details
  • Simply calls WASM next() and eagerly tries to finish()
  • Lets WASM handle all the complexity internally
  • Works with completely opaque byte arrays (just like the WIT interface)

What's Hidden in WASM (Everything Important)

  • All state machine phases and transitions
  • Authentication method-specific logic (challenge/response, OIDC flows, signature verification)
  • Cryptographic operations and key derivation
  • Protocol-specific message formatting and parsing
  • Error handling and retry logic
  • Internal state representations (all states are opaque byte arrays)

What's Exposed in TypeScript (Minimal Surface)

  • Input helpers only: createEthAuthInput, createOidcAuthInput, and signer handlers
  • High-level status: Just mirrors server's SessionStatus enum
  • Simple API methods: startHandshake(), authenticate()
  • Basic configuration: baseUrl, sessionId
  • Opaque state management: Byte arrays we never interpret

API Reference

T3nClient

The main client class for interacting with T3n nodes.

Constructor

new T3nClient(config: T3nClientConfig)

Methods

  • startHandshake(): Promise<void> - Establish secure session with the node
  • authenticate(authInput: AuthInput): Promise<Did> - Authenticate using provided credentials
  • getSessionId(): SessionId | null - Get the server-minted session ID (null until handshake completes; pentest M-1 / MAT-983 moved session-id minting from the client to the server)
  • getStatus(): SessionStatus - Get current session status
  • getDid(): Did | null - Get authenticated DID (null if not authenticated)
  • isAuthenticated(): boolean - Check if client is authenticated

Types

SessionStatus

enum SessionStatus {
  Init = 0,
  Encrypted = 1,
  Authenticated = 2,
}

AuthInput

enum AuthMethod {
  Ethereum = "eth",
  OIDC = "oidc",
}

interface EthAuthInput {
  method: AuthMethod.Ethereum;
  address: string;
}

interface OidcCredentials {
  provider: string;
  idToken: string;
}

interface OidcAuthInput {
  method: AuthMethod.OIDC;
  credentials: OidcCredentials;
}

type AuthInput = EthAuthInput | OidcAuthInput;

Helper utilities:

createEthAuthInput(address: string): EthAuthInput;
createOidcAuthInput(credentials: OidcCredentials): OidcAuthInput;

Error Handling

The SDK provides specific error types for different failure scenarios:

import { 
  T3nError, 
  SessionStateError, 
  AuthenticationError, 
  HandshakeError, 
  RpcError 
} from '@terminal3/t3n-sdk';

try {
  await client.authenticate(signer);
} catch (error) {
  if (error instanceof AuthenticationError) {
    console.log('Authentication failed:', error.message);
  } else if (error instanceof SessionStateError) {
    console.log('Invalid session state:', error.currentState);
  }
}

Configuration

T3nClientConfig

interface T3nClientConfig {
  baseUrl?: string;                   // T3n node URL (defaults to "http://localhost:3000" if not provided)
  wasmComponent: WasmComponent;       // WASM component instance
  transport?: Transport;              // Optional transport layer (uses HttpTransport with baseUrl if not provided)
  sessionId?: SessionId;              // Optional custom session ID
  timeout?: number;                   // Request timeout (default: 30000ms)
  headers?: Record<string, string>;   // Custom headers
  logLevel?: LogLevel;                // Log level for this client (defaults to global log level, which is LogLevel.ERROR)
  logger?: Logger;                    // Optional custom logger (overrides logLevel if provided)
  handlers?: GuestToHostHandlers;     // Optional guest-to-host request handlers
}

Example with Custom Configuration

import { T3nClient, LogLevel } from '@terminal3/t3n-sdk';

const client = new T3nClient({
  baseUrl: "https://t3n-node.example.com",
  wasmComponent: await loadWasmComponent(),
  timeout: 60000, // 60 second timeout
  logLevel: LogLevel.DEBUG, // Enable debug logging
  headers: {
    'User-Agent': 'MyApp/1.0.0',
    'X-Custom-Header': 'custom-value'
  },
  handlers: {
    EthSign: eth_sign(privateKey),
    MlKemPublicKey: ml_kem_public_key(),
    Random: random(),
  }
});

Logging

The T3n SDK provides flexible logging capabilities with hierarchical log levels, allowing you to control verbosity for debugging and monitoring purposes.

Log Levels

The SDK supports four log levels in hierarchical order (from most to least verbose):

  • LogLevel.DEBUG (0) - Most verbose, includes all log messages
  • LogLevel.INFO (1) - Informational messages, warnings, and errors
  • LogLevel.WARN (2) - Warnings and errors only
  • LogLevel.ERROR (3) - Errors only (default)

Log levels are hierarchical: setting a level includes all higher-priority levels. For example, setting LogLevel.INFO will log INFO, WARN, and ERROR messages, but not DEBUG messages.

Default Behavior

By default, the SDK uses LogLevel.ERROR, which means only error messages are logged. This ensures minimal console output in production environments.

Global Log Level Control

The SDK provides a global log level that serves as the default for all loggers created without an explicit level. Use setGlobalLogLevel() to configure this default, typically at application startup.

How it works:

  • setGlobalLogLevel() affects all loggers created via getLogger() or createLogger() without an explicit level
  • Only loggers created after calling setGlobalLogLevel() will use the new level
  • Existing logger instances retain their original log level

Interaction with per-component overrides:

  • When creating a T3nClient, you can override the global level by providing logLevel in the config
  • Per-component logLevel takes precedence over the global setting
  • If neither is provided, the global default (LogLevel.ERROR) is used
import { setGlobalLogLevel, LogLevel, T3nClient } from '@terminal3/t3n-sdk';

// Set global level at application startup
setGlobalLogLevel(LogLevel.DEBUG);

// This client will use DEBUG level (from global)
const client1 = new T3nClient({
  wasmComponent: await loadWasmComponent(),
});

// This client overrides global level with INFO
const client2 = new T3nClient({
  wasmComponent: await loadWasmComponent(),
  logLevel: LogLevel.INFO, // Overrides global DEBUG
});

Creating Logger Instances

You can create logger instances programmatically:

import { createLogger, getLogger, LogLevel } from '@terminal3/t3n-sdk';

// Create a logger with a specific level
const debugLogger = createLogger(LogLevel.DEBUG);

// Get a logger using the global log level
const globalLogger = getLogger();

// Use logger in handlers
const handlers = {
  EthSign: metamask_sign(address, debugLogger),
};

Best Practices

  • Production: Use LogLevel.ERROR (default) to minimize console output
  • Development: Use LogLevel.DEBUG for detailed debugging information
  • Staging: Use LogLevel.INFO for monitoring without excessive verbosity
  • Custom Logging: Implement a custom logger to integrate with your logging infrastructure (e.g., Winston, Pino, or cloud logging services)

Example: Environment-Based Logging

import { setGlobalLogLevel, LogLevel } from '@terminal3/t3n-sdk';

// Set log level based on environment
const logLevel = process.env.NODE_ENV === 'production' 
  ? LogLevel.ERROR 
  : LogLevel.DEBUG;

setGlobalLogLevel(logLevel);

Development

Building

pnpm build

Testing

pnpm test
pnpm run test:coverage

Linting

pnpm lint
pnpm run lint:fix

Demo

# Run the demo (builds first)
pnpm demo

# Run demo in development mode
pnpm demo:dev

# Run demo with real WASM component
pnpm demo:real-wasm

# Run demo with real WASM component and upsert profile
pnpm demo:real-wasm --upsert

# Run demo with real WASM component and upsert profile from file
pnpm demo:real-wasm --upsert path/to/profile.json

# Run demo with real WASM component, upsert profile, and specify email
pnpm demo:real-wasm --upsert --email [email protected]

# Run demo with real WASM component, upsert profile from file, and specify email
pnpm demo:real-wasm --upsert path/to/profile.json --email [email protected]

# Provider webhook (default: user update only; no OID4VP present)
pnpm demo:real-wasm --provider

# Provider webhook: user update then OID4VP present (add --oid4vp or --oid4vp-json)
pnpm demo:real-wasm --provider --oid4vp

# Provider webhook with custom user-update payload from file
pnpm demo:real-wasm --provider --user-update-json path/to/user-update.json

# Provider with custom OID4VP payload (runs user update then OID4VP with this file)
pnpm demo:real-wasm --provider --oid4vp-json path/to/oid4vp.json

# OID4VP present only (skip user update; use existing DID)
pnpm demo:real-wasm --provider --oid4vp-only --did did:t3n:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

# Provider with custom signing key (must match TEE provider config public_key)
pnpm demo:real-wasm --provider --provider-signing-key 0x0123...your32bytehex

# Provider webhook call with custom email
pnpm demo:real-wasm --provider --email [email protected]

# Provider webhook call with custom provider ID
pnpm demo:real-wasm --provider --provider-id t3 --email [email protected]

# Get profile after authentication (requires session authentication)
pnpm demo:real-wasm --get-profile --email [email protected]

# Get profile with JSON path filters
pnpm demo:real-wasm --get-profile --email [email protected] --filter "$.first_name" --filter "$.email_address"

# Provider webhook + Get profile (email automatically passed from provider call)
pnpm demo:real-wasm --provider --get-profile

Provider and OID4VP

With --provider, the demo runs a user update (create/update profile) by default. No OID4VP present request is sent unless you pass an OID4VP-related flag:

  1. User update only (default) – POST a configurable payload (profile, attestations, credentials) to the provider webhook. The TEE creates or updates the profile and returns a did and optional tx_hash.

  2. User update then OID4VP present – When you pass --oid4vp or --oid4vp-json <path>, after step 1 the demo also sends an oid4vp_present request with profile.did from step 1, plus dcql_query, response_uri, nonce, and client_id.

  3. OID4VP present only – When you pass --oid4vp-only --did <did>, the demo skips user update and sends only the OID4VP present request for the given DID.

Payloads are configurable via defaults or JSON files (--user-update-json, --oid4vp-json). The request body is signed with EIP-191 using the provider’s EOA key. The key must match auth_method.public_key for the provider in the TEE's provider config. By default the demo uses the test key (0x01×32); override with --provider-signing-key or PROVIDER_SIGNING_KEY for other environments.

# User update only (default)
pnpm demo:real-wasm --provider

# User update then OID4VP present (default OID4VP payload)
pnpm demo:real-wasm --provider --oid4vp

# User update then OID4VP present (custom OID4VP JSON)
pnpm demo:real-wasm --provider --oid4vp-json ./my-oid4vp.json

# OID4VP only for an existing DID
pnpm demo:real-wasm --provider --oid4vp-only --did did:t3n:your-uuid-here

Demo Command-Line Arguments

The demo supports the following command-line arguments:

  • --upsert or -u: Enable upsert profile action after authentication. Optionally provide a JSON file path to load profile data from.

  • --email or -e: Specify email address for profile operations. If not provided, the demo will prompt you interactively.

  • --provider or -p: Call the provider webhook endpoint. This simulates a third-party provider (e.g., KYC service) sending user data to T3n. The webhook call:

    • Signs the payload with an EOA signature using a test private key (or --provider-signing-key / PROVIDER_SIGNING_KEY)
    • Sends profile data to /api/{provider_id} endpoint
    • Returns a DID and transaction hash
    • Does not require session authentication
  • Default flow: With --provider only, the demo runs user update only (create/update profile). No OID4VP present request is sent.

    • To also run OID4VP present after user update, pass --oid4vp (default OID4VP payload) or --oid4vp-json <path> (custom payload).
    • To run only OID4VP present (no user update), use --oid4vp-only with --did <did>.
  • --provider-id: Specify the provider ID for webhook calls (default: "t3"). Used with --provider flag.

  • --user-update-json: Path to a JSON file for the user-update (create profile) payload. Shape: { "profile": { ... }, "attestations": [ ... ], "credentials": [ ... ] }. If omitted, default payload is used (includes a test SD-JWT so OID4VP with default DCQL can be satisfied).

  • --oid4vp-json: Path to a JSON file for the OID4VP present payload. When running the full two-step flow, profile.did is always set from step 1. If omitted, default payload is used (dcql_query, response_uri from VP_VERIFIER_URL, nonce, client_id). Presence of this flag (with a path) also enables the OID4VP present step after user update.

  • --oid4vp: With --provider, run user update then OID4VP present using the default OID4VP payload. Use this to trigger the full two-step flow without providing --oid4vp-json.

  • --oid4vp-only: With --provider, skip step 1 and run only OID4VP present. Requires --did <did>.

  • --did: DID to use for OID4VP when --oid4vp-only is set (e.g. did:t3n:uuid).

  • --wait-tx: After step 1 (user update), wait for transaction confirmation before step 2. Can also set env WAIT_FOR_TX=true. (Tx wait is not implemented; flag is accepted for future use.)

  • --provider-signing-key: EOA private key (32-byte hex, e.g. 0x01...01) for signing provider webhooks. Must match the TEE’s provider config auth_method.public_key for the provider. Default is the test key used in integration. Can also set env PROVIDER_SIGNING_KEY.

  • --script-version: Optional override for contract script version used by session-based actions (--upsert, --get-profile, --agent-auth). If omitted, the demo resolves the latest version dynamically from /api/contracts/current for tee:user/contracts.

  • --get-profile or -g: Retrieve user profile using the session client. This command:

    • Requires authentication (handled automatically)
    • Uses email from provider webhook call or --email argument
    • Supports optional JSON path filtering
  • --filter or -f: Specify JSON path filters for get-profile command. Can be used multiple times to filter specific profile fields. Example: --filter "$.first_name" --filter "$.email_address".

Combining Commands:

You can combine multiple flags for more complex workflows:

# Provider webhook call followed by get-profile (email automatically passed)
pnpm demo:real-wasm --provider --get-profile

# Provider webhook with custom email, then get-profile with filters
pnpm demo:real-wasm --provider --email [email protected] --get-profile --filter "$.first_name"

# Upsert profile, then get-profile
pnpm demo:real-wasm --upsert --email [email protected] --get-profile

Demo Environment Variables

The demo supports the following environment variables:

  • CRYPTO_NODE_URL: The base URL of the T3n crypto node API (defaults to http://localhost:3000).
  • TEE_API_URL: The base URL of the T3n TEE API endpoint (defaults to http://localhost:3000/api). Used by the --provider flag for webhook calls.
  • VP_VERIFIER_URL: Base URL for the OID4VP response endpoint (defaults to http://localhost:8100). The demo sets response_uri to {VP_VERIFIER_URL}/post in the default OID4VP payload.
  • PROVIDER_SIGNING_KEY: EOA private key (32-byte hex) for signing provider webhooks. Overrides the default test key when set.
  • WAIT_FOR_TX: When set to "true" or "1", enables waiting for tx confirmation before OID4VP (same as --wait-tx).
  • SCRIPT_VERSION: Optional script version override for session-based demo actions. Priority is --script-version > SCRIPT_VERSION > dynamic latest lookup from /api/contracts/current.
  • DEBUG: Enable debug logging when set to "true" (defaults to disabled).

Example usage:

# With custom crypto node URL
CRYPTO_NODE_URL=http://localhost:3000 pnpm demo:real-wasm

# With custom TEE API URL for provider webhook calls
TEE_API_URL=http://localhost:3000/api pnpm demo:real-wasm --provider

# With custom VP verifier URL for OID4VP response_uri
VP_VERIFIER_URL=http://localhost:8100 pnpm demo:real-wasm --provider

# With custom provider signing key (must match TEE provider config)
PROVIDER_SIGNING_KEY=0x0123... pnpm demo:real-wasm --provider

# Override script version explicitly (otherwise latest is resolved dynamically)
pnpm demo:real-wasm --get-profile --script-version 1.0.0

# With debug logging enabled
DEBUG=true pnpm demo:real-wasm

# With both custom URLs and debug logging
CRYPTO_NODE_URL=http://localhost:3000 TEE_API_URL=http://localhost:3000/api DEBUG=true pnpm demo:real-wasm --provider --get-profile

Email Verification (OTP Flow)

When upserting a profile with an email address (either for a new profile or when changing an existing email), the system requires email verification via OTP (One-Time Password):

  1. Initial Upsert: When you run --upsert with an email, the system sends a 6-digit OTP code to the specified email address.

  2. OTP Prompt: The demo will automatically detect that OTP verification is needed and prompt you:

    📧 Email verification required
    ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ address. The demo will prompt you to enter the 6-digit OTP code sent to your email.
    
  3. Enter OTP Code: When prompted, enter the 6-digit code you received in your email.

  4. Verification: The demo will automatically retry the upsert with your OTP code and complete the profile upsert.

Note: In debug/mock mode (DEBUG=true or MOCK_MODE=true), the OTP code will be displayed in the console for testing purposes.

Note: The demo supports interactive OIDC authentication. When you select OIDC authentication, the demo will:

  1. Start a local web server on port 8081
  2. Open your browser automatically
  3. Guide you through the Google OAuth flow
  4. Allow you to paste the ID token back into the terminal

KYC Walkthrough (MAT-1371)

The --kyc-walkthrough mode drives the full MetaMask KYC L1 + L2 flow against a running Trinity node, end-to-end, without needing the actual MetaMask front-end. It is useful for:

  • Smoke-testing a freshly-built node (node/bin/start-local) before pointing the real FE at it.
  • Reproducing edge cases (rejected KYC, OTP expiry, merge suggestions) deterministically.
  • Showing internal stakeholders what the L1 + L2 flow looks like without spinning up MetaMask Snap + Veriff sandbox.

Quick start (interactive):

pnpm demo:real-wasm --kyc-walkthrough

The walkthrough then prompts at each step in the order Trinity expects:

  1. Handshake + authenticate (existing prompts — wallet / OIDC).
  2. Email OTP — request + verify on tee:user/contracts::user-upsert (email channel).
  3. Phone OTP — request + verify on the same function (SMS channel via keys.generic_api.otp_channel: "sms" shadow). Gated by the contract on a verified email.
  4. Level 1 user-input — single user-upsert call carrying first_name, last_name, country_of_residence, document_issuance_country, ssn, address.
  5. create-kyc-provider-session (MAT-1284) — prints the Veriff session_url. The CLI does not auto-open the URL.
  6. kyc-status poll — until terminal status (verified, rejected, or orphan) or the timeout fires. Each snapshot is logged.

Veriff is driven manually. The CLI prints the session_url and waits — open it in your browser, drive the Veriff flow to the desired terminal state (approve / reject / abandon), then come back to the terminal. The poll loop will see the contract update and surface the terminal status.

KYC walkthrough flags

| Flag | Default | Meaning | |---|---|---| | --kyc-walkthrough | off | Enable the walkthrough mode. Implicitly enabled by --scenario. | | --scenario <name> | none | One of happy-path-l1, happy-path-l2, rejected. See Scenarios below. | | --auth-method <eth\|metamask\|oidc> | interactive prompt | Skip the auth-method prompt. | | --email <addr> | interactive prompt (or [email protected] in scenario mode) | Email to verify in the L1 OTP step. | | --phone <e164> | interactive prompt (or +14155552671 in scenario mode) | Phone in E.164 format for the SMS OTP step. | | --first-name <name> | interactive prompt (or Ada in scenario mode) | L1 first name. | | --last-name <name> | interactive prompt (or Lovelace in scenario mode) | L1 last name. | | --country <ISO-2> | US | Convenience: applies to both country_of_residence and document_issuance_country unless overridden individually. | | --country-of-residence <ISO-2> | --country value | L1 residence country. | | --document-issuance-country <ISO-2> | --country value | L1 document issuance country. | | --ssn <value> | interactive prompt (or 123-45-6789 in scenario mode) | L1 SSN (9 digits or XXX-XX-XXXX). | | --address <text> | interactive prompt (or default address in scenario mode) | L1 residential address. | | --kyc-provider-id <id> | veriff | Provider id passed to create-kyc-provider-session and kyc-status. | | --kyc-poll-fast-ms <ms> | 2000 | Fast-cadence poll interval (T3-TS-026 §8.4). | | --kyc-poll-slow-ms <ms> | 5000 | Slow-cadence poll interval. | | --kyc-poll-switch-at-ms <ms> | 30000 | Elapsed threshold to switch from fast to slow. | | --kyc-poll-timeout-ms <ms> | 300000 | Total cap before KycStatusTimeoutError is raised. | | --skip-create-kyc-session | off | Stop after L1 — useful when no Veriff sandbox is configured. |

Scenarios

Pre-baked shortcuts that auto-feed answers (mock-mode OTP codes, default L1 fields) so the walkthrough runs without operator input. All scenarios still require a running Trinity node configured with the mock OTP provider (otherwise the deterministic OTP code algorithm doesn't match what the node actually generated).

| Scenario | What it does | Veriff | |---|---|---| | happy-path-l1 | Email OTP → phone OTP → L1 upsert. Stops before create-kyc-provider-session. | Not exercised. | | happy-path-l2 | Full L1 + L2 flow. Asserts kyc-status terminates with verified. | Manual: open the printed session_url and drive Veriff to approval. | | rejected | Full L1 + L2 flow. Asserts kyc-status terminates with rejected. | Manual: open the printed session_url and drive Veriff to a rejection. |

Examples:

# L1-only happy path — no Veriff sandbox needed
pnpm demo:real-wasm --scenario happy-path-l1

# Full L1 + L2 happy path — drive Veriff to approved manually
pnpm demo:real-wasm --scenario happy-path-l2

# Rejection scenario — drive Veriff to a rejection manually
pnpm demo:real-wasm --scenario rejected

# Custom inputs without a scenario
pnpm demo:real-wasm --kyc-walkthrough \
  --auth-method eth \
  --email [email protected] \
  --phone +14155551234 \
  --country US \
  --first-name Ada --last-name Lovelace
Mock-mode OTP code

When the Trinity node runs against the mock OTP provider (the default for node/bin/start-local), the OTP code is a deterministic 6-digit hash of the contact value. The walkthrough always prints the calculated mock code in the OTP-pending log line, so even without a scenario you can simply enter that code at the prompt to advance.

The algorithm matches node/wasm/src/otp.rs byte-for-byte: String((hash * 31 + byte_i) % 1_000_000).padStart(6, "0") over the UTF-8 bytes of the contact (email address or E.164 phone).

Sample output (excerpt)
🚦 KYC walkthrough mode enabled (MAT-1371)
   Scenario: happy-path-l1

📧 Step 5/8 — Email OTP: [email protected]
5️⃣ Executing action: user-upsert...
   ✅ Action executed successfully
📨 Email verification required
   📬 OTP code sent to: [email protected]
   📡 Channel: email
   💡 Mock mode OTP code (deterministic): 482913
   🤖 Scenario auto-feeding OTP code: 482913
🔄 Verifying Email OTP code...
5️⃣ Executing action: user-upsert...
   ✅ OTP verified successfully
   📝 Transaction hash: tx:1:42

📱 Step 6/8 — Phone OTP: +14155552671
6️⃣ Executing action: user-upsert...
   ...

📋 KYC walkthrough summary
   ✅ email-otp            [email protected]
   ✅ phone-otp            +14155552671
   ✅ l1-upsert            tx:1:44
   ⏭️  create-kyc-session  (scenario)
   ⏭️  kyc-status-poll     (scenario)
Caveats
  • No version bump. The walkthrough is dev tooling — it does not introduce new @terminal3/t3n-sdk public-API surface, so package.json is not bumped.
  • Spec alignment. The walkthrough follows the as-built code, which differs from earlier T3-TS-026 versions on two points: user-upsert has no op: discriminator (dispatch is implicit on input shape), and the function for L2 session creation is tee:user/contracts::create-kyc-provider-session rather than tee:kyc/contracts::create-session. See T3-TS-026 v0.6.0 changelog and MAT-1374 for the deferred discriminator-vs-split decision.
  • STRICT_SCENARIO=true in the environment turns scenario assertion failures into a non-zero exit code (otherwise they're logged but the demo exits zero). --scenario always exits non-zero on a terminal-status mismatch.

Tenant Developer Demo

Three-entity walkthrough: an Admin admits a tenant, the Tenant registers the Duffel flight contract and seeds credentials, a User (created via demo:dev) authenticates and searches / books flights. Each step is a separate command so you can run them independently.

Three entities

| Entity | Key env var | Role | |---|---|---| | Admin | ADMIN_KEY | Signs tenant.admit via the admin API | | Tenant | TENANT_KEY | Registers contract, creates KV map, injects Duffel API key | | User | USER_KEY | Authenticates and calls search-offers / book-offer on behalf of an AI agent |

Environment variables

| Variable | Default | Used by | |---|---|---| | CRYPTO_NODE_URL | http://localhost:3000 | all commands | | ADMIN_KEY | 0x000...0001 | admit | | TENANT_KEY | 0x000...0002 | admit, setup | | DUFFEL_API_KEY | duffel_test_placeholder | setup | | FLIGHT_WASM_PATH | (unset) | setup — path to compiled z-tenant-flight WASM; falls back to a placeholder | | USER_KEY | 0x000...0003 | search, book — ETH key of a user already registered via demo:dev | | OFFER_ID | (required) | book — offer ID printed by search | | TOTAL_AMOUNT | (required) | book — total price printed by search | | TOTAL_CURRENCY | (required) | book — currency code printed by search |

Step 1 — Admit the tenant (Admin)

ADMIN_KEY=0x... TENANT_KEY=0x... pnpm demo:tenant:admit

Step 2 — Register contract + seed credentials (Tenant)

TENANT_KEY=0x... DUFFEL_API_KEY=duffel_test_... FLIGHT_WASM_PATH=../z-tenant-flight/target/wasm32-wasip2/release/z_tenant_flight.wasm pnpm demo:tenant:setup

This registers z:<tid>:duffel-flight, creates the z:<tid>:secrets KV map restricted to that contract, then calls store-credentials on the contract to write the Duffel API key into the map.

Step 3 — Create the user (User)

Use the standard user demo — no changes needed:

USER_KEY=0x... pnpm demo:dev

Step 4 — Search for flights (User / Agent)

USER_KEY=0x... pnpm demo:tenant:search

Prints up to 5 offers. The last line of output is the exact book command with the correct OFFER_ID, TOTAL_AMOUNT, and TOTAL_CURRENCY pre-filled.

Step 5 — Book a flight (User / Agent)

USER_KEY=0x... OFFER_ID=off_... TOTAL_AMOUNT=199.00 TOTAL_CURRENCY=GBP pnpm demo:tenant:book

The passenger record is a hardcoded fixture (Jane Doe, GB passport). PII enters the TEE enclave and is never returned — only booking_id, pnr, and status cross the WIT boundary back to the caller.


WASM Integration

The SDK can work with both real T3n WASM components and mock components for testing.

Automatic WASM Loading

import { loadWasmComponent } from '@terminal3/t3n-sdk';

// Automatically loads real WASM if available, falls back to mock
const wasmComponent = await loadWasmComponent();

The loader will automatically:

  1. Try to load the real T3n WASM component (if available)
  2. Fall back to a mock component for testing

Real WASM Component

To use the real WASM component:

  1. Build the WASM component (from T3n project root):

    cd node
    cargo build -p session-client --target wasm32-wasip2 --release
  2. Copy the WASM file to the SDK directory:

    cp target/debug/build/cn-api-*/out/session.wasm t3n-sdk/
  3. The SDK will automatically detect and use it:

    pnpm demo:real-wasm

Mock Component for Testing

import { createMockWasmComponent } from '@terminal3/t3n-sdk';

// Use mock component for testing
const mockComponent = createMockWasmComponent();

WASM Component Features

When using the real WASM component, you get:

  • Actual cryptographic operations from the T3n session module
  • Real state machine logic for handshake and authentication
  • Production-grade security for session establishment
  • Full protocol compatibility with T3n nodes

Examples

See the examples directory for more comprehensive usage examples:

  • Basic Usage - Simple session establishment and RPC
  • Advanced Usage - Error handling, custom configuration, and integration patterns

License

MIT

Contributing

Please read our contributing guidelines and submit pull requests to our repository.

Support

For support, please open an issue on our GitHub repository or contact the T3n team.