- Update package.json version to v1.4.7
- Update README.md header to v1.4.7
- Update GEMINI.md version references to v1.4.7
- Update RECOVERY_PLAYBOOK.md version to v1.4.7
- Update SECURITY_AUDIT_REPORT.md version to v1.4.7
- Move documentation files to doc/ directory for better organization
- Add new documentation files: LOCAL_TESTING_GUIDE.md, SERVE.md, TAILS_OFFLINE_PLAYBOOK.md
- Add Makefile and serve.ts for improved development workflow
- Change button labels: 'Extra secure' / 'Normal' (better reflects defense-in-depth)
- Update tooltips to acknowledge CSP already blocks connections:
- 'Extra secure: Added manual blocking layer (CSP already blocks connections)'
- 'Normal: Relying on CSP to block connections'
- Update comment: Clarify button adds extra manual layer, not primary control
- More transparent about how security actually works (CSP does the real work)
- Fix CSP: Change connect-src from 'self' to 'none' (strict enforced blocking)
- Improve UX: Rename button from 'Blocked/Active' to 'Internet OFF/ON' (layman terms)
- Add clear tooltips explaining security implications:
- Internet OFF: "Network disabled - Maximum security (no data can leave device)"
- Internet ON: "Network enabled - Normal operation (browser CSP blocks connections)"
- Update comment to describe functionality: "Click to disable/enable internet access for maximum security"
This implements Option B from security audit: strict CSP enforcement with user-friendly interface explanations.
Route mic source through analyser into a silent sink to force processing on Safari/WebKit.
Add ScriptProcessorNode raw-PCM capture path for reliable RMS/entropy sampling.
Clamp/scale levels to avoid Infinity/opacity warnings and improve UI thresholds.
Deduplicate/guard AudioContext shutdown to prevent “Cannot close a closed AudioContext”.
Improve logging around context state, pipeline init, and capture source selection.
This commit introduces several improvements to the entropy generation and application state management:
1. **Implement Dice Entropy Stats Panel:**
- After generating entropy from dice rolls, a detailed statistics panel is now displayed for user review.
- This panel includes roll distribution, chi-square analysis, and a preview of the generated seed.
- Users can now choose to "Continue with this Seed" or "Roll Again" to discard and restart, improving user control and confidence in the entropy quality.
2. **Fix UI Layering and Overflow:**
- Increased the header's `z-index` to `z-[100]` to ensure it always remains on top of other components, fixing an issue where the "Reset All" button was inaccessible.
- Made the main content area for entropy components scrollable to prevent the new stats panels from overflowing the viewport on smaller screens.
3. **Improve "Reset All" Functionality:**
- The "Reset All" button now correctly resets the internal state of the `DiceEntropy` and `CameraEntropy` components.
- This is achieved by adding a `resetCounter` to the `App` state and passing it into the `key` prop of the entropy components, forcing a full remount on reset.
- Fix videoRef timing issue by using useEffect for video setup
- Stop animation loop on capture to prevent infinite warnings
- Fix null canvas reference in generateMnemonicFromEntropy
- Add stats review panel with continue/retake options
- Add seed generation explanation and blurred preview
- Implement seed generation from camera noise/entropy bits and enhance dice rolls with detailed statistical analysis
## Major Changes
### Mobile-First Responsive Design
- Converted entire app to mobile-first single-column layout
- Constrained max-width to 448px (mobile phone width)
- Black margins on desktop, centered content
- Removed all multi-column grids (md:grid-cols-3)
### Header Reorganization (3-Row Layout)
- Row 1: App logo + title + version
- Row 2: Security badges + action buttons (Empty, Reset)
- Row 3: Navigation tabs (Create, Backup, Restore, Blender)
- Replaced text buttons with emoji icons (📋 clipboard, 🙈 privacy mask)
- Consistent button sizing across all tabs
### Font Size Reductions
- Reduced all button text sizes for mobile density
- Main buttons: py-4 → py-3, added text-sm
- Labels: text-xs → text-[10px]
- Placeholders: consistent text-[10px] across all inputs
- Input fields: text-sm → text-xs, p-4 → p-3
### Create Tab Improvements
- Changed "GENERATE NEW SEED" from button-style to banner
- Left-aligned banner with gradient background
- Equal-width button grid (12/24 Words, Backup/Seed Blender)
- Used grid-cols-2 for consistent sizing
### Backup Tab Improvements
- Simplified drag-drop area with 📎 emoji
- Reduced padding and text sizes
- Cleaner, shorter copy
- PGP label font size: text-xs → text-[12px]
### SeedBlender Component
- Reorganized mnemonic input cards: textarea on row 1, buttons on row 2
- QR button (left) and X button (right) alignment
- Consistent placeholder text sizing (text-[10px])
- Shortened dice roll placeholder text
### HTTPS Development Server
- Added @vitejs/plugin-basic-ssl for HTTPS in dev mode
- Configured server to listen on 0.0.0.0:5173
- Fixed Web Crypto API issues on mobile (requires secure context)
- Enables testing on iPhone via local network
## Technical Details
- All changes maintain cyberpunk theme and color scheme
- Improved mobile usability and visual consistency
- No functionality changes, pure UI/UX improvements
This major update introduces a new "Create" tab for generating fresh BIP39 mnemonic seeds and significantly improves the entire application workflow, particularly the interaction with the Seed Blender.
**✨ New Features & Enhancements**
* **Create Seed Tab**:
* Add a new "Create" tab as the default view for generating 12 or 24-word BIP39 seeds.
* Implement a destination selector, allowing users to send the newly generated seed directly to the "Backup" tab for encryption or to the "Seed Blender" for advanced operations.
* The UI automatically switches to the chosen destination tab after generation for a seamless workflow.
* **Seed Blender Integration**:
* Generated seeds sent to the Seed Blender are now automatically added to the list of inputs.
* The Seed Blender's state is now preserved when switching between tabs, preventing data loss and allowing users to accumulate seeds from the Create tab.
* **Global Reset Functionality**:
* A "Reset All" button has been added to the main header for a global application reset.
* This action clears all component states (including the Seed Blender's internal state), passwords, generated data, and the in-memory session key, returning the app to a fresh initial state.
* **UI/UX Polish**:
* The "Use This Seed for Backup" button in the Seed Blender has been restyled to match the application's cyberpunk aesthetic and its text clarified.
* The "Create" tab UI is cleared automatically after a seed is generated and the user is navigated away, ensuring a clean slate for the next use.
**🔒 Security Fixes**
* **Auto-Clear Passwords**: Password and passphrase fields in both the "Backup" and "Restore" tabs are now automatically cleared from the UI and state after a successful encryption or decryption operation. This prevents sensitive data from lingering in the application.
* **Robust Seed Generation**: The seed generation process now uses the secure `crypto.getRandomValues` Web API to generate entropy before converting it to a mnemonic.
**🐛 Bug Fixes**
* **Seed Blender State**:
* Fixed a critical bug where the Seed Blender's internal state was lost when switching tabs. The component is now kept mounted but hidden via CSS.
* Resolved an issue where a seed sent from the "Create" tab could be added multiple times to the blender. A `useRef` guard now prevents duplicates.
* Corrected a race condition where transferring a blended seed to the "Backup" tab would clear the blender's state before the data could be used. The auto-clear has been removed in favor of the manual "Reset All" button.
- Eliminate all white/light boxes and backgrounds
- Fix drag-drop zone with neon cyberpunk colors (#00f0ff, #ff006e, #16213e)
- Fix restored mnemonic display with matrix green (#39ff14)
- Fix security options panel with dark gradient
- Fix all remaining slate-700/slate-800 labels to cyberpunk neon
- Fix info banners and text colors
- Update badge components with cyberpunk color scheme
- Apply consistent dark theme across all components
This commit addresses several issues related to the QR code scanner:
- Fixes a build failure by defining stable handlers (`handleRestoreClose`, `handleRestoreError`) for the QRScanner component in `App.tsx` using `useCallback`.
- Resolves a race condition that caused an `AbortError` when the scanner was initialized, particularly in React Strict Mode. This was fixed by ensuring all props passed to the scanner are stable.
- Implements more robust error handling within the `QRScanner` component to prevent crashes when `null` or `undefined` errors are caught.
- Updates documentation (`README.md`, `GEMINI.md`) to version 1.4.5.
- Replace BarcodeDetector with jsQR for raw binary byte access
- BarcodeDetector forced UTF-8 decoding which corrupted binary data
- jsQR's binaryData property preserves raw bytes without text conversion
- Fix regex bug: use single backslash \x00 instead of \x00 for binary detection
- Add debug logging for scan data inspection
- QR generation already worked (Krux-compatible), only scanning was broken
Resolves binary QR code scanning for 12/24-word CompactSeedQR format.
Tested with Krux device - full bidirectional compatibility confirmed.
Restores the `encryptToKrux` and `bytesToHex` functions that were accidentally removed during previous refactoring.
Their absence caused a build failure due to missing imports in other parts of the application. This commit re-adds the functions to ensure the application builds correctly.
Fixes the final decryption failure for Krux QR codes by correcting the salt used in key derivation.
- The KEF `unwrap` function now returns the raw `labelBytes` from the envelope.
- `KruxCipher` constructor now accepts these raw bytes and uses them directly as the salt for PBKDF2.
- This resolves a subtle bug where the string representation of the label was being incorrectly re-encoded, leading to an invalid key and failed decryption, even with the correct password.
Restores the `encrypt`, `bytesToHex`, and `encryptToKrux` functions that were accidentally removed in a previous refactor.
These functions are used by other parts of the application (`seedpgp.ts` and tests) and their absence caused a 'binding name not found' build error. This commit restores the original functionality, ensuring the application builds correctly and all features work as intended.
Overhauls the `krux.ts` library to correctly decrypt QR codes from Krux devices that use Base43 encoding and zlib compression.
- Replaces the previously buggy `krux.ts` with a clean implementation.
- `KruxCipher.decrypt` now correctly uses `pako.inflate` to decompress the payload for compressed KEF versions (e.g., v21), which was the final missing step.
- The `decryptFromKrux` function robustly handles both hex and Base43 encoded inputs.
- This resolves the 'decryption failed' error for valid Krux QR codes.
Implements zlib decompression for encrypted Krux QR codes, resolving the final decryption failure.
- Adds `pako` as a dependency to handle zlib (deflate/inflate) operations in JavaScript.
- Overhauls `krux.ts` to be a more complete port of the `kef.py` logic.
- `VERSIONS` constant is updated to include `compress` flags.
- `KruxCipher.decrypt` now checks the KEF version and uses `pako.inflate` to decompress the plaintext after decryption, matching the behavior of the official Krux implementation.
- This fixes the bug where correctly identified and decoded Krux payloads still failed to produce a valid mnemonic.
Implements support for Base43-encoded QR codes generated by Krux devices, resolving a bug where they were misidentified as invalid text.
- Adds a new `lib/base43.ts` module with a decoder ported from the official Krux Python implementation.
- Updates `detectEncryptionMode` to use the Base43 alphabet for more accurate `'krux'` format detection.
- Modifies `decryptFromKrux` to be robust, attempting to decode input as Hex first and falling back to Base43.
- This allows the Seed Blender to correctly parse and trigger the decryption flow for both Hex and Base43-encoded Krux QR codes.
Modifies the frame parsing logic to accommodate Base45-encoded SeedPGP payloads that are missing the 'SEEDPGP1:' prefix and CRC. This scenario occurs with certain QR code generators, such as some modes on Krux devices.
- `frameParse` now detects when the prefix is missing and treats the entire input as a raw Base45 payload.
- A `rawPayload` flag is added to the `ParsedSeedPgpFrame` type to signal this case.
- `frameDecodeToPgpBytes` now bypasses the CRC check when this flag is true, allowing the decryption to proceed.
- This resolves a bug where valid encrypted payloads were being rejected due to a missing frame structure.
Improves the user experience by providing clear, inline error messages when mnemonic validation fails.
- The validation logic now captures the error message from `mnemonicToEntropy`.
- The UI displays this message directly below the invalid input field.
- This addresses the ambiguity where an input was marked as invalid (red border) without explaining why, as seen in the user-provided screenshot.
Fixes a bug where plain text mnemonics and Krux QR codes were being misidentified as encrypted SeedPGP frames.
- The `detectEncryptionMode` function has been rewritten to be stricter and now correctly identifies 'text' as a type.
- It no longer defaults to 'pgp', which was causing plain text to be treated as an encrypted format.
- The `EncryptionMode` type in `types.ts` has been updated to include 'text'.
- This resolves issues where the UI would incorrectly ask for a password for plain text mnemonics and use the wrong decryption logic for Krux QRs.
This commit addresses several issues and implements new features for the Seed Blender based on user feedback.
- **Flexible QR Scanning**: `QRScanner` is now content-agnostic. `SeedBlender` detects QR content type (Plain Text, Krux, SeedPGP) and triggers the appropriate workflow.
- **Per-Row Decryption**: Replaces the global security panel with a per-row password input for encrypted mnemonics, allowing multiple different encrypted seeds to be used.
- **Data Loss Warning**: Implements a confirmation dialog that warns the user if they try to switch tabs with unsaved data in the blender, preventing accidental data loss.
- **Final Mnemonic Actions**: Adds 'Transfer to Backup' and 'Export as QR' buttons to the final mnemonic display, allowing the user to utilize the generated seed.
- **Refactors `SeedBlender` state management** around a `MnemonicEntry` interface for robustness and clarity.
Adds the missing state declarations (`useState`) and handler functions for the dice input and final mixing steps.
The previous implementation included JSX that referenced these variables and functions before they were declared, causing a 'Can't find variable' runtime error. This commit defines the necessary state and logic to make the component fully functional.
Refactors the crypto module loading in `seedblend.ts` to be truly isomorphic and prevent browser runtime errors.
- Replaces the static Node.js `crypto` import with a dynamic `import()` inside a singleton promise (`getCrypto`).
- This ensures Vite does not externalize the module for browser builds, resolving the 'Cannot access \'crypto.webcrypto\' in client code' error.
- The browser will use its native `window.crypto`, while the Node.js test environment dynamically loads the `crypto` module.
- All tests continue to pass, verifying the fix.
Implements a new 'Seed Blender' feature that allows users to securely combine multiple BIP39 mnemonics and enhance them with dice roll entropy.
- Adds a new 'Seed Blender' tab to the main UI.
- Implements a multi-step workflow for inputting mnemonics (manual/QR) and dice rolls.
- Provides live validation and previews for blended seeds and dice-only entropy.
- Includes statistical analysis of dice rolls (chi-square, distribution) and pattern detection for quality assessment.
- The core logic is a 1-to-1 port of the reference Python implementation, using the Web Crypto API for browser compatibility and Node.js for testing.
- A full suite of unit tests ported from the reference implementation ensures correctness and deterministic outputs.