Motebit

Identity & Cryptography

How motebit creates and manages persistent sovereign identity

Session tokens expire. API keys are shared secrets managed by someone else. A motebit agent owns an Ed25519 keypair — the private key is never sent to a relay in the clear (pairing moves it to a new device end-to-end encrypted), and the identity persists across sessions, devices, and intelligence providers. This guide explains how the cryptographic identity works, from first launch through multi-device sync.

For the conceptual foundation, see Identity. For where identity sits in the package architecture, see Architecture.

The identity bootstrap

On first launch, the system runs a shared bootstrap protocol (bootstrapIdentity in @motebit/core-identity):

  1. Generate an Ed25519 keypair using @noble/ed25519 — a zero-dependency, audited library. The same signing scheme used by SSH, Signal, and WireGuard.
  2. Store the private key in the platform's secure storage. On desktop, ~/.motebit/dev-keyring.json — an owner-only (mode 0600 on Unix; Windows is best-effort) file, access-controlled rather than application-encrypted; the OS keychain is not used yet (its enablement is a separate arc gated on real-device testing, because keychain providers differ in ways that can lose a key). On CLI, PBKDF2-encrypted as cli_encrypted_key in ~/.motebit/config.json. On mobile, expo-secure-store (iOS Keychain / Android Keystore, no fallback). On the web, browser storage. Outside the CLI's encrypted cli_encrypted_key, the private key never appears in config.json; it crosses the network only when pairing transfers it to a new device, end-to-end encrypted (X25519) through the relay, which cannot read it; on desktop the key file is access-controlled but not application-encrypted (moving it into the OS keychain is tracked as its own arc — see security boundaries).
  3. Derive the motebit_id from the public key — a UUIDv8 (RFC 9562) built from the first 16 bytes of SHA-256(public_key) with the version and variant bits set (deriveSovereignMotebitId in @motebit/crypto). The id is a self-certifying commitment to the genesis key: anyone can recompute it offline, and it is recoverable from the key's seed. A random device_id is generated alongside it. Identities minted before this default carry a random UUID v7 motebit_id that is not bound to any key; they remain valid identifiers.
  4. Register the device against the identity, binding the device's public key to the motebit_id.
  5. Write config — the motebit_id, device_id, and public key are persisted to ~/.motebit/config.json (non-secret metadata only).

On subsequent launches, the identity loads from config and the keypair from the keyring. No network round-trip required.

The motebit.md identity file

The motebit.md file is a human-readable, cryptographically signed identity document conforming to the motebit/identity@1.0 specification. It declares who the agent is, what it may do, and how it governs itself.

The file has two parts:

  • YAML frontmatter — identity metadata, governance thresholds, privacy rules, memory configuration, and registered devices
  • Signature comment — <!-- motebit:sig:motebit-jcs-ed25519-hex-v1:{hex_signature} --> on the line immediately following the closing ---. The inline cryptosuite identifier makes the suite explicit on the wire so post-quantum migration is a registry addition, not a wire-format break.

The signature covers the canonical JCS (RFC 8785) JSON serialization of the parsed frontmatter under the motebit-jcs-ed25519-hex-v1 cryptosuite. Any modification — even a single character — invalidates it. The public key is embedded in the frontmatter, making the file self-contained: verification requires no external service. See the Identity Standard guide for the full file format, creation commands, and verification workflow.

Multi-device registration

A single motebit identity spans multiple devices. Each device has its own Ed25519 keypair — there is no shared private key.

StepWhat happens
Device registrationThe device generates a keypair and registers its public key with the IdentityManager. The device receives a device_id and device_token.
PairingA 6-character code exchange, relay-mediated, with no shared secrets. The new device proves it holds a valid keypair.
Sync authenticationTo communicate with the sync relay, the device signs a short-lived token with its private key. The relay verifies the signature against the registered public key.

Devices are listed in the motebit.md file under the devices array, each with its own device_id, name, public_key, and registered_at timestamp.

Signed tokens

Signed tokens authenticate sync requests between a device and the relay server. They are lightweight, short-lived, and require no server-side secret storage.

Format: base64url(payload).base64url(signature)

Payload fields:

All fields below are required — a verifier MUST reject a token missing any of them (spec auth-token@1.0 §3). createSignedToken stamps only suite; every other field is the caller's to supply, so mint with mintAudienceToken (which fills jti, iat and exp and binds aud) rather than hand-assembling a {mid,did,iat,exp} payload — that would be rejected for missing jti/aud.

