Skip to content

CKSDK

Overview

CKSDK is the JavaScript SDK that provides a secure, opinionated way for your platform to interact with the CoKeeps Hot Wallet. It is responsible for managing cryptographic keys, signing requests, and encrypting payloads, while your own application focuses on business logic, UX, and governance.

Depending on your integration strategy, CKSDK can be used in the frontend, the backend, or a hybrid (bridge) model. Regardless of where it runs, CKSDK acts as the trusted interface to CoKeeps Hot Wallet and is the primary mechanism for:

  • Establishing authenticated sessions
  • Creating and using per-user keypairs
  • Signing and encrypting requests
  • Verifying responses and enforcing integrity checks

Instead of conventional API keys or bearer tokens which can be leaked, misconfigured, or misused by backend code - each end user can be associated with their own cryptographic keypair. CKSDK uses these keys to sign request payloads and perform end-to-end encryption, enabling:

  • Strong accountability: actions can be traced back to the expected user or machine identity.
  • Spoofing resistance: developers cannot arbitrarily "act as" end users when you choose a higher-security integration model.
  • Data integrity: signed payloads can be validated by CoKeeps (and internally via SBDV) to detect tampering at rest or in transit.

When CKSDK runs on the frontend, the authentication private key never leaves the user's device; only the public key and signed messages are sent to CoKeeps Hot Wallet. In hybrid and backend-only models, CKSDK still provides cryptographic protection and avoids static secrets, while giving you flexibility to balance security, complexity, and operational control.

All communication between CKSDK and CoKeeps Hot Wallet is transported over TLS (HTTPS). Sensitive artefacts such as raw credentials or secret keys are not stored within the CoKeeps Hot Wallet system.

IMPORTANT

The effectiveness of these protections depends on the integrity of both your application and the user’s device. A compromised frontend (e.g. injected scripts) or malware such as keyloggers can still capture raw credentials before they are processed by CKSDK. We strongly recommend implementing Multi-Factor Authentication (MFA) on your own platform and applying standard secure-coding and hardening practices to minimise these risks.

Frontend Implementation

During the integration phase, you will be provided with the Browser CoKeeps JavaScript SDK (CKSDK). This section outlines a minimal example of how to load, initialise, and use CKSDK in your web application.

IMPORTANT

The examples below focus on illustrating the integration flow. They do not include production concerns such as rate limiting, CSRF protection, content security policies, or advanced session management. You should apply your organisation's standard security practices on top of this baseline.

1. Including CKSDK

Add the CKSDK script to your web application:

html
<script src="/cksdk-browser.js"></script>

2. Initialisation on Application Load

On application load, you must:

  1. Attach CKSDK from window.
  2. Configure the connection to the CoKeeps Hot Wallet.
  3. Attempt to restore an existing session (if any).
javascript
var CKSDK = null;

window.addEventListener('load', async () => {
  CKSDK = window.CKSDK;

  // setup
  const params = {
    hostSignPublicKey: "<hot_wallet_public_key>",
    hostId: "cokeeps-hotwallet",
    hostEndpoint: "<hot_wallet_https_endpoint>"
  };

  // Include this only if it is used for your administration.
  params.scope = "admin";

  CKSDK.auth.setup(params);

  // Unique per-user, per-session/device secret generated by your backend
  const secret = "<your_system_generated_unique_user_secret_session>";

  // Load session if it exists. Refer to the Log In section.
  if (await CKSDK.auth.load(params.hostSignPublicKey, secret)) {
      if (await CKSDK.auth.connected()) {
          // Connection successful
          // - User session is valid
          // - CKSDK keys match the provided secret
      } else {
          // Connection failure with CoKeeps Hot Wallet:
          // - Account is blocked by your back-end verify endpoint
          // - Invalid hot wallet public key
          // - Invalid secret
      }
  }

  await run();
});

function run() {
    // Your application logic
}

