# 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:

```bash
chmod 600 .env
```

`tf` 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:

```bash
tf --dir /path/to/checkout fetch
```

Without `--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.md` prints a matching ignore rule for each path that exists. `stat` shows mode `600` on `.env` and `.tokens.json`. After `tf login`, `.cloud.json` is mode `600` too.
