Release v4.1.5: Clean up docs, update release notes

- Remove old PLAN-4.1.x.md and RELEASE-4.1.1.md files
- Update RELEASE_NOTES.md for v4.1.5
- Highlights: dev docs, pull-image.sh auto-resize, 16GB images

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Aaron D. Lee
2026-01-07 01:17:25 -05:00
parent 4a27d0c182
commit 52e1a3dfbf
7 changed files with 37 additions and 1259 deletions

View File

@@ -1,538 +0,0 @@
# Stegasoo 4.1.0 Plan
## Overview
Version 4.1.0 is a feature release focusing on small-group deployment improvements and new utilities.
## Goals
1. ~~**Multi-User Support** - Admin can create up to 16 users for shared deployments~~ ✅ DONE
2. **Channel Key QR** - Easy visual sharing of channel keys via QR codes
3. ~~**CLI Channel Commands** - Manage channel keys from command line~~ ✅ DONE
4. **Advanced Tools** - Image/stego utilities (TBD)
---
## Feature 1: Multi-User Support ✅ COMPLETED
> Implemented in commit 7b33501. All requirements met.
### Requirements
- 16 users + 1 admin maximum (17 total)
- First user created at setup is always admin
- Admin can add/delete users, reset passwords
- Regular users can only change their own password
- No self-registration (admin-invite only)
### Database Changes
**Update User model in `frontends/web/models.py`:**
```python
class User(db.Model):
id = Column(Integer, primary_key=True)
username = Column(String(80), unique=True, nullable=False)
password_hash = Column(String(255), nullable=False)
role = Column(String(20), default='user') # 'admin' or 'user'
created_at = Column(DateTime, default=datetime.utcnow)
```
**Migration:** Add `role` and `created_at` columns. Existing users get `role='admin'`.
### New Routes
| Route | Method | Access | Description |
|-------|--------|--------|-------------|
| `/admin/users` | GET | admin | List all users |
| `/admin/users/new` | GET, POST | admin | Create user form |
| `/admin/users/<id>/delete` | POST | admin | Delete user |
| `/admin/users/<id>/reset-password` | POST | admin | Generate temp password |
### New Decorator
```python
# auth.py
def admin_required(f):
@wraps(f)
def decorated(*args, **kwargs):
if not current_user.is_authenticated:
return redirect(url_for('login'))
if current_user.role != 'admin':
flash('Admin access required', 'error')
return redirect(url_for('index'))
return f(*args, **kwargs)
return decorated
```
### UI Changes
**Navigation (for admin users):**
- Add "Users" link in navbar (visible only to admin)
**Account page (`/account`):**
- Admin sees link to user management
- All users see their own password change form
**New template: `templates/admin/users.html`:**
- Table: Username | Role | Created | Actions
- Actions: Reset Password, Delete (disabled for self)
- "Add User" button (disabled if at 16 user limit)
- Show count: "3 of 16 users"
**New template: `templates/admin/user_new.html`:**
- Username field (email-style allowed)
- Password field (auto-populated with random 8-char, admin can override)
- Submit → confirmation page shows password once with copy button
### Validation
- Username: 3-80 chars, alphanumeric + underscore/hyphen + @/. for email-style
- Password: 8+ chars (same as current)
- Can't delete yourself
- Can't demote the last admin
- Deleting user immediately invalidates their sessions
---
## Feature 2: Channel Key QR
### Web UI
**About page additions:**
If `STEGASOO_CHANNEL_KEY` environment variable is set:
```
┌─────────────────────────────────────────┐
│ Channel Key │
│ │
│ ██████████████ Your server uses a │
│ ██ ██ private channel key. │
│ ██ ██████ ██ Share this QR with │
│ ██ ██████ ██ others to join. │
│ ██ ██ │
│ ██████████████ [Copy Key] [Download]│
│ │
│ Key: abc123...xyz │
└─────────────────────────────────────────┘
```
- QR generated server-side using `qrcode` library
- "Copy Key" copies text to clipboard
- "Download QR" saves as PNG
**Implementation:**
```python
# about route addition
@app.route('/about')
def about():
channel_key = os.environ.get('STEGASOO_CHANNEL_KEY', '')
channel_qr_b64 = None
if channel_key:
# Generate QR as base64 PNG
qr = qrcode.make(channel_key)
buffer = BytesIO()
qr.save(buffer, format='PNG')
channel_qr_b64 = base64.b64encode(buffer.getvalue()).decode()
return render_template('about.html',
channel_key=channel_key,
channel_qr=channel_qr_b64)
```
### CLI Commands
**New command group: `stegasoo channel`**
```bash
# Generate a new channel key
stegasoo channel generate
# Output:
# Channel Key: stg_abc123...xyz789
#
# ██████████████████
# ██ ██
# ██ ██████████ ██
# ...
#
# Set in environment: export STEGASOO_CHANNEL_KEY="stg_abc123..."
# Show current key (from env or argument)
stegasoo channel show
# Output:
# Channel Key: stg_abc123...xyz789
# Display QR in terminal (ASCII)
stegasoo channel qr
# Output: ASCII QR code
# Save QR as PNG
stegasoo channel qr -o channel-key.png
# Output: Saved to channel-key.png
# Explicit format selection
stegasoo channel qr --format ascii # Terminal (default)
stegasoo channel qr --format png -o - # PNG to stdout
```
**Implementation notes:**
- Use `qrcode[pil]` for PNG output
- Use `qrcode` with `print_ascii()` for terminal
- Read key from `--key` argument or `STEGASOO_CHANNEL_KEY` env var
- `generate` uses existing `generate_channel_key()` from `stegasoo.channel`
---
## File Changes Summary
### New Files
| File | Description |
|------|-------------|
| `frontends/web/templates/admin/users.html` | User management page |
| `frontends/web/templates/admin/user_new.html` | Add user form |
### Modified Files
| File | Changes |
|------|---------|
| `frontends/web/models.py` | Add `role`, `created_at` to User |
| `frontends/web/auth.py` | Add `@admin_required`, user management routes |
| `frontends/web/templates/base.html` | Add Users link for admins |
| `frontends/web/templates/account.html` | Add admin link |
| `frontends/web/templates/about.html` | Add channel key QR section |
| `src/stegasoo/cli.py` | Add `channel` command group |
---
## Testing Plan
### Multi-User
1. Fresh install → first user is admin
2. Admin can create users up to limit (16)
3. Admin can't create 17th user (shows error)
4. Regular user can log in, encode/decode
5. Regular user can't access `/admin/users`
6. Admin can reset user password
7. Admin can delete user
8. Admin can't delete self
9. Existing 4.0.2 databases upgrade correctly (single user becomes admin)
### Channel Key QR
1. About page shows nothing if no channel key
2. About page shows QR + key if channel key set
3. Copy button works
4. Download gives valid PNG
5. QR scans correctly to key value
### CLI
1. `channel generate` creates valid key + shows QR
2. `channel show` displays current key
3. `channel qr` outputs ASCII to terminal
4. `channel qr -o file.png` saves PNG
5. Commands work with `--key` override
6. Commands read from env var
---
## Feature 3: Advanced Tools
### Included Tools
| Tool | Web | CLI | Description |
|------|-----|-----|-------------|
| **Capacity Calculator** | ✓ | ✓ | Upload image → show DCT/LSB capacity |
| **Metadata Stripper** | ✓ | ✓ | Remove EXIF/metadata from image |
| **Stego Detector** | ✓ | ✓ | Analyze image for signs of hidden data |
| **Image Compare** | ✓ | - | Side-by-side before/after diff |
| **Header Peek** | ✓ | ✓ | Check for Stegasoo header without decrypting |
| **Batch Mode** | - | ✓ | Encode/decode multiple files |
### Web UI: `/tools` Page
New page with card-based layout:
```
┌─────────────────────────────────────────────────────────────┐
│ 🛠️ Advanced Tools │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 📏 Capacity │ │ 🧹 Metadata │ │
│ │ Calculator │ │ Stripper │ │
│ │ │ │ │ │
│ │ Check how much │ │ Remove EXIF │ │
│ │ data fits │ │ before encoding │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ 🔍 Stego │ │ 🔎 Header │ │
│ │ Detector │ │ Peek │ │
│ │ │ │ │ │
│ │ Analyze image │ │ Check for │ │
│ │ for hidden data │ │ Stegasoo data │ │
│ └─────────────────┘ └─────────────────┘ │
│ │
│ ┌─────────────────┐ │
│ │ ⚖️ Image │ │
│ │ Compare │ │
│ │ │ │
│ │ Before/after │ │
│ │ diff view │ │
│ └─────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
```
Each card opens a modal or expands inline for the tool interface.
### CLI Structure
```bash
# Capacity calculator
stegasoo capacity image.jpg
stegasoo capacity image.jpg --format json
# Metadata stripper
stegasoo strip image.jpg # Output to image_stripped.jpg
stegasoo strip image.jpg -o clean.jpg # Custom output
stegasoo strip image.jpg --in-place # Overwrite original
# Stego detector
stegasoo detect image.jpg
stegasoo detect image.jpg --verbose # Detailed analysis
# Header peek
stegasoo peek image.jpg
# Output: "Stegasoo DCT header detected" or "No Stegasoo header found"
# Batch mode
stegasoo encode --batch manifest.json # JSON with files + credentials
stegasoo decode --batch input_dir/ --out output_dir/
```
### Tool Details
#### Capacity Calculator
- Input: Image file
- Output: Dimensions, megapixels, DCT capacity, LSB capacity
- Web: Upload zone + results panel
- CLI: Table or JSON output
#### Metadata Stripper
- Input: Image file
- Output: Clean image (EXIF/metadata removed)
- Show what was removed (camera model, GPS, etc.)
- Preserve image quality
#### Stego Detector
- Input: Image file
- Analysis:
- Chi-square analysis (LSB detection)
- DCT coefficient histogram analysis
- Visual inspection hints
- Output: Likelihood score + findings
- Note: Detection is probabilistic, not definitive
#### Image Compare
- Input: Two images (original + stego)
- Output:
- Side-by-side view
- Difference overlay (amplified)
- Pixel-level stats (PSNR, SSIM)
- Web only (visual tool)
#### Header Peek
- Input: Image file
- Output: Header found (yes/no), mode (DCT/LSB), embedded size estimate
- Does NOT decrypt - just checks for valid header structure
- Useful for "is this a stego image?" without credentials
#### Batch Mode
- CLI only
- Manifest file (JSON) or directory-based
- Progress bar for multiple files
- Error handling per-file (continue on failure)
---
## Migration Notes
### Database Migration
For existing 4.0.2 installations:
```python
# migrations/add_user_role.py
def upgrade():
# Add columns with defaults
op.add_column('user', sa.Column('role', sa.String(20), default='user'))
op.add_column('user', sa.Column('created_at', sa.DateTime))
# Set existing users as admin (they were the first user)
op.execute("UPDATE user SET role = 'admin' WHERE role IS NULL")
op.execute("UPDATE user SET created_at = datetime('now') WHERE created_at IS NULL")
```
Or simpler: detect on startup, update schema automatically (current pattern).
---
## Out of Scope
- Per-user channel keys
- User groups/teams
- API authentication tokens
- User activity logging
- Password complexity rules beyond length
---
## Estimated Effort
| Component | Complexity |
|-----------|------------|
| Database schema change | Low |
| Admin routes + templates | Medium |
| Access control decorator | Low |
| About page QR | Low |
| CLI channel commands | Medium |
| Advanced Tools (TBD) | Medium-High |
| Testing | Medium |
---
## Decisions
1. **Temp password flow:** Password field auto-populates with random 8-char password. Admin can override if desired. Show password once on confirmation page.
2. **Session handling:** Yes - deleting a user immediately invalidates their active sessions (ban hammer).
3. **Username rules:** Sane requirements, email-style allowed. Validation: 3-80 chars, alphanumeric, underscore, hyphen, @ and . for email-style.
---
## Approval
- [x] Plan reviewed
- [x] Questions resolved
- [x] Ready to implement
## Progress
- [x] Multi-User Support (commit 7b33501)
- [x] Channel Key QR (Web UI) - added QR generator on About page
- [x] CLI Channel Commands
- [x] Saved Channel Keys (Web UI) - users can save/manage channel keys
- [x] Advanced Tools - Image Security Toolkit
- [x] CLI: `stegasoo tools capacity/strip/peek/exif`
- [x] API: `/api/tools/capacity`, `/api/tools/peek`, `/api/tools/exif/*`
- [x] WebUI: Tools page with tabbed interface
- [x] EXIF Editor with inline editing, clear all, save/download
---
## Architectural Improvements (4.1.0)
### Consolidated Channel Key Resolution
Moved `resolve_channel_key()` from 3 duplicate implementations to single source of truth in `src/stegasoo/channel.py`:
```python
# Library: src/stegasoo/channel.py
def resolve_channel_key(value, *, file_path=None, no_channel=False) -> str | None:
"""Unified channel key resolution - returns None (auto), "" (public), or key."""
def get_channel_response_info(channel_key) -> dict:
"""Get channel info dict for API/WebUI responses."""
```
Frontends now use thin wrappers that translate exceptions to their context (Click/HTTP).
### DCT Payload Pre-Check
Added `will_fit_by_mode()` pre-check to WebUI encode to fail fast with helpful error message instead of cryptic exception deep in DCT processing.
### EXIF Tools (Library Layer)
Added to `src/stegasoo/utils.py`:
- `read_image_exif(image_data)` - Read EXIF metadata as dict
- `write_image_exif(image_data, updates)` - Update EXIF fields (JPEG only)
Dependencies added: `piexif>=1.1.0`
---
## Action Item: Architectural Review ✅ DONE
Reviewed modules for consistency with Library → CLI → API → WebUI pattern:
| Module | Library | CLI | API | WebUI | Status |
|--------|---------|-----|-----|-------|--------|
| encode | ✓ | ✓ | ✓ | ✓ | Consistent |
| decode | ✓ | ✓ | ✓ | ✓ | Consistent |
| channel | ✓ | ✓ | ✓ | ✓ | Consolidated resolve_channel_key |
| tools | ✓ | ✓ | ✓ | ✓ | Complete |
| generate | ✓ | ✓ | - | ✓ | CLI has `stegasoo generate` |
Priority order: Developer/CLI → API integrator → WebUI end-user
---
## Admin Recovery System (4.1.0) ✅ DONE
Password reset capability for locked-out admins with multiple backup options.
### Library Layer (`src/stegasoo/recovery.py`)
```python
# Key generation and validation
generate_recovery_key() -> str # XXXX-XXXX-XXXX-... (32 chars)
hash_recovery_key(key) -> str # SHA-256 for storage
verify_recovery_key(key, hash) -> bool
# QR code (obfuscated - scans as gibberish)
obfuscate_key(key) -> str # XOR with RECOVERY_OBFUSCATION_KEY
deobfuscate_key(data) -> str | None
generate_recovery_qr(key) -> bytes # PNG with obfuscated data
extract_key_from_qr(image) -> str | None
# Stego backup (hide key in an image)
create_stego_backup(key, carrier_image) -> bytes
extract_stego_backup(stego_image, reference) -> str | None
```
### Database (`app_settings` table)
- `recovery_key_hash` - SHA-256 of recovery key (or null if disabled)
### Web Routes
| Route | Method | Description |
|-------|--------|-------------|
| `/setup/recovery` | GET, POST | Step 2 of initial setup |
| `/recover` | GET, POST | Password reset page |
| `/recover/stego` | POST | Extract key from stego backup |
| `/account/recovery/regenerate` | GET, POST | Generate new key |
| `/account/recovery/disable` | POST | Remove recovery option |
| `/account/recovery/stego-backup` | POST | Create stego backup |
### CLI Commands
```bash
stegasoo admin recover --db path/to/stegasoo.db # Reset password
stegasoo admin generate-key [--qr] # Generate key (reference)
```
### Security Model
1. Recovery key shown once during setup - only hash stored
2. QR codes XOR'd with `RECOVERY_OBFUSCATION_KEY` (fixed in constants.py)
3. Stego backups use fixed internal passphrase/PIN - security is obscurity
4. Instance-bound: recovery key hash must match in target database
5. Options: text file, QR image, stego image, or no recovery (most secure)

