nimimo Logonimimo

Security Audit

AI-assisted security assessment of nimimo's architecture, cryptographic implementation, and operational security controls.

As of May 4, 2026, the cryptographic core of nimimo is open source under AGPL-3.0 at github.com/chriszemmel/nimimo-core. Every cryptographic primitive, key-derivation routine, and client-side authorization check named in this audit is inspectable line-by-line. You no longer have to take the findings on faith. You can re-run them yourself.

Pass

Strong posture across every reviewed surface

92

/ 100

Assessed by

Claude (Anthropic)

Version audited

v1.2.5

Date

September 14, 2026

Scope

Full codebase, covering crypto, auth, the public API and SDK, creator monetization, campaigns, the admin surface and dependencies

Methodology

Static analysis, code review, architecture assessment

Audit prompt

Honest review requested, with no constraints on findings or severity

Summary

nimimo is a non-custodial identity, wallet, and creator monetization platform. The core security property, that the server never has access to user private keys or seed phrases, is architecturally guaranteed. A full server database compromise would expose only public addresses and email addresses. No funds can be stolen.

This revision was assessed against the current build. Every claim below was checked in the source rather than carried over from the previous revision, and every control described is one that is live today. Findings from the review were fixed and re-verified before this page was updated, with regression tests in CI.

The assessment covers cryptographic primitives and key management, authentication and ownership verification, the public v1 API and published SDK, the creator-monetization surface with on-chain payment verification, campaigns and the operator-funded gas tank, admin endpoints, client-side storage, input validation, dependency security, infrastructure configuration, and the machine-facing surface that automated clients read: status codes, the published API description, and the discovery files. The caveats that keep us from a perfect score are enumerated below, along with two open items accepted as residual risk.

Architecture

nimimo separates four concerns into independent layers: Access (how you log in), Identity (your human-readable handle), Ownership (cryptographic control over keys), and Recovery (restoration after device loss). This separation means losing access does not mean losing ownership, and identity can persist across key rotations.

Client-side key generation

BIP-39 mnemonic (256-bit entropy) generated entirely in the browser via WebCrypto. Private keys are never transmitted to or stored on the server.

Device-bound encryption

Mnemonics encrypted with AES-256-GCM using a key derived via PBKDF2 (600,000 iterations) from device-specific material. Keys marked non-extractable via WebCrypto.

Multi-chain derivation

Bitcoin (BIP-84 P2WPKH), Ethereum (EIP-55), and Solana (Ed25519) addresses derived on-demand from the HD wallet. Derived keys exist in memory only during derivation.

Offline recovery card

Encrypted PDF with embedded QR code, protected by a separate user-chosen PIN with its own PBKDF2 derivation (600,000 iterations). Generated and decrypted fully offline; the server never sees recovery material.

Security controls

Pass

Non-custodial architecture

The server stores only public addresses and account metadata. Private keys, seed phrases, and encryption keys never leave the browser. This is enforced by architecture, not policy.

Pass

Encryption standards

AES-256-GCM with authenticated encryption (AAD binding to ownership ID). PBKDF2-SHA256 with 600,000 iterations for key derivation (exceeds OWASP 2023+ minimum). Separate key material and salt.

Pass

Authentication and authorization

Private routes require a server-side session; ownership-scoped routes additionally verify the session owns the target resource via the ownership_users junction table (requireOwnership in lib/auth-guard.ts). Ownership claims are never accepted from the request body; they are derived from the session.

Pass

On-chain payment verification

Creator monetization, meaning content unlocks and tips, verifies the client-supplied transaction hash on-chain server-side. The transaction must be confirmed, transfer to the creator's on-file payout address, cover the expected amount (with a 2% drift tolerance), and have been mined after the intent that claims it. A two-hour recency window applies where there is no intent. Every grant path claims the hash in one shared consumed_payments table before granting, so a proof is spendable exactly once across tips, entitlements and intents alike.

Pass

Admin surface

