titen (Javanese): to watch closely, to observe with care.
Self-hosted Threads management platform. Post, schedule, and analyze Threads content from your own infrastructure.
Threads has no native post scheduling. Existing tools are SaaS products where your API tokens live on someone else's server. Titen runs on your box, talks directly to the Threads Graph API, and stores everything in a single SQLite file.
No subscription. No vendor lock-in. Your tokens stay on your machine, encrypted at rest with AES-256-GCM.
| Capability | Details |
|---|---|
| Multi-account | Manage multiple Threads accounts in one instance |
| Post scheduling | Cron-based scheduler for automated posting that Threads itself does not offer |
| Comment fetching | Pull comments from the Threads API, store locally |
| Sentiment analysis | Pluggable engine trait (stub default; ONNX/LLM/custom API extensible) |
| Analytics | Time-series snapshots per post |
| Media storage | S3-compatible via swappable storage trait |
| MCP server | JSON-RPC 2.0 over stdio, compatible with Claude Desktop, Cursor, etc. |
| CLI | Full CRUD from the terminal |
| Docker | Single container, minimal footprint |
Detailed docs: Deployment Guide · Usage Guide · Architecture Overview · Auth Flow · Changelog
4 crates, one binary each:
| Crate | Purpose |
|---|---|
titen-core |
Domain logic: models, SQLite store, Threads API client, sentiment trait, scheduler, S3 storage, AES-256-GCM encryption |
titen-api |
Axum HTTP server: REST API, API key auth, CORS, rate limiting |
titen-cli |
Clap CLI: all operations via the HTTP API |
titen-mcp |
MCP stdio server: 14 tools for AI agent integration |
8 SQLite tables: accounts, posts, schedules, comments, analytics_snap, media_assets, rate_tracking, _encryption_meta.
4 migrations: 001_initial (schema), 002_drop_refresh_token, 003_add_app_secret, 004_encrypt_tokens (encrypts existing plaintext tokens on startup).
curl -sSL https://github.com/codecoradev/titen/releases/latest/download/install.sh | shRequires Rust 1.85+ (edition 2024). SQLite is bundled via sqlx.
git clone https://github.com/codecoradev/titen.git
cd titen
cargo build --release# Start the API server (default: 0.0.0.0:7845)
titen-api
# Or via the CLI with embedded server
titen serve
# Add an account and post
export TITEN_API_KEY=your-key
titen account add myuser --access-token "THREADS_TOKEN" --expires-at "2026-12-01T00:00:00Z"
titen post create myuser --text "Hello from titen!"The server creates a SQLite database at ~/.codecora/titen/titen.db by default (override with TITEN_DB_PATH).
All config via environment variables:
| Variable | Default | Description |
|---|---|---|
TITEN_DB_PATH |
~/.codecora/titen/titen.db |
SQLite database path |
TITEN_API_KEY |
(none) | API key for endpoint access. When unset, all endpoints are open (dev mode) |
TITEN_ENCRYPTION_KEY |
(none) | AES-256-GCM key for token encryption at rest. Generate with openssl rand -hex 32 |
TITEN_REQUIRE_ENCRYPTION |
false |
Set to true in production to fail-fast if encryption key is missing |
TITEN_HOST |
0.0.0.0 |
Bind address |
TITEN_PORT |
7845 |
Bind port |
TITEN_URL |
http://localhost:7845 |
Base URL for CLI |
TITEN_SENTIMENT_ENGINE |
stub |
stub, onnx, llm, or custom_api |
TITEN_SCHEDULER_INTERVAL_SECS |
60 |
Scheduler tick interval |
TITEN_S3_ENDPOINT |
(none) | S3-compatible endpoint |
TITEN_S3_BUCKET |
(none) | S3 bucket name |
TITEN_S3_REGION |
us-east-1 |
S3 region |
TITEN_S3_ACCESS_KEY |
(none) | S3 access key |
TITEN_S3_SECRET_KEY |
(none) | S3 secret key |
TITEN_S3_PUBLIC_URL |
(none) | Public URL for uploaded media |
Base URL: http://localhost:7845
All endpoints except /health require X-API-Key authentication when TITEN_API_KEY is set. Use a plain header (not Bearer):
X-API-Key: your-key-here
| Method | Path | Auth |
|---|---|---|
| GET | /health |
None |
| Method | Path | Description |
|---|---|---|
| GET | /api/accounts |
List all accounts |
| POST | /api/accounts |
Create an account |
| PUT | /api/accounts/{id} |
Update an account |
| DELETE | /api/accounts/{id} |
Delete an account |
| POST | /api/accounts/{id}/refresh-token |
Refresh OAuth token |
| GET | /api/accounts/{id}/profile |
Fetch Threads profile |
| GET | /api/accounts/{id}/publishing-limit |
Get remaining daily limits |
| GET | /api/accounts/check-tokens |
Batch token expiry check |
| Method | Path | Description |
|---|---|---|
| GET | /api/posts |
List posts (?account_id=&status=&limit=&offset=) |
| POST | /api/posts |
Create and publish a post |
| GET | /api/posts/{id} |
Get a single post |
| DELETE | /api/posts/{id} |
Delete a post |
| GET | /api/posts/{id}/insights |
Fetch and store post insights |
| Method | Path | Description |
|---|---|---|
| GET | /api/schedules |
List schedules (?account_id=&status=) |
| POST | /api/schedules |
Create a schedule |
| PUT | /api/schedules/{id} |
Update a schedule |
| DELETE | /api/schedules/{id} |
Delete a schedule |
| GET | /api/schedules/upcoming |
Next 10 pending schedules |
| Method | Path | Description |
|---|---|---|
| GET | /api/posts/{id}/comments |
List stored comments |
| POST | /api/posts/{id}/comments/fetch |
Fetch comments from Threads API |
| GET | /api/posts/{id}/comments/sentiment |
Analyze comment sentiment |
| Method | Path | Description |
|---|---|---|
| GET | /api/analytics/posts |
Post analytics (?account_id=&from=&to=) |
| GET | /api/analytics/posts/{id}/trend |
Time-series trend for a post |
| Method | Path | Description |
|---|---|---|
| GET | /api/media |
List uploaded media |
| POST | /api/media |
Upload media (multipart) |
| DELETE | /api/media/{id} |
Delete media |
| Method | Path | Description |
|---|---|---|
| POST | /api/threads/container |
Create a Threads media container |
| POST | /api/threads/container/{id}/publish |
Publish a container |
| POST | /api/threads/container/{id}/status |
Check container status |
The CLI talks to the running HTTP server. Set TITEN_URL and TITEN_API_KEY as needed.
titen serve [--host 0.0.0.0] [--port 7845] [--mcp]titen account list
titen account add <username> --access-token <TOKEN> [--user-id <ID>] [--expires-at <ISO8601>]
titen account remove <id>
titen account refresh <id>
titen account status <id>
titen token-checktiten post create <account> --text <TEXT> [--media-type TEXT|IMAGE] [--image-url <URL>]
titen post delete <post_id>
titen post insights <post_id>titen schedule add <account> --text <TEXT> --at <ISO8601> [--media-type TEXT|IMAGE]
titen schedule list [--account <id>] [--status <status>]
titen schedule cancel <id>
titen schedule upcomingtiten comment fetch <post_id>
titen comment list <post_id>
titen comment sentiment <post_id>titen analytics posts <account> [--from <date>] [--to <date>]
titen analytics trend <post_id>
titen analytics sentiment-summary <post_id>titen media list
titen media upload <file_path> [--content-type <mime>]
titen media delete <id>Titen ships an MCP (Model Context Protocol) server for AI agent integration. It communicates over stdio using JSON-RPC 2.0.
Claude Desktop (claude_desktop_config.json):
{
"mcpServers": {
"titen": {
"command": "/path/to/titen-mcp",
"env": {
"TITEN_DB_PATH": "/path/to/titen.db"
}
}
}
}Cursor: add to your MCP settings:
{
"mcp": {
"titen": {
"command": "/path/to/titen-mcp",
"env": {
"TITEN_DB_PATH": "/path/to/titen.db"
}
}
}
}| Tool | Description |
|---|---|
list_accounts |
List all Threads accounts |
get_account |
Get a single account by ID |
create_post |
Create and publish a post |
schedule_post |
Schedule a post for later |
list_schedules |
List scheduled posts |
cancel_schedule |
Cancel a scheduled post |
fetch_comments |
Fetch and store comments from Threads |
get_post_sentiment |
Sentiment analysis on a post's comments |
get_post_analytics |
Analytics for a specific post |
get_account_analytics |
Analytics summary for an account |
upload_media |
Upload media to S3 storage |
refresh_token |
Refresh an account's access token |
check_tokens |
Batch check all accounts' token expiry |
The platform enforces these per-account daily limits:
| Limit | Count |
|---|---|
| Posts | 250/day |
| Replies | 1,000/day |
| Deletes | 100/day |
| Caption length | 500 chars |
text_attachment length |
10,000 chars |
- Engine: tokio-cron with a 60-second tick interval
- Flow: at scheduled time, create media container, wait for ready, publish
- Publish delays: text posts publish immediately (0s), image posts wait 30s, video posts wait 60s for processing
- Token encryption:
access_tokenandapp_secretcolumns encrypted at rest with AES-256-GCM. Each value gets a random 96-bit nonce and aenc:v1:versioned prefix for future migration. Key is zeroized on drop. - Fail-fast mode: set
TITEN_REQUIRE_ENCRYPTION=truein production to reject startup if the encryption key is missing. - API key auth: constant-time comparison against
TITEN_API_KEY. Three credential sources:X-API-Keyheader,api_keyquery param,titen_sessioncookie. - HTTP client timeouts: all outbound calls to the Threads API and S3 have connect and total timeout limits.
See SECURITY.md for the full policy and vulnerability reporting.
docker build -t titen .
docker run -p 7845:7845 \
-e TITEN_API_KEY=your-key \
-e TITEN_DB_PATH=/data/titen.db \
-e TITEN_S3_ENDPOINT=https://s3.example.com \
-e TITEN_S3_BUCKET=titen-media \
-v titen-data:/data \
titen- Fork the repository
- Create a feature branch (
git checkout -b feature/my-feature) - Commit your changes
- Push to the branch (
git push origin feature/my-feature) - Open a Pull Request