A Spring Boot-based Text-to-Speech (TTS) API server that converts text to speech audio using Piper TTS engine. Features API key management, token-based usage limits, iframe embedding, and a comprehensive admin dashboard.
- 🎙️ Text-to-Speech Conversion: Convert text to high-quality WAV audio files
- 🔐 API Key Authentication: Secure API access with API key management
- 📊 Token Management: Flexible token limit system (unlimited, total, monthly, yearly, one-time)
- 🎨 Admin Dashboard: Web-based interface for managing API keys and monitoring usage
- 🔗 Iframe Embedding: Generate embeddable iframe players for websites
- 📈 Usage Tracking: Detailed usage history and statistics per API key
- 💾 Database Support: MySQL (production) and H2 (development)
- 📝 API Documentation: Swagger/OpenAPI documentation included
- Java 17 or higher
- Maven 3.6+ (for building)
- MySQL 8+ (for production) or H2 (for development)
# Update package list
sudo apt update
# Install OpenJDK 17
sudo apt install openjdk-17-jdk
# Verify installation
java -version
javac -version# Install OpenJDK 17
sudo dnf install java-17-openjdk-devel
# Or for older versions
sudo yum install java-17-openjdk-devel
# Verify installation
java -version
javac -version# Using Homebrew
brew install openjdk@17
# Link it
sudo ln -sfn /opt/homebrew/opt/openjdk@17/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk-17.jdk
# Verify installation
java -version- Download OpenJDK 17 from Adoptium or Oracle
- Run the installer
- Set
JAVA_HOMEenvironment variable:- Open System Properties → Environment Variables
- Add
JAVA_HOMEpointing to JDK installation (e.g.,C:\Program Files\Java\jdk-17) - Add
%JAVA_HOME%\bintoPATH
- Verify installation:
java -version javac -version
sudo apt install mavensudo dnf install maven
# Or for older versions
sudo yum install mavenbrew install maven- Download Maven from Apache Maven
- Extract to a directory (e.g.,
C:\Program Files\Apache\maven) - Add
MAVEN_HOMEenvironment variable pointing to Maven directory - Add
%MAVEN_HOME%\bintoPATH
Creating a dedicated user improves security and makes process management easier.
# Create a new user (without home directory)
sudo useradd -r -s /bin/false flossktts
# Or with home directory (if needed)
sudo useradd -m -s /bin/bash flossktts
# Create application directory
sudo mkdir -p /opt/flossk-tts
sudo chown flossktts:flossktts /opt/flossk-tts
# Create cache directory
sudo mkdir -p /var/cache/flossk-tts
sudo chown flossktts:flossktts /var/cache/flossk-tts
# Create log directory
sudo mkdir -p /var/log/flossk-tts
sudo chown flossktts:flossktts /var/log/flossk-tts- Open Computer Management → Local Users and Groups → Users
- Right-click → New User
- Create user
flossktts(uncheck "User must change password at next logon") - Create directories:
mkdir C:\flossk-tts mkdir C:\flossk-tts\cache mkdir C:\flossk-tts\logs
- Set permissions for the user on these directories
mvn clean packagemvn clean package -DskipTestsThe JAR file will be created at: target/flossk-tts.jar
- Copy the example properties file:
cp application.properties.production-example application.properties- Edit
application.propertiesand configure:- MySQL database connection
- Cache directory
- Server port (default: 8080)
- Logging settings
Example configuration:
spring.datasource.url=jdbc:mysql://localhost:3306/tts
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.jpa.database-platform=org.hibernate.dialect.MySQLDialect
tts.cache.directory=./cache
server.port=8080export MYSQL_URL="jdbc:mysql://localhost:3306/tts"
export MYSQL_USER="your_username"
export MYSQL_PASSWORD="your_password"
export MYSQL_DIALECT="org.hibernate.dialect.MySQLDialect"For local development without MySQL, use the dev profile:
java -jar flossk-tts.jar --spring.profiles.active=devThis uses an H2 file-based database at ./data/ttsdb.
java -jar flossk-tts.jar --spring.config.location=file:./application.propertiesjava -jar flossk-tts.jarjava -jar flossk-tts.jar --spring.profiles.active=devThe application will start on http://localhost:8080 (or your configured port).
# Navigate to application directory
cd /opt/flossk-tts
# Run in background with nohup
nohup java -jar flossk-tts.jar --spring.config.location=file:./application.properties > /var/log/flossk-tts/app.log 2>&1 &
# Save the process ID
echo $! > /var/run/flossk-tts.pid
# Check if running
ps aux | grep flossk-tts
# View logs
tail -f /var/log/flossk-tts/app.logStopping the application:
# Find and kill the process
kill $(cat /var/run/flossk-tts.pid)
# Or find by name
pkill -f flossk-tts.jarCreate a systemd service file:
sudo nano /etc/systemd/system/flossk-tts.serviceAdd the following content:
[Unit]
Description=Flossk TTS API Server
After=network.target mysql.service
[Service]
Type=simple
User=flossktts
Group=flossktts
WorkingDirectory=/opt/flossk-tts
ExecStart=/usr/bin/java -jar /opt/flossk-tts/flossk-tts.jar --spring.config.location=file:/opt/flossk-tts/application.properties
Restart=always
RestartSec=10
StandardOutput=journal
StandardError=journal
SyslogIdentifier=flossk-tts
# Security settings
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/var/cache/flossk-tts /var/log/flossk-tts
# Resource limits
LimitNOFILE=65536
LimitNPROC=4096
[Install]
WantedBy=multi-user.targetEnable and start the service:
# Reload systemd
sudo systemctl daemon-reload
# Enable service to start on boot
sudo systemctl enable flossk-tts
# Start the service
sudo systemctl start flossk-tts
# Check status
sudo systemctl status flossk-tts
# View logs
sudo journalctl -u flossk-tts -f
# Stop the service
sudo systemctl stop flossk-tts
# Restart the service
sudo systemctl restart flossk-tts# Start a new screen session
screen -S flossk-tts
# Run the application
java -jar flossk-tts.jar --spring.config.location=file:./application.properties
# Detach from screen: Press Ctrl+A, then D
# Reattach to screen session
screen -r flossk-tts
# List all screen sessions
screen -ls
# Kill a screen session
screen -X -S flossk-tts quit# Start a new tmux session
tmux new -s flossk-tts
# Run the application
java -jar flossk-tts.jar --spring.config.location=file:./application.properties
# Detach from tmux: Press Ctrl+B, then D
# Reattach to tmux session
tmux attach -t flossk-tts
# List all tmux sessions
tmux ls
# Kill a tmux session
tmux kill-session -t flossk-ttsFor Windows, use NSSM (Non-Sucking Service Manager):
# Download and extract NSSM
# Run as Administrator
# Install service
nssm install FlosskTTS "C:\Program Files\Java\jdk-17\bin\java.exe"
nssm set FlosskTTS AppParameters "-jar C:\flossk-tts\flossk-tts.jar --spring.config.location=file:C:\flossk-tts\application.properties"
nssm set FlosskTTS AppDirectory "C:\flossk-tts"
nssm set FlosskTTS DisplayName "Flossk TTS API Server"
nssm set FlosskTTS Description "Flossk Text-to-Speech API Server"
nssm set FlosskTTS Start SERVICE_AUTO_START
nssm set FlosskTTS AppStdout "C:\flossk-tts\logs\output.log"
nssm set FlosskTTS AppStderr "C:\flossk-tts\logs\error.log"
# Start service
nssm start FlosskTTS
# Stop service
nssm stop FlosskTTS
# Remove service
nssm remove FlosskTTS confirmIf you created a dedicated user (recommended), run the application as that user:
# Switch to the dedicated user
sudo su - flossktts
# Or run a command as that user
sudo -u flossktts java -jar /opt/flossk-tts/flossk-tts.jar --spring.config.location=file:/opt/flossk-tts/application.propertiesNote: Ensure the dedicated user has:
- Read access to the JAR file and application.properties
- Write access to cache and log directories
- Network access (for database connections and API requests)
For production, configure JVM memory settings based on your server resources:
# Example: Allocate 2GB heap memory
java -Xms2g -Xmx2g -jar flossk-tts.jar --spring.config.location=file:./application.properties
# Example: With garbage collection options
java -Xms2g -Xmx2g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
-jar flossk-tts.jar --spring.config.location=file:./application.propertiesRecommended JVM Options for Production:
java \
-Xms2g \
-Xmx2g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-XX:+HeapDumpOnOutOfMemoryError \
-XX:HeapDumpPath=/var/log/flossk-tts/heap-dump.hprof \
-XX:+PrintGCDetails \
-XX:+PrintGCDateStamps \
-Xloggc:/var/log/flossk-tts/gc.log \
-jar flossk-tts.jar \
--spring.config.location=file:./application.propertiesMemory Guidelines:
- Minimum: 1GB heap for small deployments (< 100 requests/hour)
- Recommended: 2-4GB heap for medium deployments (100-1000 requests/hour)
- Large: 4-8GB heap for high-traffic deployments (> 1000 requests/hour)
Update your systemd service file to include JVM options:
[Service]
ExecStart=/usr/bin/java \
-Xms2g \
-Xmx2g \
-XX:+UseG1GC \
-XX:MaxGCPauseMillis=200 \
-jar /opt/flossk-tts/flossk-tts.jar \
--spring.config.location=file:/opt/flossk-tts/application.propertiesCheck Application Status:
# Using systemd
sudo systemctl status flossk-tts
# Using curl (health endpoint)
curl http://localhost:8080/actuator/health
# Check if port is listening
netstat -tlnp | grep 8080
# Or
ss -tlnp | grep 8080
# Check process
ps aux | grep flossk-ttsView Logs:
# Systemd logs
sudo journalctl -u flossk-tts -f
# Application logs (if configured)
tail -f /var/log/flossk-tts/app.log
# Error logs only
sudo journalctl -u flossk-tts -p errMonitor Resource Usage:
# CPU and memory usage
top -p $(pgrep -f flossk-tts.jar)
# Or use htop for better visualization
htop -p $(pgrep -f flossk-tts.jar)
# Disk usage (cache directory)
du -sh /var/cache/flossk-tts
# Database connections
mysql -u root -p -e "SHOW PROCESSLIST;"-
Access the Admin Dashboard: Navigate to
http://localhost:8080/admin -
Login:
- Default username:
floosk - Default password:
flosskaadmin ⚠️ Important: Change the default password after first login!
- Default username:
-
Create API Keys:
- Click "Create New API Key"
- Enter owner name
- Select token limit type:
- Unlimited: No token restrictions
- Total: One-time total token limit
- Monthly: Tokens reset at the start of each month
- Yearly: Tokens reset at the start of each year
- Once: Single-use only (disabled after first use)
- Set token limit value (if applicable)
- Optionally set referer domain for iframe embeds
-
Manage API Keys:
- View all API keys and their status
- Edit token limits and referer domains
- View usage history and statistics
- Generate iframe embed codes
Endpoint: POST /api/tts/speak
Headers:
X-API-KEY: your-api-key-here
Content-Type: application/json
Request Body:
{
"text": "Hello, this is a test message.",
"voiceId": "edon"
}Available Voices: edon, arta, arben, dren
Response: WAV audio file (binary)
Example using cURL:
curl -X POST http://localhost:8080/api/tts/speak \
-H "X-API-KEY: your-api-key-here" \
-H "Content-Type: application/json" \
-d '{"text":"Hello world","voiceId":"edon"}' \
--output speech.wavExample using JavaScript (Fetch API):
const response = await fetch('http://localhost:8080/api/tts/speak', {
method: 'POST',
headers: {
'X-API-KEY': 'your-api-key-here',
'Content-Type': 'application/json'
},
body: JSON.stringify({
text: 'Hello, this is a test message.',
voiceId: 'edon'
})
});
const audioBlob = await response.blob();
const audioUrl = URL.createObjectURL(audioBlob);
const audio = new Audio(audioUrl);
audio.play();Example with multiline text:
{
"text": "This is line one.\nThis is line two.\nThis is line three.",
"voiceId": "edon"
}Note: Newlines in the text are automatically replaced with " ." (space and period) before processing.
-
Generate Embed Code:
- Go to Admin Dashboard → Select API Key → Click "Iframe"
- Copy the iframe code provided
-
Use in HTML:
<!-- With pre-filled text -->
<iframe
src="http://your-server.com/embed/player?token=YOUR_TOKEN"
text_to_speech="Pershendetje, ky është zëri shqip."
width="500"
height="400"
frameborder="0">
</iframe>
<!-- Without text (user can enter text) -->
<iframe
src="http://your-server.com/embed/player?token=YOUR_TOKEN"
width="500"
height="400"
frameborder="0">
</iframe>Security Features:
- API key is encoded in the token (not exposed in URL)
- Referer domain validation ensures iframe can only be embedded on authorized domains
- Token includes timestamp and signature for security
Interactive API documentation is available at:
- Swagger UI:
http://localhost:8080/swagger-ui.html - OpenAPI JSON:
http://localhost:8080/v3/api-docs
Tokens are counted as words longer than 3 characters in the input text. Punctuation is removed before counting.
Example:
- "Hello world" → 2 tokens ("Hello" and "world" both count)
- "This is a test" → 2 tokens ("This" and "test" both count)
- Unlimited: No restrictions on token usage
- Total: One-time limit that decreases with each request
- Monthly: Limit resets at the start of each month
- Yearly: Limit resets at the start of each year
- Once: Single-use only - API key is disabled after first successful request
Each API key tracks:
- Total tokens used
- Total requests made
- Tokens used today
- Tokens used this month
- Remaining tokens (based on limit type)
- Detailed request history with timestamps, IP addresses, and user agents
flossk-tts/
├── src/
│ ├── main/
│ │ ├── java/com/flossk/tts/
│ │ │ ├── controller/ # REST controllers
│ │ │ ├── service/ # Business logic
│ │ │ ├── entity/ # Database entities
│ │ │ ├── repository/ # Data access
│ │ │ ├── config/ # Configuration
│ │ │ └── filter/ # Security filters
│ │ └── resources/
│ │ ├── templates/ # Thymeleaf templates
│ │ └── voices/ # Voice model files (.onnx)
│ └── test/
├── application.properties.production-example
├── pom.xml
└── README.md
Generated audio files are cached to improve performance. Cache directory is configurable via tts.cache.directory property (default: ./cache).
Cache keys are based on:
- Text content (normalized)
- Voice ID
- Text hash (SHA-256)
- API Authentication: API keys required for
/api/**endpoints - Admin Authentication: Form-based login for admin dashboard
- CSRF Protection: Enabled for admin endpoints
- Iframe Security: Referer domain validation for embed tokens
- Password Encryption: BCrypt password hashing
-
Database Connection Issues:
- Check MySQL is running and accessible
- Verify database credentials in
application.properties - For development, use
--spring.profiles.active=devto use H2
-
Port Already in Use:
- Change
server.portinapplication.properties - Or stop the process using the port
- Change
-
Voice Models Not Found:
- Ensure voice model files (
.onnxand.onnx.json) are insrc/main/resources/voices/ - Required voices:
edon,arta,arben,dren
- Ensure voice model files (
- Check API key has remaining tokens
- Verify token limit type and reset period (for monthly/yearly)
- Check API key is not disabled (for "once" type)
- Verify referer domain is set correctly in API key settings
- Check iframe URL includes valid token
- Ensure
Content-Security-Policyheaders allow embedding
- ✅ Install Java SDK 17+
- ✅ Create dedicated user (
flossktts) - ✅ Set up MySQL database
- ✅ Build the JAR file:
mvn clean package -DskipTests - ✅ Copy JAR to
/opt/flossk-tts/ - ✅ Create
application.propertieswith production settings - ✅ Set up systemd service (or preferred process manager)
- ✅ Configure reverse proxy (Nginx/Apache)
- ✅ Set up SSL/TLS certificates
- ✅ Configure firewall rules
- ✅ Set up log rotation
- ✅ Configure monitoring and alerts
See PRODUCTION_SETUP.md for comprehensive production deployment instructions including:
- Complete systemd service configuration
- Docker containerization
- Nginx reverse proxy setup
- SSL/TLS configuration with Let's Encrypt
- Database migration and backup strategies
- Log rotation configuration
- Monitoring and health checks
License: GNU Affero General Public License v3.0 (AGPL-3.0)
For issues and questions, please create an issue or contact support.