Every /api/admin/* route calls requireAdmin against a hardcoded allowlist. The SQL console is additionally gated by a timing-safe password compare. File uploads are prefix-locked to admin-uploads/ on both write and delete. External service keys (LLM, image gen, X) live server-only.

Pass

Published SDK

@nimimo/resolve pins the default API host, exposes no credentials, contains no unsafe eval or prototype traversal, and retries with exponential backoff on rate-limit responses.

Pass

Content Security Policy

Nonce-based CSP with strict-dynamic for scripts. No unsafe-eval. HSTS, X-Frame-Options DENY, X-Content-Type-Options nosniff, restrictive Permissions-Policy. Policy construction is shared, so every page path including the /@handle share URL is covered by the same policy.

Pass

CSRF and input validation

Origin header validation on all mutation routes. Zod schema validation on all API endpoints. Parameterized SQL queries throughout (no injection vectors).

Pass

Rate limiting

Three-tier rate limiting via Upstash Redis on all API routes (strict, standard, relaxed), with every route family explicitly tiered. Client IP is read from cf-connecting-ip, which the CDN sets and strips from incoming requests, so per-IP controls key on an address the caller cannot choose. Client-side retry with exponential backoff on 429 responses.

Pass

API key security

Third-party API keys passed via Authorization headers, not URL paths. Sensitive keys stripped from all log output. Error responses return sanitized messages only.

Info

Dependency management

Zero critical advisories. Seven high-severity advisories remain, all transitive except two in nodemailer, which is held back until the transactional email path has test coverage. Cryptographic libraries use audited packages (@scure/bip32, ethers, ed25519-hd-key).

Info

Client-side storage

Encrypted data stored in IndexedDB, protected by browser same-origin policy. Device encryption material is unencrypted in IndexedDB, a deliberate usability tradeoff mitigated by nonce-based CSP preventing XSS-based extraction.

Info

JavaScript memory limitations

Decrypted mnemonics exist as JavaScript strings during derivation. JavaScript cannot reliably zero memory. This is a fundamental language constraint shared by all browser-based wallets. Mitigated by on-demand decryption with minimal exposure window.

Testing coverage

The codebase includes 601 automated tests: 545 in the app suite, 28 in the published SDK package, and 28 driving a real browser end to end.

466

Unit tests

Crypto primitives, validation, chain config, handle generation, payer binding, rate-limit tiers, static-asset routing, the transactional email path, the published API description, content negotiation

79

Integration tests

API routes, auth guards, ownership verification, payment freshness and single-spend, error-path handling

56

SDK and browser

Resolution, batch, payment intents, caching, retry logic, plus end-to-end identity creation, derivation, auth routing, and the machine-facing surface: status codes, content types and response headers

Why not 100?

A perfect score would mean there is nothing left to improve. That is not the case. Here is what costs nimimo the remaining 8 points:

No formal third-party audit

This assessment is thorough but it was conducted by an AI, not a specialized security firm with manual penetration testing. A human-led audit remains the gold standard for production financial software. The methodology here is full codebase review, dependency scanning, architecture analysis, finding real issues, fixing them, and re-verifying each fix at the path it was found. That is more than many launched products have had, and it is still not a substitute for an adversarial human team. The honest answer is that a formal audit costs $30k to $100k and is not feasible for a solo-developed project at this stage.

Device encryption key stored unencrypted

The key material used to decrypt your seed phrase is stored in the browser's IndexedDB without its own layer of encryption. This means if an attacker achieves JavaScript execution on nimimo.com, they could theoretically access it. In practice, this requires bypassing the nonce-based Content Security Policy first, which blocks inline scripts and unauthorized code. The alternative (requiring a PIN on every page load) would make the app unusable for daily use. Every browser-based wallet makes this same tradeoff. Hardware wallets exist for users who need stronger guarantees.

Five high-severity transitive advisories

Critical advisories are at zero, and distinct advisories are down from 81 to 30. Five high-severity ones remain, all transitive: preact via next-auth, fast-xml-builder via the AWS SDK, ws twice via polkadot, and browserslist twice via autoprefixer. None is reachable with attacker-controlled input on a path we expose today, which is an argument about our usage rather than about the packages, and it stops holding if that usage changes. Each is recorded with the release that would clear it, and a gate in CI, running on every change and on a weekly schedule, fails on any critical and on any high that is not one of those five.

End-to-end coverage misses three flows

The codebase has 601 automated tests. Twenty-eight of them drive a real browser, covering identity creation, auth routing, derivation checked against the published BIP-84 and BIP-44 test vectors rather than against itself, and the machine-facing surface. What they do not cover yet is the send flow, the recovery card round trip, and switching identity. That is a test coverage gap rather than a security gap; it means UI regressions in those flows could still ship undetected.

JavaScript memory constraints

Decrypted seed phrases exist briefly in browser memory during key derivation. JavaScript cannot guarantee memory is zeroed after use; there is no equivalent of C's memset_s or Rust's Zeroize. This is a fundamental limitation of every browser-based wallet, not specific to nimimo. The exposure window is minimized: mnemonics are decrypted on-demand, used immediately for derivation, and never stored in component state or transmitted anywhere. A compromised device (malware with memory access) could extract it during that brief window. Users handling significant funds should consider hardware wallets for signing.

Threat model

ThreatProtection
Server database breachNo private keys stored. Only public addresses and emails exposed.
Unauthorized API accessSession-based auth on all private routes + ownership verification.
Cross-site scripting (XSS)Nonce-based CSP, React auto-escaping, no unsafe DOM methods.
SQL injectionParameterized queries throughout.
CSRF attacksOrigin header validation on all mutation routes.
ClickjackingX-Frame-Options: DENY.
API abuseThree-tier rate limiting via Redis.
Recovery file theftAES-256-GCM + 600K PBKDF2 iterations. PIN required to decrypt.
Session hijackinghttpOnly + secure + sameSite cookies. Server-side sessions.
Forged content entitlementServer verifies the transaction hash on-chain before granting access. Re-used hashes are rejected.
Fake tip injection on public feedTipper identity asserted from session; transaction hash verified on-chain; re-used hashes rejected.
Admin upload path abuseUpload paths prefix-locked to admin-uploads/. No directory traversal possible on write or delete.

Limitations and transparency

This assessment was conducted by an AI model (Claude, by Anthropic) through static code analysis and architecture review. The full codebase was reviewed, covering cryptographic implementations, authentication and ownership verification, the public v1 API and published SDK, the creator-monetization surface, admin endpoints, input validation, dependency security, and infrastructure configuration.

The cryptographic core of nimimo is open source under AGPL-3.0 at github.com/chriszemmel/nimimo-core. Keys, identity, and the device-bound encryption layer are inspectable. Anyone can audit the parts that touch funds, fork them, or self-host. The product surface (creator monetization, admin tooling, hosted UI) stays closed so the project can keep shipping; the core stays open so trust is verifiable rather than asserted.

nimimo is a self-custodial system. This means users are responsible for their own recovery files and device security. If you lose your device and have not created a recovery card, your funds cannot be recovered by anyone, nimimo included. This is by design, not a limitation.

Assessment signature

This security assessment was conducted through comprehensive static analysis of the nimimo codebase, covering roughly 87,000 lines of application TypeScript across 546 files (excluding static data such as BIP-39 word lists and identity generation pairs). The analysis included review of all cryptographic implementations, authentication and ownership verification, the public API and SDK surface, creator-monetization flows with on-chain payment verification, campaigns and the operator-funded gas tank, admin endpoints, input validation, dependency vulnerability scanning, client-side storage security, and security header configuration. Every finding was verified against the source at the file and line it names, and every fix was re-verified in the tree after it landed.

The audit was prompted with an explicit request for an honest, unconstrained review, covering security, bugs, architecture quality, innovation, and production readiness. No findings were suppressed or downplayed. Issues identified during this assessment were fixed and re-verified before the report was finalized.

Claude (Anthropic)

Static Analysis & Architecture Review

September 14, 2026

This page reflects the security posture of nimimo v1.2.5 as of September 14, 2026. If you have security concerns or want to report a vulnerability, we take that seriously and will respond promptly.