View File

@@ -1,250 +0,0 @@
# Stegasoo 4.1.2 Plan
## Release Theme
Polish and UX improvements after the 4.1.1 stability release.
---
## 1. Real Progress Bar for Encode/Decode
**Status:** Done
**Problem:** Users see elapsed time but no indication of how far along the operation is. Long DCT encodes on Pi can take 2-3 minutes with no feedback.
**Solution:** Polling + progress file approach
### Backend Changes
1. **dct_steganography.py** - Write progress during block loop:
```python
if progress_file and block_num % 50 == 0:
with open(progress_file, 'w') as f:
json.dump({"current": block_num, "total": total_blocks, "phase": "embedding"}, f)
```
2. **app.py** - New endpoints:
- `POST /encode` returns `job_id`, starts subprocess
- `GET /encode/progress/<job_id>` returns progress JSON
- `GET /encode/result/<job_id>` returns final result when done
3. **Subprocess wrapper** - Pass progress file path to encode/decode functions
### Frontend Changes
1. **stegasoo.js** - After form submit:
- Show progress bar (Bootstrap progress component)
- Poll `/encode/progress/{job_id}` every 500ms
- Update bar width and percentage text
- Show phase (hashing, embedding, encoding, etc.)
2. **Templates** - Add progress bar markup to encode.html and decode.html
### Files to Modify
- `src/stegasoo/dct_steganography.py`
- `frontends/web/app.py`
- `frontends/web/static/js/stegasoo.js`
- `frontends/web/templates/encode.html`
- `frontends/web/templates/decode.html`
---
## 2. Granular Decode Error Messages
**Status:** Done
**Problem:** Decode failures show generic "Decryption failed" - users don't know if it's wrong photo, wrong passphrase, wrong PIN, corrupted image, or format mismatch.
**Solution:** Bubble up specific error types from library to UI
### Implementation
- Added new exceptions: InvalidMagicBytesError, ReedSolomonError, NoDataFoundError, ModeMismatchError
- DCT decode now raises InvalidMagicBytesError for wrong magic bytes
- DCT decode now raises ReedSolomonError (renamed from reedsolo's) for corruption
- app.py catches specific exceptions with user-friendly messages:
- Invalid magic → "Try a different mode (LSB/DCT)"
- RS error → "Image too corrupted, may have been re-saved"
- Invalid header → "Image may have been modified"
- Decryption error → "Wrong credentials"
### Files Modified
- `src/stegasoo/exceptions.py` (new exceptions)
- `src/stegasoo/__init__.py` (exports)
- `src/stegasoo/dct_steganography.py` (raise specific exceptions)
- `frontends/web/app.py` (catch and display)
---
## 3. Mobile-Responsive Polish
**Status:** Done
**Problem:** UI works on mobile but has rough edges - cramped buttons, hard-to-tap targets, awkward layouts on small screens.
**Solution:** Targeted CSS/layout fixes for mobile breakpoints
### Areas to Improve
1. **Encode/Decode Forms:**
- Stack image drop zones vertically on mobile (currently side-by-side)
- Larger touch targets for file inputs
- Full-width buttons on small screens
- Passphrase input readable at smaller sizes
2. **Navigation:**
- Hamburger menu for mobile navbar (if not already)
- Sticky header doesn't eat too much screen
- Easy thumb reach for main actions
3. **Results/Output:**
- Download buttons full-width on mobile
- QR codes sized appropriately
- Click-to-copy message box works well with touch
4. **Drop Zones:**
- Larger tap targets
- Visual feedback for touch (not just hover)
- Camera integration hint on mobile ("Tap to take photo or choose file")
### Testing Targets
- iPhone SE (small)
- iPhone 14 (medium)
- iPad (tablet)
- Android Chrome
### Files to Modify
- `frontends/web/static/css/style.css` (or new mobile.css)
- `frontends/web/templates/encode.html`
- `frontends/web/templates/decode.html`
- `frontends/web/templates/base.html` (navbar)
---
## Testing Checklist
- [ ] Progress bar works on localhost
- [ ] Progress bar works on Pi (slower, more visible)
- [ ] Cancellation handling (what if user navigates away?)
- [ ] Error states display correctly
- [ ] Smoke test passes
---
## 4. Forced First-Login Setup
**Status:** Done
**Problem:** Users can navigate the app without creating an admin account first. Should force password setup before anything else.
**Solution:** Middleware/decorator that redirects to setup page if no users exist.
### Implementation
- Added `@app.before_request` hook that redirects to /setup if no users exist
- Skips redirect for static files and setup-related routes
### Files Modified
- `frontends/web/app.py` (added require_setup before_request hook)
---
## 5. Dropzone UX Fixes
**Status:** Done
**Problem:** Dropzone has some interaction bugs:
- Dropzone doesn't clear properly if first QR image fails
- Can't click on image preview to replace file (have to click surrounding border)
**Solution:** Fix JS event handling and state management
### Implementation
- Added click handler on preview images to trigger file input
- Made entire drop zone clickable (not just label)
- QR zone now resets after 2 seconds on error, allowing retry
- Clear file input on QR error so same file can be re-selected
### Files Modified
- `frontends/web/static/js/stegasoo.js`
---
## 6. Smoke Test Benchmarking
**Status:** Done
**Problem:** No way to measure encode/decode performance or track regressions.
**Solution:** Add timing to smoke tests using `hyperfine` or `time`.
### Implementation
- Added `--benchmark` flag to run encode/decode benchmarks after tests
- Added `--runs=N` flag to customize number of benchmark runs (default: 5)
- Uses hyperfine if available for precise timing with warmup
- Falls back to manual timing with bc if hyperfine not installed
- Outputs min/max/avg stats for both encode and decode operations
### Files Modified
- `tests/smoke-test.sh`
---
## 7. Docker Cleanup
**Status:** Done (4.1.1)
**Problem:** Docker build context is larger than needed (includes test images, rpi scripts, etc.)
**Solution:** Added `.dockerignore` and fixed volume permissions in Dockerfile
### Files Modified
- `.dockerignore` (created)
- `Dockerfile` (instance dir permissions)
---
## 8. Release Validation Script
**Status:** Done
**Problem:** Manual release checklist is error-prone. Need automated validation.
**Solution:** Script that runs through testable checklist items
### Features
- Run pytest
- Build and test Docker image
- SSH to Pi and run smoke test (optional, if PI_IP provided)
- Report pass/fail summary
### Files to Create
- `scripts/validate-release.sh`
---
## 9. Smoke Test Docker Support
**Status:** Done
**Problem:** Smoke test expects systemd service, doesn't auto-create admin for Docker.
**Solution:** Make smoke test Docker-aware
### Features
- Skip systemd checks if not on Pi/Linux with systemd
- Auto-detect fresh Docker (no users) and create admin via /setup
- Add `--docker` flag to skip Pi-specific checks
### Implementation
- Added `--docker` flag that sets localhost and skips SSH/systemd checks
- Docker health check verifies container responds with HTTP 200/302
- Header shows "Docker Smoke Test" in Docker mode
### Files Modified
- `rpi/smoke-test.sh`
---
## Notes
- Keep 4.1.2 focused - 9 features (9 done)
- Don't break DCT compatibility (4.1.1 RS format is stable)
- Test on Pi before release

View File

@@ -1,42 +0,0 @@
# Stegasoo 4.1.3 Plan
## Release Theme
Performance and admin features.
---
## 1. DCT Performance Optimizations
**Status:** Planned
**Problem:** DCT encode/decode can be slow on Pi, especially for large images.
**Ideas:**
- Vectorize block processing with NumPy
- Reduce Python loop overhead
- Parallel block processing (multiprocessing?)
- Profile and identify bottlenecks
- Consider Cython for hot paths
---
## 2. User Management UI
**Status:** Planned
**Problem:** No way for admin to manage users via UI. Currently need direct DB access.
**Features:**
- List all users
- Create new user (admin only)
- Delete user (admin only)
- Reset user password
- User activity/last login
---
## Notes
- These are heavier lifts than 4.1.2
- Profile before optimizing
- Consider security implications of user management

View File

@@ -1,165 +0,0 @@
# Stegasoo 4.1.4 Plan
## Build / Deploy
- [x] Pre-built Python 3.12 venv tarball for Pi (skip 20+ min compile) - see details below
- [x] Fixed partition sizing in flash script (16GB rootfs for faster imaging)
- [x] Rename `flash-pi.sh``flash-stock-img.sh` for clarity
- [x] pip-audit integration in release validation
### Pi venv Tarball Approach
1. Flash fresh Pi image, let it fully build (20+ min compile)
2. Once running and working, SSH in and create optimized tarball:
```bash
cd /opt/stegasoo
# Strip caches and tests (295MB → 208MB)
find venv/ -type d -name '__pycache__' -exec rm -rf {} + 2>/dev/null
find venv/ -type d -name 'tests' -exec rm -rf {} + 2>/dev/null
find venv/ -type d -name 'test' -exec rm -rf {} + 2>/dev/null
# Compress with zstd (208MB → 39MB)
tar -cf - venv/ | zstd -19 -T0 > /tmp/stegasoo-venv-pi-arm64.tar.zst
```
3. Pull tarball to host: `scp admin@pi:/tmp/stegasoo-venv-pi-arm64.tar.zst rpi/`
4. setup.sh auto-detects and extracts tarball if present in rpi/
5. Re-flash and test fresh build with pre-built venv (should be <2 min vs 20+)
## Features
- [x] QR channel key sharing (see detailed plan below)
- [ ] Role-based permissions: admin / mod / user
- [x] `stegasoo info` fastfetch-style command (version, service status, channel, CPU, temp, etc.)
- [ ] Better capacity estimates / pre-flight check before encode fails
---
## QR Channel Key Sharing - Implementation Plan
### Current State
- ✅ **CLI**: `stegasoo channel qr` generates ASCII/PNG QR for server channel key
- ✅ **Web UI (about.html)**: Client-side QR generator exists - input key, generate/show QR, download PNG
- ✅ **Account page**: Shows saved channel keys with fingerprint, rename, delete
- ❌ No role restrictions on QR sharing
- ❌ No QR button for saved keys on account page
- ❌ No QR scanning to import keys
### Design Decisions
**UI Placement** (avoiding encode/decode page crowding):
- Keep QR generator in **about.html** (already exists, logical place for tools)
- Add QR button to **account.html** saved keys (small icon, doesn't crowd)
- Both should be admin-only
**Role Restriction** (per user request):
- QR sharing = admin only (hide generator + saved key QR buttons from non-admins)
- Prerequisite: Need role-based permissions feature first
- Interim option: Just hide from non-admin users using existing `is_admin` flag
### Implementation Steps
#### Phase 1: Admin-only restriction (quick win)
1. **about.html**: Wrap QR generator section in `{% if is_admin %}` block
2. **Account route**: Pass `is_admin` to template (if not already)
3. **account.html**: Add small QR icon button to saved keys row (admin only)
- Opens modal with QR canvas (reuse qrcode.js pattern from about.html)
- Download PNG button in modal
#### Phase 2: QR Import (optional enhancement)
1. Add "Import via QR" button to account.html key-add section
2. Use device camera or file upload to scan QR
3. Decode and populate channel_key input field
4. Requires `pyzbar` on server OR client-side JS library like `jsQR`
### Files to Modify
```
frontends/web/app.py
- about() route: Add missing vars: is_admin, channel_configured,
channel_fingerprint, channel_source (BUG: currently not passed!)
- account() route: ✅ Already passes is_admin
frontends/web/templates/about.html
- Wrap channel key QR section in {% if is_admin %}
frontends/web/templates/account.html
- Add QR button to saved keys (admin only)
- Add QR modal (copy pattern from about.html)
- Include qrcode.min.js CDN script
```
### Bug Found During Research
The about.html template uses `channel_configured`, `channel_fingerprint`,
`channel_source` but the route doesn't pass them - always shows "public mode".
Fix this while implementing QR admin restriction.
### Exact Code Changes
**app.py - Fix about() route (around line 1564):**
```python
@app.route("/about")
def about():
from stegasoo.channel import get_channel_status
channel_status = get_channel_status()
# Check if user is admin (for QR sharing)
current_user = get_current_user()
is_admin = current_user.is_admin if current_user else False
return render_template(
"about.html",
has_argon2=has_argon2(),
has_qrcode_read=HAS_QRCODE_READ,
# Channel info (bugfix)
channel_configured=channel_status["configured"],
channel_fingerprint=channel_status.get("fingerprint"),
channel_source=channel_status.get("source"),
# Admin check for QR sharing
is_admin=is_admin,
)
```
### Template Changes Preview
**account.html - Add to saved key row:**
```html
{% if is_admin %}
<button type="button" class="btn btn-outline-info btn-sm"
onclick="showKeyQr('{{ key.channel_key }}')" title="Show QR">
<i class="bi bi-qr-code"></i>
</button>
{% endif %}
```
**about.html - Wrap existing section:**
```html
{% if is_admin %}
<!-- Channel Key QR Generator -->
<div class="card bg-dark border-secondary">
...existing QR generator...
</div>
{% endif %}
```
### Testing Checklist (Phase 1 Implemented)
- [ ] Non-admin users cannot see QR generator in about.html
- [ ] Non-admin users cannot see QR buttons on account page
- [ ] Admin users can generate QR for any saved key
- [ ] QR downloads work correctly
- [ ] QR scans correctly with phone camera
### Implementation Status
**Phase 1: COMPLETE** - Admin-only QR sharing implemented:
- `app.py`: Fixed about() route to pass channel status + is_admin
- `about.html`: QR generator wrapped in `{% if is_admin %}` with Admin badge
- `account.html`: QR button added to saved keys (admin only), modal + JS for generation/download
---
## Security
- [ ] Optional encryption for temp file storage (paranoid mode, config toggle)
## Docs
- [x] Update UNDER_THE_HOOD.md (v4.1 changes, channel keys)
- [ ] General docs refresh
## Ideas (maybe later)
- [ ] Stego detection tool
- [ ] Browser extension
- [ ] Pi snapshot/backup feature

View File

@@ -1,147 +0,0 @@
# Stegasoo 4.1.5 Plan
## Decode Progress Bar (Real Progress)
Mirror the encode async pattern for decode operations.
### Backend Changes
**1. Add async mode to `/decode` route (`app.py`)**
- Check for `async=true` form param
- Generate job_id, store job, submit to executor
- Return `{"job_id": ..., "status": "pending"}` immediately
**2. Add decode status/progress endpoints (`app.py`)**
```python
@app.route("/decode/status/<job_id>")
def decode_status(job_id):
# Return {"status": "pending|running|complete|error", "result": {...}}
@app.route("/decode/progress/<job_id>")
def decode_progress(job_id):
# Read from /tmp/stegasoo_progress_{job_id}.json
# Return {"percent": 0-100, "phase": "..."}
```
**3. Add `_run_decode_job()` background worker (`app.py`)**
- Similar to `_run_encode_job()`
- Pass `progress_file` param to decode function
- Store result/error in job dict
**4. Update decode functions to write progress (`lsb_steganography.py`, `dct_steganography.py`)**
Phases for decode:
- `"starting"` (0%)
- `"reading"` (10%) - reading stego image
- `"extracting"` (30%) - extracting hidden data
- `"decrypting"` (60%) - Argon2 + AES decryption
- `"verifying"` (80%) - HMAC verification
- `"finalizing"` (95%) - preparing output
- `"complete"` (100%)
### Frontend Changes
**5. Update decode form submission (`decode.html`)**
- Add async form handler like encode
- Call `Stegasoo.submitDecodeAsync(form, btn)`
**6. Add decode async methods (`stegasoo.js`)**
```javascript
submitDecodeAsync(form, btn) // POST with async=true, show modal
pollDecodeProgress(jobId) // Poll /decode/status, /decode/progress
```
Reuse existing:
- `showProgressModal('Decoding')`
- `updateProgress(percent, phase)`
**7. Handle decode result redirect**
- On complete: redirect to `/decode/result/{file_id}` or display inline
### Files to Modify
```
frontends/web/app.py
- Add async handling to /decode route (~line 1300+)
- Add /decode/status/<job_id> endpoint
- Add /decode/progress/<job_id> endpoint
- Add _run_decode_job() function
frontends/web/static/js/stegasoo.js
- Add submitDecodeAsync()
- Add pollDecodeProgress()
frontends/web/templates/decode.html
- Update form submit to use async mode
src/stegasoo/lsb_steganography.py
- Add progress_file param to decode()
- Write progress at each phase
src/stegasoo/dct_steganography.py
- Add progress_file param to decode()
- Write progress at each phase
```
### Testing Checklist
- [ ] Decode shows progress modal on submit
- [ ] Progress bar animates through phases
- [ ] Successful decode redirects to result
- [ ] Failed decode shows error in modal
- [ ] Works for both LSB and DCT modes
- [ ] Works for message and file payloads
- [ ] Progress file cleaned up after completion
---
---
## Browser Webcam QR Scanning
Add webcam-based QR code scanning for all key input fields.
### Use Cases
- Import channel key via QR scan on account page
- Scan QR codes instead of typing long keys
### Implementation
**1. Add JS QR scanning library**
- Use `jsQR` or `html5-qrcode` (client-side, no server needed)
- Include via CDN in base template
**2. Add camera button to channel key inputs**
- Account page: "Add Key" field
- Encode/decode pages: channel key selector (if manual input)
**3. Camera modal component**
- Request camera permission
- Live video preview
- Auto-detect QR and populate input field
- Close modal on successful scan
### Files to Modify
```
frontends/web/templates/base.html - Add QR library CDN
frontends/web/templates/account.html - Camera button + modal
frontends/web/static/js/stegasoo.js - QR scan methods
```
### Testing Checklist
- [ ] Camera permission prompt works
- [ ] QR detected and input populated
- [ ] Works on mobile browsers
- [ ] Graceful fallback if no camera
---
## Other 4.1.5 Ideas (if time)
- [ ] Role-based permissions: admin / mod / user
- [ ] Better capacity estimates / pre-flight check
- [ ] Stego detection tool
## Bugs / Nice to Have
- [ ] **flash-stock-img.sh 16GB resize not working** - partition still full SD size after flash, makes dd pull slow. Investigate resize2fs/parted logic and test fix.

View File

@@ -1,93 +0,0 @@
# Stegasoo 4.1.1 Release Notes
**Release Date:** January 5, 2026
## Highlights
- **Reed-Solomon Error Correction** - DCT steganography now includes RS error correction, making encoded images more resilient to minor corruption and compression artifacts
- **Completely Rewritten Pi Setup** - Fresh install tested and validated, works reliably from scratch
- **SSH Login Banner** - See your Stegasoo URL immediately on SSH login
## New Features
### Reed-Solomon Error Correction
DCT-encoded images now include Reed-Solomon error correction codes, allowing recovery from minor image corruption. This significantly improves reliability when images are shared through platforms that may slightly modify them.
### SSH Login Banner (MOTD)
When you SSH into your Stegasoo Pi, you'll now see:
```
___ _____ ___ ___ _ ___ ___ ___
/ __||_ _|| __| / __| /_\ / __| / _ \ / _ \
\__ \ | | | _| | (_ | / _ \ \__ \ | (_) || (_) |
|___/ |_| |___| \___//_/ \_\|___/ \___/ \___/
● Stegasoo is running
https://192.168.0.4
```
### Elapsed Time Counter
Encode/decode buttons now show elapsed time during operations.
### Click-to-Copy Decoded Message
Click the decoded message box to copy to clipboard (no button needed).
### Overclock Wizard Option
First-boot wizard now offers optional CPU overclocking for Pi 4/5 with active cooling.
## Improvements
### Setup Script (setup.sh)
- Fixed pyenv Python path resolution (handles 3.12 → 3.12.12 mapping)
- Changed default install location to `/opt/stegasoo`
- Fixed jpegio build order (clone stegasoo first, then build jpegio into venv)
- Added python3-dev to dependencies
- Added btop for system monitoring
- Shows `/setup` URL at completion for admin account creation
### Sanitize Script
- Now clears port 443 iptables redirect (clean slate for wizard)
- Removes overclock settings before imaging
### Documentation
- Updated all docs to reference `/opt/stegasoo` path
- Added pre-setup steps (chown /opt, install git)
- Added Pi 4 performance baseline (~60s for 10MB JPEG)
### About Page
- Redesigned "Limits & Specs" section with key stats cards and accordion
## Bug Fixes
- Fixed DCT steganography for non-8-aligned images
- Fixed MOTD port detection (was using iptables which requires root)
- Fixed smoke test `--443` flag parsing
## Performance
On a Raspberry Pi 4 at 2GHz with USB 3.0 NVMe:
- ~50 seconds to encode a 10MB JPEG
- ~60 seconds to decode a 10MB JPEG
- Full encryption: passphrase + PIN + reference photo
## Upgrade Notes
If upgrading from 4.1.0:
```bash
cd /opt/stegasoo # or ~/stegasoo
git pull origin 4.1
```
For fresh installs, see the [Pi README](rpi/README.md).
## Pre-built Images
- `stegasoo-rpi-4.1.1_20260105-2.img.zst` - Raspberry Pi 4/5 image
Flash with:
```bash
zstdcat stegasoo-rpi-4.1.1_20260105-2.img.zst | sudo dd of=/dev/sdX bs=4M status=progress
```
---
Full changelog: [v4.1.0...v4.1.1](https://github.com/adlee-was-taken/stegasoo/compare/v4.1.0...v4.1.1)

View File

@@ -1,14 +1,41 @@
## Stegasoo v4.1.3
## Stegasoo v4.1.5
### Fixes
- **SSL Certificate Generation**: First-boot wizard now properly generates self-signed certs when HTTPS is enabled
- **Download Bug Fixed**: No more "File expired or not found" errors - fixed multi-worker temp file sharing
- **Docker Build**: Reduced build context from 2.3GB to ~900KB
### Developer Experience
- **Educational Code Comments**: Core modules now include detailed explanations
- DCT: zig-zag coefficient diagrams, QIM embedding math, Reed-Solomon "Voyager" reference
- LSB: visual bit manipulation examples, ChaCha20 pixel selection
- Crypto: multi-factor KDF flow diagrams, Argon2id memory-hardness reasoning
- CLI/Web: architectural patterns for future contributors
### Improvements
- Docker memory limits increased to 2GB (prevents OOM on large DCT operations)
- Decode button now shows loading spinner during processing
- Headless Pi flash script with Trixie/NetworkManager support
### Raspberry Pi Improvements
- **Streamlined Image Creation**: `pull-image.sh` now handles everything
- Auto-resizes rootfs to exactly 16GB (consistent images from any SD card)
- Disables Pi OS auto-expand
- Compresses with zstd
- Optional .zst.zip wrapper for GitHub releases
- **16GB Minimum**: Pre-built images are now 16GB (was variable)
- **Host Requirements**: `rpi/host-requirements.txt` documents all dependencies
- **Test Automation**: `kickoff-pi-test.sh` for one-command flash+test cycles
### MOTD Polish
- Dynamic temperature emoji (ice/cool/fire based on CPU temp)
- Rocket emoji for service status
- Cleaner formatting
### Raspberry Pi Image
Download `stegasoo-rpi-4.1.5.img.zst.zip` from Releases.
```bash
# Flash (auto-detects SD card)
sudo ./rpi/flash-image.sh stegasoo-rpi-4.1.5.img.zst.zip
# Or manual
zstdcat stegasoo-rpi-4.1.5.img.zst | sudo dd of=/dev/sdX bs=4M status=progress
```
Default login: `admin` / `stegasoo`
First boot runs the setup wizard for WiFi, HTTPS, and channel key configuration.
### Docker
```bash
@@ -16,19 +43,5 @@ docker-compose up -d web # Web UI on :5000
docker-compose up -d api # REST API on :8000
```
### Raspberry Pi Image
Download `stegasoo-rpi-4.1.3.img.zst`, flash to SD card, and boot. The first-boot wizard will guide you through WiFi, HTTPS, and channel key setup.
```bash
# Flash with included script
./rpi/flash-image.sh stegasoo-rpi-4.1.3.img.zst /dev/sdX
# First time: save your WiFi credentials
./rpi/inject-wifi.sh --setup
# Then inject WiFi after flashing
sudo ./rpi/inject-wifi.sh /dev/sdX
```
### Full Changelog
See [CHANGELOG.md](CHANGELOG.md) for complete details.
See [CHANGELOG.md](CHANGELOG.md) for complete version history.