nimimo Logonimimo
架构

技术规范 · 第 01 篇,共 07 篇

The Four Axes of nimimo

A structured explanation of Access, Ownership, Identity, and Recovery: the four cryptographically separated axes that nimimo is built on.

作者
Chris Zemmel
发布
2025-12-16
修订
2026-04-07

A structured explanation of Access, Ownership, Identity, and Recovery.

Overview

nimimo is designed around a deliberate separation of four fundamental axes required for human interaction with cryptographic systems. These axes are Access, Ownership, Identity, and Recovery. Each axis serves a distinct role and is intentionally prevented from escalating authority into another.

This document defines each axis. The companion papers sixteen-states.md and access-primitive.md examine, respectively, the full state space of the four axes and the formal properties of the Access axis in isolation.

Each code block below is extracted from the current source at build time. If the implementation changes, this page changes with it, the build refuses to ship a page whose snippets drift from the code they describe.


1. Access

Definition. Access is the ability to initiate a session within nimimo on a specific device.

  • Access enables interaction with the interface but does not grant authority.
  • Access methods are replaceable and non-persistent.
  • Loss of access does not imply loss of ownership.
NextAuth session callback, the entire payload that crosseslib/auth.ts
    async session({ session, user }) {
      if (session.user && user) {
        session.user.id = user.id
      }
      return session
    },

The session callback is the only place a session object is assembled for the client. It attaches an opaque user.id and nothing else, no keys, no signing material, no ambient authority.

2. Ownership

Definition. Ownership is cryptographic control over private keys generated and stored locally on the user's device.

  • Includes private keys and derived wallet addresses (protocol identities).
  • Keys are never transmitted to or stored by nimimo.
  • Ownership exists independently of access or identity.
Multi-chain address derivation, runs on the user's devicelib/ownership/v1/derive.ts
export async function deriveV1Addresses(
  seedPhrase: string,
  opts?: { customChain?: CustomChainSelection; purpose?: DerivationPurpose },
): Promise<AddressDerivationResult> {
  const purpose: DerivationPurpose = opts?.purpose ?? "primary"
  try {

    const seed = await bip39.mnemonicToSeed(seedPhrase)

    const masterKey = HDKey.fromMasterSeed(new Uint8Array(seed))

    const addresses: DerivedAddress[] = []
    const byChain = new Map<string, string>()
    const chainsToProcess = chainsForDerivation(opts?.customChain ?? null)

    for (const chain of chainsToProcess) {
      try {
        let address: string
        const path = resolvePath(chain, purpose)

        if (chain.chain === "bitcoin") {
          const hdKey = masterKey.derive(path)
          address = deriveBitcoinAddress(hdKey)
        } else if (chain.chain === "ethereum") {
          address = await deriveEthereumAddress(seedPhrase, path)
        } else if (chain.chain === "solana") {
          address = await deriveSolanaAddress(new Uint8Array(seed), path)
        } else if (chain.evmAlias) {

          const aliased = byChain.get(chain.evmAlias)
          if (!aliased) throw new Error(`evmAlias source not yet derived: ${chain.evmAlias}`)
          address = aliased
        } else if (chain.addressType === "sr25519" && chain.substrate) {
          const { deriveSubstrateAddress } = await import("@/lib/custom-chains/derive-substrate")
          address = await deriveSubstrateAddress({
            mnemonic: seedPhrase,
            ss58Format: chain.substrate.ss58Format,
            derivationSuffix: purpose === "campaigns" ? CAMPAIGNS_SUBSTRATE_SUFFIX : undefined,
          })
        } else {
          throw new Error(`Unsupported chain: ${chain.chain}`)
        }

        byChain.set(chain.chain, address)
        addresses.push({
          chain: chain.chain,
          symbol: chain.symbol,
          name: chain.name,
          address,
          derivationPath: path,
          logo: chain.logo,
        })
      } catch (chainError) {
        log.error(`Error deriving ${chain.name}`, chainError)
      }
    }

    return { success: true, addresses }
  } catch (error) {
    log.error("Address derivation failed", error)
    return { success: false, addresses: [], error: String(error) }
  }
}

The seed phrase enters this function in the browser and never leaves. It is fed into BIP-32 / ed25519 derivation for Bitcoin, Ethereum, and Solana, and only the public addresses are returned to the page.

3. Identity

Definition. Identity is a human-readable reference that resolves to cryptographic ownership.

  • Usernames and profiles act as social pointers, not authority.
  • Identity is persistent across access methods.
  • Identity does not sign transactions or hold balances.

Referential is not peripheral. The statement that identity has no cryptographic authority is a safety property, not a priority ranking. Identity is the surface users actually touch: the handle they share, the profile they customize, the @name a payer types. Ownership is the cryptographic invariant beneath it. Both layers are load-bearing; they carry different loads. The separation in this document prevents identity from becoming authority, it does not demote identity to plumbing.

Public resolve() in the @nimimo/resolve SDK, a pure readpackages/resolve/src/client.ts
  async resolve(handle: string): Promise<ResolveAllResult>

  async resolve(handle: string, chain: Chain): Promise<ResolveSingleResult>

  async resolve(
    handle: string,
    chain?: Chain,
  ): Promise<ResolveAllResult | ResolveSingleResult> {
    const params = new URLSearchParams({ handle })
    if (chain) params.set("chain", chain)

    const url = `${this.baseUrl}/api/v1/resolve?${params}`
    return this.fetchWithRetry(url)
  }

Third-party wallets and apps call resolve(handle) to look up addresses. The SDK hits the read-only /api/v1/resolve endpoint and returns. It never signs, never holds funds, never authenticates, identity is a pointer, not an authority.

4. Recovery

Definition. Recovery is an optional, user-initiated export of encrypted ownership material.

  • Recovery artifacts are created locally and encrypted with a user-chosen password.
  • nimimo never stores or manages recovery material.
  • Recovery adds portability but introduces user responsibility.
Password-derived key using PBKDF2 + AES-GCM, all in the browserlib/recovery/crypto.ts
export async function derivePinKey(pin: string, salt: Uint8Array): Promise<CryptoKey> {
  const encoder = new TextEncoder()
  const pinBytes = encoder.encode(pin)

  const keyMaterial = await window.crypto.subtle.importKey("raw", pinBytes, "PBKDF2", false, ["deriveKey"])

  return window.crypto.subtle.deriveKey(
    {
      name: "PBKDF2",
      salt: salt as BufferSource,
      iterations: 600000,
      hash: "SHA-256",
    },
    keyMaterial,
    { name: "AES-GCM", length: 256 },
    false,
    ["encrypt", "decrypt"],
  )
}

The password is stretched with 600,000 rounds of PBKDF2-SHA-256, then bound to an AES-256-GCM key for symmetric encryption of the recovery payload. Every call to window.crypto.subtle runs in the user's browser; the password never crosses the network, and the server has nothing to forget. (The internal parameter is named pin for historical reasons, a PIN is a special case of a password; the implementation accepts any string.)


Axis Comparison Table

AxisPurposeCryptographic Authority
AccessEnter systemNone
OwnershipControl valueFunds only
IdentityHuman referenceNone
RecoveryRestore ownershipNone

"Cryptographic Authority: None" means the axis cannot sign, mutate state, or move value on its own. It does not mean the axis is unimportant to the product. Identity, in particular, carries none of the cryptographic authority and most of the product surface, that combination is the point, not an accident.

By separating these axes, nimimo achieves human usability without introducing custody or authority. Each axis exists independently, yet interoperates through well-defined, non-escalating boundaries.