Replace:

  • <hot_wallet_public_key>: with the Hot Wallet signing public key provided during integration.

  • <hot_wallet_https_endpoint>: with the HTTPS base URL of the CoKeeps Hot Wallet.

  • <your_system_generated_unique_user_secret_session>: with a backend-generated secret that is unique per user session/device.

    This user secret is used as an encryption salt for CKSDK's persistence mechanism. If a different secret is supplied for the same logical session, the stored cryptographic material becomes invalid and Hot Wallet authentication will fail.

3. Logging In

Use the login flow to:

  1. Collect the user's credential (or equivalent authenticator output).
  2. Let CKSDK derive the Hot Wallet authentication keypair on the device.
  3. Persist the session using your backend-generated user secret in the previous step.
javascript
async function log_in() {
    const username = "<identifiable_such_as_email>";
    var password = "<user_input_credential_or_webauth_private_key_string>";

    // An error will be thrown if any issues arise
    await CKSDK.auth.login({ username, password });

    // Immediately clear the raw credential from memory
    password = null;

    // Securely store session for persistence
    const secret = "<your_system_generated_unique_user_secret_session>";
    CKSDK.auth.store(secret);

    // Additional login routines (e.g. updating UI, redirecting)
}

Key points:

  • The raw password value is not transmitted to CoKeeps Hot Wallet and is not stored by CKSDK.
  • It is used locally to derive the user's Hot Wallet authentication cryptographic key.
  • Only the derived public key and signed/encrypted payloads are sent to the Hot Wallet.

You must ensure that:

  • The secret used in CKSDK.auth.store(secret) matches the one used later in CKSDK.auth.load(...) for the same session/device.
  • The secret itself is managed and stored securely according to your organisation's standards.

4. Logging Out

To log a user out of the Hot Wallet session:

javascript
async function log_out() {
    // An error will be thrown if any issues arise
    await CKSDK.auth.logout();

    // Additional logout routines (e.g. clearing app state, redirecting)
}

This clears CKSDK's authenticated state. You should also clear any application-level session or UI state associated with the user in your own platform.

Backend + Bridge Implementation

During the integration phase, you will be provided with the NodeJS CoKeeps JavaScript SDK (CKSDK). This section outlines a minimal example of how to use CKSDK in your backend.

All capabilities available in the frontend browser CKSDK are also exposed in the backend Node.js CKSDK. The main difference is how sessions and secrets are managed.

The bridge pattern is intended for architectures where your frontend is a native mobile app (e.g. iOS/Android), or cannot safely / conveniently run the browser CKSDK directly. In this model:

  • The Node.js CKSDK runs on your backend,
  • The frontend uses a lightweight bridge client (e.g. JavaScript or Dart) to:
    • Encrypt and sign user credential into a sealed payload using a bridge public key, and
    • Send that sealed payload to your backend,
  • Your backend uses Node.js CKSDK to:
    • Decrypt and verify sealed,
    • Manage CKSDK sessions on behalf of the user, and
    • Call Hot Wallet operations (e.g. generate deposit address, submit transfers).

IMPORTANT

Examples below are focused on the mechanics of CKSDK usage. You must still apply your own production security controls (rate limiting, authentication, authorisation, audit logging, etc.).

Using CKSDK in Your Backend Code (Node.js)

Below is a simplified example of using the Node.js CKSDK in JavaScript:

javascript
const sha256 = require('crypto-js/sha256');
const CKSDK = require("<path_to>/cksdk-node.min.js");

// Setup parameters
const setupParams = {
    hostSignPublicKey: "<hot_wallet_public_key>",
    hostId: "cokeeps-hotwallet",
    hostEndpoint: "<hot_wallet_https_endpoint>"
};

// Example "session store"
// 1. Clear the entry when the user logs out or resets their password.
// 2. Apply an expiry for inactivity.
const simpleSessionDatabase = {};

// A server-side secret that must be isolated from other systems/stores.
const serverSecret = "<your_system_secret_separated_from_database>";

