stash-sense-context

star 8

Use when starting work on stash-sense to load project context, architecture overview, and development patterns. Reference for any implementation decisions.

carrotwaxr By carrotwaxr schedule Updated 2/21/2026

name: stash-sense-context description: Use when starting work on stash-sense to load project context, architecture overview, and development patterns. Reference for any implementation decisions.

Stash Sense Project Context

Quick-reference for architecture, conventions, and operational knowledge. See docs/architecture.md for the full system design.

Architecture

Two components:

  • Sidecar API (api/) — Python/FastAPI, face recognition, recommendations, upstream sync
  • Plugin (plugin/) — JS/CSS/Python injected into Stash web UI, proxies to sidecar

Two databases:

  • performers.db — Read-only, distributed via GitHub Releases. Face metadata, stash-box IDs, Voyager indices.
  • stash_sense.db — Read-write, user-local. Recommendations, watermarks, upstream snapshots, fingerprints.

Deployment:

  • Sidecar: Docker on unRAID (10.0.0.4:6960), image carrotwaxr/stash-sense:latest
  • Plugin: SCP to /mnt/nvme_cache/appdata/stash/config/plugins/stash-sense/
  • Dev sidecar: http://localhost:5000 with --reload

Key Systems

System Key Files Pattern
Face Recognition embeddings.py, recognizer.py, face_config.py 3-phase batch: extract -> detect -> embed+match
Recommendations recommendations_router.py, recommendations_db.py, analyzers/ BaseAnalyzer + incremental watermarking
Duplicate Detection analyzers/duplicate_scenes.py Candidate generation (SQL joins) -> sequential scoring
Upstream Sync upstream_field_mapper.py, stashbox_client.py, analyzers/base_upstream.py 3-way diff (upstream vs local vs snapshot), logic versioned
Multi-Signal multi_signal_matcher.py, body_proportions.py Face primary, body/tattoo multiplicative adjustment
Gallery ID recognizer.py (/identify/image, /identify/gallery) Independent images, aggregate by performer
DB Self-Update database_updater.py download -> verify -> swap -> reload, 503 gating
Plugin Proxy stash_sense_backend.py All sidecar calls go through plugin backend

Development Commands

# Start sidecar (dev)
cd api && source ../.venv/bin/activate && make sidecar

# Or without shell activation
/home/carrot/code/stash-sense/.venv/bin/python -m uvicorn main:app --host 0.0.0.0 --port 5000 --reload

# Run tests
cd api && ../.venv/bin/python -m pytest tests/ -v

# Deploy plugin
scp plugin/* root@10.0.0.4:/mnt/nvme_cache/appdata/stash/config/plugins/stash-sense/

# Build and push Docker image
docker build -t carrotwaxr/stash-sense:latest . && docker push carrotwaxr/stash-sense:latest

Conventions

  • Logging: Default level is WARNING. Use logger.warning() for user-visible progress. logger.info() is not visible.
  • Rate limiting: Shared 5 req/s for Stash and StashBox APIs. StashBox uses Priority.LOW.
  • Plugin defaults: Plugin sends NO face recognition defaults; relies on sidecar face_config.py.
  • Background tasks: Don't inherit shell activation. Use explicit venv python path for background processes.
  • Hot reload caveat: Background analysis tasks block uvicorn --reload on file changes; must kill and restart.
  • Plugin logging: Use Stash's log protocol with level prefix bytes (\x01 + level_char + \x02), not plain JSON to stderr. See stash_sense_backend.py:_log_prefix().
  • Upstream logic versioning: Each upstream analyzer has a logic_version class attribute. When bumped, the next analysis run auto-clears stale snapshots and watermarks, forcing full re-analysis. Bump when comparison logic changes (field sets, normalization, ID resolution).
  • Local-only fields: favorite, rating, o_count are local Stash metadata — never compare against upstream StashBox values.

Field Mapping (Stash vs StashBox)

Diff Engine Stash Mutation StashBox Notes
aliases alias_list aliases
height height_cm (Int) height (Int)
breast_type fake_tits breast_type
career_start_year career_length (String) career_start_year (Int) Combined "YYYY-YYYY"
career_end_year career_length (String) career_end_year (Int) Combined "YYYY-YYYY"
cup_size measurements (String) cup_size Combined "38F-24-35"
band_size measurements (String) band_size (Int) Combined "38F-24-35"
waist_size measurements (String) waist_size (Int) Combined "38F-24-35"
hip_size measurements (String) hip_size (Int) Combined "38F-24-35"

Translation: recommendations_router.py:update_performer_fields()

Face Recognition Tuned Defaults

Parameter Value Notes
Fusion weights 0.5/0.5 FaceNet/ArcFace Equal weight since ArcFace normalization fixed
max_distance 0.5 Plateau at 0.5, was 0.7
num_frames 60 +12.5% accuracy vs 40
min_unique_frames 2 Higher values cost precision
Cluster threshold 0.6 cosine distance On concatenated 1024-dim embeddings
Tagged performer boost +0.03 Applied when performer already on scene

Docker Image

  • Base: nvidia/cuda:12.4.0-runtime-ubuntu22.04
  • ONNX models baked in: facenet512.onnx (90MB) + arcface.onnx (130MB)
  • Stripped: PyTorch, TensorFlow, DeepFace (not needed at runtime)
  • Port: 6960 (host) -> 5000 (container)
  • GPU: --runtime=nvidia --gpus all
  • Volumes: /data (Voyager indices, performers.db, stash_sense.db), /root/.insightface (detector cache)

GitHub

  • Repo: carrotwaxr/stash-sense
  • Issues: Used for feature planning (full implementation plans in body)
  • Trainer repo: /home/carrot/code/stash-sense-trainer/ (private, builds performers.db)

Related Skills

  • deploy-dev-plugin — SCP plugin files to Unraid
  • db-import-export — Copy face DB from trainer to sidecar
  • create-ticket — Plan and create GitHub Issues
  • work-ticket — Pick up and implement a GitHub Issue
  • git-preferences — Branching, commits, PR conventions
  • python-fastapi — FastAPI patterns used in this project
  • stash / stash-box / stash-plugin-dev — Stash ecosystem references
Install via CLI
npx skills add https://github.com/carrotwaxr/stash-sense --skill stash-sense-context
Repository Details
star Stars 8
call_split Forks 1
navigation Branch main
article Path SKILL.md
More from Creator