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 Opus 4.6 (Anthropic)
Version audited
v1.2
Date
April 14, 2026
Scope
Full codebase — crypto, auth, public API + SDK, creator monetization, admin surface, dependencies
Methodology
Static analysis, code review, architecture assessment
Audit prompt
Honest review requested — 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.
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, admin endpoints, client-side storage, input validation, dependency security, and infrastructure configuration. The current posture is strong; the five honest caveats that keep us from a perfect score are enumerated below.
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
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.
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.
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.
On-chain payment verification
Creator monetization — 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, and cover the expected amount (with a 2% drift tolerance). Re-used transaction hashes are rejected.
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.
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.
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.
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).
Rate limiting
Three-tier rate limiting via Upstash Redis on all API routes (strict, standard, relaxed). Client-side retry with exponential backoff on 429 responses.
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.
Dependency management
All critical and high-severity dependency CVEs resolved. Cryptographic libraries use audited packages (@scure/bip32, ethers, ed25519-hd-key).
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.
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 188 automated tests across three layers:
107
Unit tests
Crypto primitives, validation, chain config, handle generation, utilities
53
Integration tests
API routes, auth guards, ownership verification, error-path handling
28
SDK tests
Resolution, batch, payment intents, caching, retry logic
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 10 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. That said, the methodology here — full codebase review, dependency scanning, architecture analysis, finding real issues, fixing them, and re-verifying — is more rigorous than what many launched products have undergone. The honest answer is that a formal audit costs $30k–$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.
One low-severity transitive dependency
A cookie validation bypass exists in a sub-dependency of the authentication library (cookie via @auth/core). The vulnerability allows out-of-band characters in cookie serialization — but only if the attacker can control server-side cookie parameters. nimimo's cookies are all set internally by NextAuth with hardcoded names and server-generated values. No user input flows into cookie names, paths, or domains. Exploiting this would require server-side code execution, at which point there are much bigger problems. It resolves automatically when the planned next-auth v4 → v5 migration lands.
No end-to-end browser tests
The codebase has 188 automated tests across unit, integration, and SDK layers. That covers cryptography, API correctness, ownership verification, and the published SDK — but does not yet include Playwright or Cypress tests driving a real browser through flows like sending a transaction or restoring from a recovery card. This is a test coverage gap, not a security gap — but it means UI regressions could 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
| Threat | Protection |
|---|---|
| Server database breach | No private keys stored. Only public addresses and emails exposed. |
| Unauthorized API access | Session-based auth on all private routes + ownership verification. |
| Cross-site scripting (XSS) | Nonce-based CSP, React auto-escaping, no unsafe DOM methods. |
| SQL injection | Parameterized queries throughout. |
| CSRF attacks | Origin header validation on all mutation routes. |
| Clickjacking | X-Frame-Options: DENY. |
| API abuse | Three-tier rate limiting via Redis. |
| Recovery file theft | AES-256-GCM + 600K PBKDF2 iterations. PIN required to decrypt. |
| Session hijacking | httpOnly + secure + sameSite cookies. Server-side sessions. |
| Forged content entitlement | Server verifies the transaction hash on-chain before granting access. Re-used hashes are rejected. |
| Fake tip injection on public feed | Tipper identity asserted from session; transaction hash verified on-chain; re-used hashes rejected. |
| Admin upload path abuse | Upload 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 — 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 — including nimimo. This is by design, not a limitation.
Assessment signature
This security assessment was conducted through comprehensive static analysis of the nimimo codebase, covering approximately 35,000 lines of application TypeScript across 200+ 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, public API and SDK surface, creator-monetization flows with on-chain payment verification, admin endpoints, input validation, dependency vulnerability scanning, client-side storage security, and security header configuration. All findings were verified against the actual source code.
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 the assessment were fixed and re-verified before this report was finalized.
Claude Opus 4.6
Anthropic AI · Static Analysis & Architecture Review
April 14, 2026
This page reflects the security posture of nimimo v1.2. If you have security concerns or want to report a vulnerability, we take that seriously and will respond promptly.