async function request(uid, sealed = false) {
    // CKSDK instance must be scoped to the request, not a global singleton.
    const CKUSER = new CKSDK();

    // Unique per-user, per-session/device secret coming from your client.
    const secret = "<your_system_generated_unique_user_secret_session>";
    const sessionSecret = sha256(serverSecret + secret).toString();

    // Setup as usual – this also returns the bridge public key used by your client.
    const bridgePublicKey = await CKUSER.auth.setup(setupParams);

    // If a session exists, restore it.
    if (simpleSessionDatabase[uid]) {
        await CKUSER.auth.self_load(simpleSessionDatabase[uid], setupParams.hostSignPublicKey, sessionSecret);
    } else if (sealed) {
        // No existing session – perform a login using the sealed payload.
        await CKUSER.auth.login({ sealed });

        // Store the user session using the derived sessionSecret.
        simpleSessionDatabase[uid] = CKUSER.auth.self_store(sessionSecret);
    } else {
        throw new Error("Invalid procedure.");
    }

    // Use the SDK as usual – e.g. generate a deposit address.
    let depositAddress = await CKUSER.action.generate_deposit({
        chain_id: "bitcoin"
    });

    console.log("Deposit address", depositAddress);
}

// Example call – the `sealed` value comes from your bridge client.
const sealed = "<hex_digest_from_your_bridge_client>";

request("<UID>", sealed).catch(console.log);

Key concepts in this pattern:

  • serverSecret

    A backend-only secret that never leaves your server environment. It is combined with the client-provided session secret to derive a sessionSecret, reducing the impact of compromise of any single value.

  • Client secret (per-user, per-session)

    A unique value generated by your system and provided to the client. The same value must be used consistently for a given session/device to successfully restore CKSDK state.

  • sessionSecret = sha256(serverSecret + secret)

    Used to encrypt and decrypt stored CKSDK session data via self_store / self_load.

  • sealed

    An encrypted "envelope" produced by the bridge client using the bridge public key. It contains user credential material (or equivalent) in a form that only your backend CKSDK can open and verify.

Obtaining the Bridge Public Key

Before your frontend client can generate sealed payloads, it needs the bridge public key. You obtain it from the Node.js CKSDK:

NOTE

This is just a one time procedure.

javascript
const setupParams = {
  hostSignPublicKey: "<hot_wallet_public_key>",
  hostId: "cokeeps-hotwallet",
  hostEndpoint: "<hot_wallet_https_endpoint>"
};

const CKUSER = new CKSDK();

async function getBridgePublicKey() {
  await CKUSER.auth.setup(setupParams);
  const bridgePublicKey = await CKUSER.auth.get_bridge_pubkey();
  console.log(bridgePublicKey);
  return bridgePublicKey;
}

You can then set this bridgePublicKey to your frontend client, where it will be used to create the sealed payloads sent back to your backend.

We provide example bridge client implementations for securely handling and passing sealed into your backend CKSDK in:

  • JavaScript
  • Dart (including Flutter)

Backend (No-Bridge) Implementation

In the no-bridge model, all CKSDK logic runs exclusively on your backend. Your frontend (web, mobile, or otherwise) has zero direct integration with CoKeeps or CKSDK.

In this setup:

  • Your backend:
    • Authenticates “users” to the CoKeeps Hot Wallet using derived credentials (not their real login passwords),
    • Owns and controls the CKSDK sessions, and
    • Invokes all wallet operations (e.g. creating accounts, generating deposit addresses, initiating transfers).
  • Your frontend:
    • Talks only to your backend,
    • Has no exposure to Hot Wallet endpoints or keys.

This is the simplest integration path, but it also means:

  • Your backend (and its operators) can act on behalf of users,
  • Accountability and non-repudiation are weaker than in frontend / bridge models,
  • It becomes closer to a "per-user API key" pattern, where the backend is fully trusted.

Minimal Example (Stateless – Login on Every Request)

The following example shows a simple pattern where your backend:

  1. Derives a synthetic username and password for CoKeeps, based on your own user identifiers.
  2. Logs in to the Hot Wallet via CKSDK.
  3. Executes an action (e.g. generating a single-signature account).
  4. Discards the CKSDK instance at the end of the request.

