Files
stegasoo/PLAN-4.1.0.md
Aaron D. Lee 01f0173dd4 Add EXIF Editor, consolidate channel key resolution
EXIF Editor (Library → CLI → API → WebUI):
- src/stegasoo/utils.py: read_image_exif(), write_image_exif()
- CLI: stegasoo tools exif [--clear|--set Field=Value]
- API: /api/tools/exif, /api/tools/exif/update, /api/tools/exif/clear
- WebUI: EXIF Editor tab with inline editing, clear all, save/download

Architectural consolidation:
- Moved resolve_channel_key() to src/stegasoo/channel.py (was duplicated in 3 frontends)
- Added get_channel_response_info() for consistent API/WebUI responses
- Frontends now use thin wrappers that translate exceptions

DCT improvements:
- Added will_fit_by_mode() pre-check to WebUI encode (fail fast)
- Suggests LSB mode when DCT capacity exceeded

Dependencies:
- Added piexif>=1.1.0 for EXIF editing

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

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-04 01:16:33 -05:00

16 KiB

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:

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

# 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:

# 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

# 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

# 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:

# 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

  • Plan reviewed
  • Questions resolved
  • Ready to implement

Progress

  • Multi-User Support (commit 7b33501)
  • Channel Key QR (Web UI) - added QR generator on About page
  • CLI Channel Commands
  • Saved Channel Keys (Web UI) - users can save/manage channel keys
  • Advanced Tools - Image Security Toolkit
    • CLI: stegasoo tools capacity/strip/peek/exif
    • API: /api/tools/capacity, /api/tools/peek, /api/tools/exif/*
    • WebUI: Tools page with tabbed interface
    • 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:

# 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