Configuration
Copy .env.example to .env. The parser is lenient: blank lines and # comments are ignored, and the value is everything after the first =. tf loads .env from the project root (the nearest directory containing tweets.db, .env, or package.json, or the path passed to --dir).
After you write secrets:
chmod 600 .envtf and setup_x.sh set mode 0600 when they write .env. A hand-edited file does not. Run chmod 600 .env after you edit it yourself. Never commit .env. Never paste values into chat.
The table below is generated from the keys in .env.example. Adding a key there without documenting it fails the docs build.
| Variable | Needed | What it does |
|---|---|---|
TWITTER_OAUTH2_CLIENT_ID | Required | OAuth 2.0 client id from the X app’s Keys and tokens tab. |
TWITTER_OAUTH2_CLIENT_SECRET | Required | OAuth 2.0 client secret. Confidential web app so the token response includes a refresh token. |
TF_LLM_PROVIDER | Optional | claude-code (default), anthropic, openrouter, or openai. |
TF_LLM_MODEL | Optional | Overrides the provider default. For Claude Code this is passed as claude --model. |
ANTHROPIC_API_KEY | With provider anthropic | Direct Anthropic API. Unused when the backend is Claude Code. |
OPENROUTER_API_KEY | With provider openrouter | OpenRouter key. Requests go to https://openrouter.ai/api/v1. |
OPENAI_API_KEY | With provider openai | OpenAI key for tf generate when the provider is openai. |
TAVILY_API_KEY | Optional | Timely search, article extract, tf generate --timely, and tf cron (cron calls enrich). |
NOTION_API_KEY | Optional | Internal integration secret. Pages must be shared with the integration. |
SLACK_WEBHOOK_URL | Optional | Incoming webhook for tf notify, --post-to-slack, and the cron pending ping. |
TF_LICENSE_KEY | Optional | License key for paid features. The default build allows every feature and does not send the key. |
TF_API_ORIGIN | Optional | Entitlement API origin once a billing provider is linked. Default https://tweetfactory.sh. |
TF_CLOUD_ORIGIN | Optional | Cloud sync API origin. Default https://tweetfactory.sh. Nothing is uploaded until tf login and tf sync push. |
OAUTH_REDIRECT_URI | Optional | Defaults to http://127.0.0.1:8675/callback. Must be registered on the X app. |
OAUTH_PORT | Optional | Callback listen port. Default 8675. |
OAUTH_BIND_HOST | Optional | Callback bind address. Default 127.0.0.1. Widen only for cross-machine auth. |
Project root
Global flag on every command:
tf --dir /path/to/checkout fetchWithout --dir, tf walks up to six directories from the working directory looking for tweets.db, .env, or package.json.
What stays out of git
.env, .tokens.json, .cloud.json, tweets.db, persona/PERSONA.md, flush/* (except .gitkeep), drafts/* (except .gitkeep), onboarding.json, logs/, backups/. .cloud.json appears only after tf login.
Verify.
git check-ignore -v .env .tokens.json .cloud.json tweets.db persona/PERSONA.mdprints a matching ignore rule for each path that exists.statshows mode600on.envand.tokens.json. Aftertf login,.cloud.jsonis mode600too.