FieldRequiredDescription
midyesThe motebit_id
didyesThe device_id
iatyesIssued-at timestamp (milliseconds since epoch)
expyesExpiration timestamp (default: 5 minutes from iat)
jtiyesUnique token nonce (UUID v4 recommended) — prevents replay. Must be unique per token.
audyesAudience — the endpoint/operation the token authorizes (e.g. "task:submit"). Prevents cross-endpoint replay.
suiteyesCryptosuite, always "motebit-jwt-ed25519-v1" today. Signed; missing/unknown is rejected fail-closed.

The device signs the raw UTF-8 bytes of the JSON payload (not the base64url form) with its Ed25519 private key. The relay verifies in two steps: (1) parse the base64url payload without crypto to extract the device_id — O(1) lookup; (2) verify the Ed25519 signature against the device's registered public key. Expired tokens, and tokens missing jti/aud, are rejected.

Encryption at rest

Sensitive data is encrypted before leaving the agent boundary:

ContextMechanism
Sync payloadsAES-256-GCM with a 256-bit random key and 12-byte nonce. The 16-byte authentication tag is appended to the ciphertext.
CLI private keyPBKDF2 key derivation (600,000 iterations, SHA-256) from a user passphrase, then AES-256-GCM encryption.
MobilePrivate key stored in expo-secure-store (iOS Keychain / Android Keystore, hardware-backed where available). No fallback.
DesktopPrivate key stored in the owner-only (0600) ~/.motebit/dev-keyring.json — access-controlled, not application-encrypted. The OS keychain is not used yet: enabling it is its own arc, gated on real-device testing.

All cryptographic operations use Web Crypto API or @noble/hashes and @noble/ciphers — zero-dependency, audited implementations. Sensitive byte arrays are securely erased (overwritten with random data, then zeroed) after use.

Verification

Anyone can verify a motebit's identity. The @motebit/crypto package has zero monorepo dependencies — only @noble/ed25519.

# CLI verification
npx create-motebit verify motebit.md
// Programmatic verification
import { verify } from "@motebit/crypto";
import fs from "node:fs";

const result = await verify(fs.readFileSync("motebit.md", "utf-8"));
// `verify` auto-detects the artifact and returns a union discriminated on
// `type`; narrow to "identity" before reading identity-file fields.
if (result.type === "identity" && result.valid) {
  console.log("Verified:", result.identity?.motebit_id);
}

A valid identity result includes a did field — the W3C did:key derived from the Ed25519 public key. This bridges motebit to ecosystems that use Decentralized Identifiers (Verifiable Credentials, DIDComm, MCP-Identity).

if (result.type === "identity" && result.valid) {
  console.log(result.did); // "did:key:z6Mk..."
}

A valid signature proves the holder has the private key. It does not prove the holder is trustworthy. Trust is accumulated at the application layer through history, governance, and memory — not by the identity file alone.

Key management

ConcernApproach
Key storageMobile: iOS Keychain / Android Keystore. Desktop: ~/.motebit/dev-keyring.json, owner-only (0600), access-controlled rather than application-encrypted; the OS keychain is not used yet. CLI: PBKDF2-encrypted (cli_encrypted_key in ~/.motebit/config.json). Web: browser storage (IndexedDB, wrapped by a non-extractable WebCrypto key).
Key rotationSigned succession records (spec §3.8). The old keypair signs a tombstone declaring the new keypair as successor — both keys sign the canonical payload. The motebit_id persists across rotations. Rotate via motebit rotate (CLI) or npx create-motebit rotate (scaffolder). No centralized revocation — succession chains are verifiable by anyone.
Guardian recoveryEnterprise agents can declare an organizational guardian (spec §3.3). If the primary key is compromised, the guardian signs a recovery succession record — rotating to a new key without the compromised key. Specified, but currently suspended on the reference relay (503 GUARDIAN_RECOVERY_SUSPENDED); the recovery seed (motebit restore) is the recovery path that works today. Guardian revocation requires dual authorization (both keys sign).
Secure erasuresecureErase() overwrites byte arrays with random data then zeros before garbage collection.
Private key scopeThe private key never appears in motebit.md. It crosses the network only inside an end-to-end-encrypted pairing transfer, and enters a config file only as the CLI's passphrase-encrypted cli_encrypted_key.

On this page