NOTE

This example is intentionally minimal and does not cover security considerations, such as rate limiting, abuse protection, or secure management of derived secrets.

javascript
const sha256 = require("crypto-js/sha256");
const CKSDK = require("<path_to>/cksdk-node.min.js");

// Setup parameters
const setupParams = {
  hostSignPublicKey: "<hot_wallet_public_key>",
  hostId: "cokeeps-hotwallet",
  hostEndpoint: "<hot_wallet_https_endpoint>"
};

async function request(frontendId, customerUid) {
  // CKSDK instance should be created per request, not reused globally
  const CKUSER = new CKSDK();

  // Derive a synthetic username and secret for this user in CoKeeps
  const username = `${frontendId}-${customerUid}`;
  const password = your_own_function_to_generate_unique_secret(frontendId, customerUid);

  // Authenticate to CoKeeps Hot Wallet with derived credentials
  await CKUSER.auth.login({ username, password });

  // Example: create a new single-signature account on maschain testnet
  const singleSignatureAddress = await CKUSER.maschain.generate_wallet({
    chain_id: "maschaintestnet",
    label: "Account 1"
  });

  return singleSignatureAddress;
}

Key points:

  • username and password do not need to match your own user login credentials.

    • In fact, they should not be your users' real passwords.
    • Treat them as CoKeeps-auth secrets, derived from your internal IDs and/or keys.
  • your_own_function_to_generate_unique_secret(...) should:

    • Produce a deterministic value for the same user (so you can log in again later),
    • Use a server-side secret, or similar to prevent trivial guessing,
    • Never be exposed to untrusted parties.

This stateless pattern is simple but requires a full login handshake on every call, which adds latency and some overhead.

Session-Based Variant (Using self_store / self_load)

If you want to avoid logging in on every request, you can:

  1. Log in once using auth.login(...).
  2. Persist the CKSDK session using auth.self_store(...).
  3. Reload the session in subsequent requests using auth.self_load(...).

At a high level (pseudo-flow):

javascript
// After first successful login
const sessionSecret = sha256("<server_secret>" + "<user_session_secret>").toString();
const storedSession = CKUSER.auth.self_store(sessionSecret);
// Persist `storedSession` in your database keyed by your user ID

// On subsequent requests
const CKUSER = new CKSDK();
CKUSER.auth.self_load(storedSession, setupParams.hostSignPublicKey, sessionSecret);
// Now call CKUSER.* actions without a fresh login

Important considerations:

  • This pattern replaces frequent logins with a "token key": the stored CKSDK session bound to sessionSecret.
  • While this is convenient and performant, it also means:
    • Anyone who can obtain or reconstruct the stored session + sessionSecret can act as that user toward CoKeeps.
    • You must design access controls, storage, and rotation policies as carefully as you would for per-user API keys.

In other words, you're shifting from "login every time" to "trust this long-lived capability". This is acceptable if:

  • Your backend is locked down and well-governed, and
  • You're comfortable with a trust model similar to per-user API keys rather than user-held keys on the client.

Typescript Caveat

Because cksdk-node.min.js is an obfuscated JavaScript bundle, TypeScript cannot infer its types automatically. You must:

  1. Allow JavaScript modules in your TypeScript project.
  2. Import CKSDK with a type override (e.g. any).

1. TypeScript Configuration

Update your tsconfig.json to enable allowJs:

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "allowJs": true
  },
  "include": ["src/**/*"],
  "exclude": ["node_modules"]
}

2. Using CKSDK in TypeScript

Since TypeScript cannot introspect the obfuscated code, import and cast the module as any:

js
import * as CKSDKModule from './cksdk/cksdk-node.min.js';
const CKSDK = CKSDKModule as any;

const CKUSER = new CKSDK.default();

From here, you can follow the same usage pattern as in the plain JavaScript example, while TypeScript will treat CKSDK as an untyped object (i.e. no compile-time type checking for CKSDK APIs).