11 KiB
Instructions for Claude Code
Project: uncloseai-speech - Raccoon Mission TTS System License: AGPL v3 (must provide source code to network service users)
Brand Identity
CRITICAL: Always use consistent naming across all files.
Project Name
- ✅ Correct:
uncloseai-speech(lowercase, hyphenated) - ❌ Wrong: "UncloseAI Speech", "Uncloseai Speech", "UncloseAI-Speech"
Organization Name
- ✅ Correct:
uncloseai(lowercase, one word) - ❌ Wrong: "UncloseAI", "Unclose AI", "UnClose AI"
Usage Guidelines
- In code: Use
uncloseai-speechfor project references - In documentation: Use
uncloseai-speechfor project name - In comments: Use
uncloseai-speechconsistently - Repository URLs:
uncloseai-speech(lowercase, hyphenated) - Docker images:
uncloseai-speech(lowercase, hyphenated) - API responses: Use
"owned_by": "uncloseai"(lowercase, one word)
Examples
# Correct
description='uncloseai-speech API Server'
owned_by = "uncloseai"
# Wrong
description='UncloseAI Speech API Server'
owned_by = "UncloseAI"
# Correct
# uncloseai-speech
**Raccoon Mission:** Rescue abandoned TTS models and integrate them into uncloseai-speech
# Wrong
# UncloseAI Speech
**Raccoon Mission:** Rescue abandoned TTS models and integrate them into UncloseAI Speech
Core Principles
1. Makefile-First Development
ALWAYS prefer Makefile targets over manual commands.
- ✅ DO:
make deploy,make voices,make test - ❌ DON'T: Manual ssh commands, docker commands, curl commands
When adding new functionality:
- Add it to the Makefile first
- Document it in
make help - Test it works from scratch
- Only then modify other files if needed
Makefile is the source of truth for all deployment and development tasks.
2. Work Locally, Deploy Remotely
- Local development:
/home/fox/git/uncloseai-speech/ - Remote server:
ai.foxhop.net(configured invars.sh, gitignored) - Never create remote directories manually - let Makefile handle it
- Always test from scratch -
make cleanthenmake deploy
3. Configuration Management
vars.sh- Deployment secrets (gitignored, never commit)vars.sh.example- Template for users (commit this)sample.env- Default environment (commit this)speech.env- Runtime environment (created automatically by Makefile)
Key environment variables:
WORKERS- Number of uvicorn workers (default: 1 for GPU models like Qwen3-TTS)- Use 1 for GPU-bound models to avoid VRAM duplication across workers
- Increase for CPU-bound models like Piper (e.g., WORKERS=4)
Never view or log secrets - source them and use them.
4. Git Commit Guidelines
- Never add AI attribution - Do not use
Co-Authored-By: Claudeor similar in commit messages - Write clear, concise commit messages describing what changed and why
- Use imperative mood ("Add feature" not "Added feature")
5. Documentation Requirements
When adding features, update ALL relevant docs:
Makefilehelp textdocs/MODELS.mdfor new TTS enginesdocs/MIRRORS.mdfor binary downloadsdocs/AUDIT.mdfor file changes- This file (
docs/CLAUDE.md) for new patterns
Common Tasks
Full Deployment from Scratch
# 1. Clean everything
make clean
# 2. Deploy (commit, push, pull on server, rebuild container)
git push && tmux send-keys -t 0:1 'cd ~/uncloseai-speech && git pull && sudo docker-compose up --build -d' Enter
# 3. Download voices (Piper + XTTS samples)
make voices
# 4. Test
make test
make test-xtts
Adding a New TTS Engine
- Document it in
docs/MODELS.mdfirst - Add download target to Makefile (e.g.,
voices-silero) - Implement engine wrapper in
speech.pyorsrc/engines/ - Add test target (e.g.,
test-silero) - Update
make voicesto include it - Test full cycle: commit, push, pull on server, rebuild, test
Debugging Issues
make logs # Tail live logs
make logs | grep ERROR # Filter errors
Never use raw docker/ssh commands - extend Makefile if needed.
File Organization
Scripts vs Docs
scripts/- Executable utilities (add_voice.py, download_samples.sh, etc.)docs/- Documentation ONLY (no executable code)- Dockerfiles, startup.sh - Root level (build artifacts)
- Makefile - Root level (primary interface)
Never put executable scripts in docs/ directory.
Current Structure (as of 2025-11-09)
uncloseai-speech/
├── Makefile # PRIMARY INTERFACE - always update first
├── vars.sh # Secrets (gitignored)
├── vars.sh.example # Template
├── speech.py # Main server (will refactor to src/)
├── openedai.py # API models
├── voice_to_speaker.default.yaml # Voice config
├── docs/
│ ├── CLAUDE.md # This file
│ ├── AUDIT.md # Repository audit
│ ├── MODELS.md # TTS engines
│ └── MIRRORS.md # Binary mirror strategy
├── scripts/
│ ├── add_voice.py
│ ├── say.py
│ ├── test_voices.sh
│ └── download_samples.sh
├── Dockerfile
├── docker-compose.yml
└── startup.sh
TTS Engine Status
Default Model (Qwen3-TTS)
- ✅ Qwen3-TTS (tts-1-qwen) - DEFAULT - 1.7B params, 10 languages, voice cloning, 97ms latency
Other Engines (disabled by default, enable in voice_to_speaker.yaml)
- Piper TTS (tts-1) - 55 voices, fast CPU inference
- XTTS v2 (tts-1-hd) - Voice cloning, multilingual
- Silero TTS (tts-1-silero) - 142 voices, 5 languages, auto-downloads
- Kokoro TTS (tts-1-kokoro) - 32 voices, lightweight (82M params)
Why Qwen3-TTS is Default
- State-of-the-art quality with voice cloning
- Actively maintained by Alibaba
- Apache 2.0 license (commercial-friendly)
- 10 languages: Chinese, English, Japanese, Korean, German, French, Russian, Portuguese, Spanish, Italian
- Fast first-packet latency (97ms)
- Easy voice cloning from 3-second samples
See docs/MODELS.md for complete roadmap and detailed model documentation.
Production Deployment
Production Server: 3090-ai.foxhop.net
- URL: https://speech.ai.unturf.com (proxied via ai.foxhop.net)
- Repo Location:
/home/fox/uncloseai-speech - Container:
uncloseai-speech_server_1(image:uncloseai-speech:local) - tmux access: Use
tmux-hoststo discover window mappings, thentmux send-keys -t 0:1 'command' Enter - Current mapping: window
0:1is3090-ai.foxhop.net(verify withtmux-hosts)
Deployment Workflow
We use git, not rsync. Commit locally, push, pull on server, rebuild.
Local:
/home/fox/git/uncloseai-speech/
↓ git commit && git push
Remote (3090-ai.foxhop.net):
/home/fox/uncloseai-speech/
↓ git pull && sudo docker-compose up --build -d
Container:
/app/
├── speech.py
├── voices/samples/ (cloned-voices mounted)
└── config/
└── voice_to_speaker.yaml
Quick Production Commands
# Use tmux-hosts to see available tmux windows
tmux-hosts
# Push changes and deploy
git add -A && git commit -m "message" && git push
tmux send-keys -t 0:1 'cd ~/uncloseai-speech && git pull && sudo docker-compose up --build -d' Enter
# Check container status
tmux send-keys -t 0:1 'sudo docker ps | grep speech' Enter
# View logs
tmux send-keys -t 0:1 'sudo docker logs --tail 50 uncloseai-speech_server_1' Enter
# Restart container
tmux send-keys -t 0:1 'cd ~/uncloseai-speech && sudo docker-compose restart' Enter
Testing Philosophy
Full stack testing workflow:
- Commit and push changes
- Pull on server and rebuild:
git pull && sudo docker-compose up --build -d - Wait for model to load (check logs)
- Test via curl or browser
Never assume - always test from scratch after changes.
Raccoon Mission Values
- Resilience - Assume upstream dies, plan mirrors
- Simplicity - Makefile > manual commands
- Documentation - Write docs before code
- Liberation - Keep TTS libre (AGPL v3)
- Unification - All TTS engines, one API
AGPL v3 Compliance
This project is AGPL v3 licensed. The key obligation: anyone who uses this TTS service over a network must be able to access the source code.
What This Means
Unlike regular GPL, AGPL closes the "SaaS loophole". If you run uncloseai-speech as a service (even without distributing binaries), users have the right to request source code.
Requirements for Operators
When running uncloseai-speech as a network service, you must provide:
- Complete source code of the running version
- Any modifications you've made
- Build instructions
How to Comply
- Link in API response - Add source URL to
/v1/modelsor root endpoint - Link in documentation - Include repository URL in service docs
- Host source code - Keep your fork in a public git repository
Example Implementation
# In API responses
"source_code": "https://github.com/uncloseai/uncloseai-speech"
Why AGPL?
- Ensures forks remain open source
- Community improvements flow back to the project
- Prevents proprietary TTS services from using our work without sharing back
- Aligns with Raccoon Mission: Keep TTS libre
Common Mistakes to Avoid
❌ DON'T create directories with raw ssh ✅ DO add Makefile target for deployment
❌ DON'T use rsync - we use git ✅ DO commit, push, pull on server, then rebuild with docker-compose
❌ DON'T put scripts in docs/ ✅ DO put scripts in scripts/, reference from docs
❌ DON'T hardcode paths/hosts ✅ DO use vars.sh variables
❌ DON'T forget to commit and push before deploying ✅ DO commit, push, pull on server, rebuild container
When Things Break
- Check logs:
tmux send-keys -t 0:1 'sudo docker logs --tail 50 uncloseai-speech_server_1' Enter - Check GPU memory:
tmux send-keys -t 0:1 'nvidia-smi' Enter - Restart container:
tmux send-keys -t 0:1 'sudo docker-compose restart' Enter - Rebuild:
git pull && sudo docker-compose up --build -d - Check voice files:
tmux send-keys -t 0:1 'ls voices/samples/' Enter
Multiprocess Architecture (uvicorn workers=4)
Key pattern: Worker processes spawn as fresh imports, don't run __main__ block.
Solutions implemented:
- Caches - Initialize in
lifespancontext manager (runs per worker) - Args - Use
DefaultArgsclass at module level, override in__main__
See speech.py:23-113 for implementation.
Future Refactoring (Planned)
- Move
speech.py,openedai.py,audio_reader.py→src/ - Create engine abstraction layer in
src/engines/ - Unified voice config with engine selection
- Binary mirror implementation (MinIO on ai.foxhop.net)
See docs/AUDIT.md for detailed refactoring plan.
Remember: Makefile first, documentation second, code third. Test from scratch every time.
🦝 Raccoon Mission: Keep TTS libre, rescue abandoned models, unify all engines.