> ## Documentation Index
> Fetch the complete documentation index at: https://mako-docs.devinagiffy.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# Environment Variables

> Complete guide to configuring environment variables for Hitler

## What is .env?

The `.env` file stores configuration values that change between environments (development, staging, production) or contain sensitive information like API keys and secrets.

<Info>
  **Why use .env files?** - Keep secrets out of code (never commit them to git) - Different values
  for different environments - Easy to change without modifying code - Follows the [12-factor
  app](https://12factor.net/config) methodology
</Info>

### How It Works

1. You create a `.env` file in the project root
2. The app loads these values at startup
3. Code accesses them via `process.env.VARIABLE_NAME`

```bash theme={null}
# .env file
DATABASE_URL=postgres://user:pass@localhost:5432/mydb

# In code
const dbUrl = process.env.DATABASE_URL;
```

### Files in This Project

| File           | Purpose                     | Committed to Git?   |
| -------------- | --------------------------- | ------------------- |
| `.env.example` | Template with all variables | Yes                 |
| `.env`         | Your actual configuration   | **No** (gitignored) |
| `.env.local`   | Local overrides (optional)  | **No**              |
| `.env.test`    | Test environment (optional) | **No**              |

***

## Quick Setup

```bash theme={null}
# Copy the template
cp .env.example .env

# Edit with your values
nano .env  # or use any text editor
```

***

## Required Variables

These must be set for the application to start.

### Database Connection

<Tabs>
  <Tab title="Using Docker (Recommended)">
    If you're using `make db-up` or `docker-compose`, use these defaults:

    ```bash theme={null}
    DATABASE_URL=postgres://hitler:hitler@localhost:5432/hitler
    REDIS_URL=redis://localhost:6379
    ```

    These match the credentials in `docker-compose.yml`.
  </Tab>

  <Tab title="External Database">
    For a managed database (Neon, Supabase, RDS, etc.):

    ```bash theme={null}
    DATABASE_URL=postgres://username:password@host:5432/database?sslmode=require
    REDIS_URL=redis://username:password@host:6379
    ```

    <Warning>
      Always use SSL (`sslmode=require`) for external databases.
    </Warning>
  </Tab>
</Tabs>

### JWT Secret

The JWT secret is used to sign authentication tokens. It must be:

* At least 32 characters long
* Random and unpredictable
* Different for each environment

<Steps>
  <Step title="Generate a secure secret">
    Run this command in your terminal:

    ```bash theme={null}
    node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
    ```

    This outputs a 64-character hex string like:

    ```
    a1b2c3d4e5f6...  (64 characters)
    ```
  </Step>

  <Step title="Add to .env">
    ```bash theme={null}
    JWT_SECRET=a1b2c3d4e5f6...your-generated-secret
    ```
  </Step>
</Steps>

<Warning>
  **Never use the example value in production!** Generate a unique secret for each environment.
</Warning>

***

## LLM Configuration

Hitler uses LLMs for natural language task parsing. You need at least one provider configured.

### Option 1: Anthropic (Claude) - Recommended

<Steps>
  <Step title="Create an Anthropic account">
    Go to [console.anthropic.com](https://console.anthropic.com) and sign up.
  </Step>

  <Step title="Get your API key">
    1. Navigate to **API Keys** in the dashboard 2. Click **Create Key** 3. Copy the key (starts
       with `sk-ant-`)
  </Step>

  <Step title="Add to .env">
    ```bash theme={null}
    ANTHROPIC_API_KEY=sk-ant-api03-your-key-here
    LLM_MODEL_ANTHROPIC=claude-3-haiku-20240307
    LLM_PROVIDER=anthropic
    ```
  </Step>
</Steps>

**Available Models:**

| Model                      | Speed   | Cost    | Best For                  |
| -------------------------- | ------- | ------- | ------------------------- |
| `claude-3-haiku-20240307`  | Fastest | Lowest  | Development, simple tasks |
| `claude-3-sonnet-20240229` | Medium  | Medium  | Production                |
| `claude-3-opus-20240229`   | Slowest | Highest | Complex reasoning         |

### Option 2: OpenAI (GPT)

<Steps>
  <Step title="Create an OpenAI account">
    Go to [platform.openai.com](https://platform.openai.com) and sign up.
  </Step>

  <Step title="Get your API key">
    1. Go to **API Keys** section 2. Click **Create new secret key** 3. Copy the key (starts with
       `sk-`)
  </Step>

  <Step title="Add to .env">
    `bash OPENAI_API_KEY=sk-your-key-here LLM_MODEL_OPENAI=gpt-4o-mini LLM_PROVIDER=openai `
  </Step>
</Steps>

**Available Models:**

| Model         | Speed  | Cost   | Best For                  |
| ------------- | ------ | ------ | ------------------------- |
| `gpt-4o-mini` | Fast   | Low    | Development, simple tasks |
| `gpt-4o`      | Medium | Medium | Production                |
| `gpt-4-turbo` | Slower | Higher | Complex tasks             |

### LLM Settings

```bash theme={null}
# Maximum tokens in LLM response (default: 1024)
LLM_MAX_TOKENS=1024

# Which provider to use: anthropic | openai
LLM_PROVIDER=anthropic
```

<Note>
  **No LLM key?** The app will use mock responses in development. Task parsing will return
  placeholder data, which is fine for UI development but not realistic testing.
</Note>

***

## Slack Integration

The Hitler Slack bot uses **Socket Mode**, which means it connects to Slack via WebSocket instead of HTTP webhooks. This is simpler for local development since you don't need ngrok or a public URL.

### Create a Slack App

<Steps>
  <Step title="Go to Slack API">
    Visit [api.slack.com/apps](https://api.slack.com/apps) and click **Create New App**.
  </Step>

  <Step title="Choose creation method">
    Select **From scratch**, enter a name (e.g., "Hitler Dev"), and select your workspace.
  </Step>

  <Step title="Get Signing Secret">
    In **Basic Information**, scroll to **App Credentials**: - Copy **Signing Secret** → This is
    your `SLACK_SIGNING_SECRET` - Copy **Client ID** and **Client Secret** (needed for OAuth login
    on web dashboard)
  </Step>
</Steps>

### Enable Socket Mode

<Steps>
  <Step title="Navigate to Socket Mode">
    In your Slack App settings, click **Socket Mode** in the left sidebar.
  </Step>

  <Step title="Enable Socket Mode">Toggle **Enable Socket Mode** to ON.</Step>

  <Step title="Create App-Level Token">
    1. Click **Generate Token and Scopes** 2. Give it a name (e.g., "socket-token") 3. Add the
       `connections:write` scope 4. Click **Generate** 5. Copy the token (starts with `xapp-`) → This
       is your `SLACK_APP_TOKEN`
  </Step>
</Steps>

<Warning>The App-Level Token is only shown once! Save it immediately to your `.env` file.</Warning>

### Configure OAuth Scopes

In your Slack App settings, go to **OAuth & Permissions**:

**Bot Token Scopes** (required):

```
app_mentions:read  # Receive @mentions
chat:write         # Send messages
commands           # Handle slash commands
im:history         # Read DM history
im:read            # View DM info
im:write           # Start DMs
users:read         # Get user info
team:read          # Get workspace info
```

**User Token Scopes** (for OAuth login on web dashboard):

```
identity.basic   # Basic user identity
identity.email   # User's email
```

### Install to Workspace and Get Bot Token

<Steps>
  <Step title="Install the App">
    1. Go to **Install App** in the left sidebar 2. Click **Install to Workspace** 3. Authorize the
       requested permissions
  </Step>

  <Step title="Copy Bot Token">
    After installation, you'll see **Bot User OAuth Token**. Copy it (starts with `xoxb-`) → This is
    your `SLACK_BOT_TOKEN`
  </Step>
</Steps>

### Configure Event Subscriptions

<Steps>
  <Step title="Enable Events">Go to **Event Subscriptions** and toggle **Enable Events** on.</Step>

  <Step title="Subscribe to Bot Events">
    Under **Subscribe to bot events**, add: - `app_home_opened` - App home tab views - `app_mention`

    * @mentions in channels - `message.im` - Direct messages
  </Step>

  <Step title="Save Changes">Click **Save Changes** at the bottom.</Step>
</Steps>

<Note>
  With Socket Mode, you don't need to set a Request URL - events are delivered over the WebSocket
  connection.
</Note>

### Create Slash Command (Optional)

<Steps>
  <Step title="Go to Slash Commands">Click **Slash Commands** in the left sidebar.</Step>

  <Step title="Create Command">
    1. Click **Create New Command** 2. Command: `/hitler` 3. Short Description: "Interact with
       Hitler" 4. Click **Save**
  </Step>
</Steps>

### Add All Credentials to .env

```bash theme={null}
# Slack Bot (Socket Mode) - Required for bot functionality
SLACK_BOT_TOKEN=xoxb-your-bot-token              # From OAuth & Permissions
SLACK_APP_TOKEN=xapp-your-app-level-token        # From Socket Mode settings
SLACK_SIGNING_SECRET=your-signing-secret         # From Basic Information

# Slack OAuth - Required for "Sign in with Slack" on web dashboard
SLACK_CLIENT_ID=1234567890.1234567890            # From Basic Information
SLACK_CLIENT_SECRET=abcdef1234567890abcdef       # From Basic Information
```

### Run the Bot

```bash theme={null}
# Start the API first (the bot needs it)
pnpm --filter @hitler/api dev

# In another terminal, start the bot
pnpm --filter @hitler/bot-slack dev
```

You should see:

```
⚡️ Hitler Slack bot running on port 3002
   API URL: http://localhost:3000
   Socket Mode: enabled
```

<Check>Now you can DM your bot in Slack or @mention it in channels!</Check>

***

## Email Configuration (Optional)

For sending email notifications (password resets, alerts, etc.).

### Option 1: Resend (Recommended)

<Steps>
  <Step title="Create a Resend account">Go to [resend.com](https://resend.com) and sign up.</Step>
  <Step title="Get your API key">In the dashboard, go to **API Keys** and create a new key.</Step>
  <Step title="Verify your domain">Add DNS records to verify your sending domain.</Step>

  <Step title="Add to .env">
    `bash RESEND_API_KEY=re_123456789 EMAIL_FROM=notifications@yourdomain.com `
  </Step>
</Steps>

### Option 2: SMTP

For using any SMTP server (Gmail, SendGrid, Mailgun, etc.):

```bash theme={null}
SMTP_HOST=smtp.gmail.com
SMTP_PORT=587
SMTP_USER=your-email@gmail.com
SMTP_PASS=your-app-password
EMAIL_FROM=your-email@gmail.com
```

<Warning>
  For Gmail, you need to create an [App
  Password](https://support.google.com/accounts/answer/185833), not your regular password.
</Warning>

***

## Secrets Storage (Production)

In production, platform OAuth tokens (Slack) are stored encrypted in Cloudflare KV.

### Set Up Cloudflare KV

<Steps>
  <Step title="Create a Cloudflare account">
    Go to [cloudflare.com](https://cloudflare.com) and sign up.
  </Step>

  <Step title="Create a KV namespace">
    1. Go to **Workers & Pages** → **KV**
    2. Click **Create a namespace**
    3. Name it (e.g., "hitler-secrets-prod")
    4. Copy the **Namespace ID**
  </Step>

  <Step title="Get your Account ID">
    Find it in the right sidebar of any Cloudflare page, or in **Account Home**.
  </Step>

  <Step title="Create an API token">
    1. Go to **My Profile** → **API Tokens**
    2. Click **Create Token**
    3. Use **Edit Cloudflare Workers** template
    4. Copy the token
  </Step>

  <Step title="Generate encryption key">
    ```bash theme={null}
    node scripts/generate-encryption-key.js
    ```

    This outputs a base64-encoded 32-byte key.
  </Step>

  <Step title="Add to .env">
    ```bash theme={null}
    CLOUDFLARE_ACCOUNT_ID=abc123def456
    CLOUDFLARE_KV_NAMESPACE_ID=xyz789
    CLOUDFLARE_API_TOKEN=your-api-token
    SECRETS_ENCRYPTION_KEY=base64-encoded-key
    ```
  </Step>
</Steps>

<Note>
  Cloudflare KV is only needed in production. In development, secrets are stored in memory (which is
  fine for local testing).
</Note>

***

## Context Memory (Optional)

The context memory system enables passive intelligence by listening to Slack channel messages, extracting facts, and building organizational memory.

### URL Fetching (Jina Reader)

```bash theme={null}
JINA_API_KEY=jina_your-api-key
```

Get a key from [jina.ai](https://jina.ai). Used to fetch and extract content from URLs shared in channels. Falls back to `@extractus/article-extractor` if Jina is unavailable.

### Web Search (Tavily)

```bash theme={null}
TAVILY_API_KEY=tvly-your-api-key
```

Get a key from [tavily.com](https://tavily.com). Used by the `search_web` LLM tool to search the web for current information.

### Tuning Parameters

```bash theme={null}
# Messages per extraction batch (default: 20)
CONTEXT_BATCH_SIZE=20

# Batch processing interval in ms (default: 300000 = 5 min)
CONTEXT_BATCH_INTERVAL_MS=300000

# Days to keep raw channel_messages before cleanup (default: 7)
CONTEXT_RAW_RETENTION_DAYS=7
```

<Note>
  The context memory system also uses `OPENAI_API_KEY` (for text-embedding-3-small embeddings) and `ANTHROPIC_API_KEY` (for Claude Haiku fact extraction). These are already configured as part of the LLM setup above.
</Note>

***

## Rate Limiting

Configure API rate limits to prevent abuse.

```bash theme={null}
# Time window in milliseconds (default: 60000 = 1 minute)
RATE_LIMIT_WINDOW_MS=60000

# Maximum requests per window (default: 100)
RATE_LIMIT_MAX_REQUESTS=100
```

**Example configurations:**

| Environment | Window | Max Requests | Result                               |
| ----------- | ------ | ------------ | ------------------------------------ |
| Development | 60000  | 1000         | 1000 req/min (relaxed)               |
| Production  | 60000  | 100          | 100 req/min (standard)               |
| Strict      | 60000  | 30           | 30 req/min (for sensitive endpoints) |

***

## Task Configuration

```bash theme={null}
# Hours before task drafts auto-expire (default: 24)
TASK_DRAFT_EXPIRY_HOURS=24
```

Task drafts that aren't confirmed within this time are automatically deleted.

***

## Logging

```bash theme={null}
# Log level: debug | info | warn | error
LOG_LEVEL=debug
```

| Level   | When to Use                           |
| ------- | ------------------------------------- |
| `debug` | Development - verbose output          |
| `info`  | Production - normal operations        |
| `warn`  | Production - only warnings and errors |
| `error` | Production - only errors              |

***

## Complete .env Template

Here's a complete template with all variables:

```bash theme={null}
# =============================================
# DATABASE
# =============================================
DATABASE_URL=postgres://hitler:hitler@localhost:5432/hitler
REDIS_URL=redis://localhost:6379

# =============================================
# SERVER PORTS
# =============================================
API_PORT=3001
WEB_PORT=3005
BOT_SLACK_PORT=3002

# =============================================
# API SERVER
# =============================================
NODE_ENV=development
JWT_SECRET=GENERATE_ME_WITH_CRYPTO_RANDOM_BYTES
JWT_EXPIRES_IN=7d
# IMPORTANT: API_KEY must be identical in root .env (read by bot-slack) and apps/api/.env (read by API).
# If they don't match, bots will get 401 errors when calling the API.
API_KEY=hitler_xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx_dev_secret_key

# =============================================
# WEB APP
# =============================================
NEXT_PUBLIC_API_URL=http://localhost:3005/api

# =============================================
# LLM (choose one or both)
# =============================================
ANTHROPIC_API_KEY=sk-ant-...
LLM_MODEL_ANTHROPIC=claude-3-haiku-20240307

OPENAI_API_KEY=sk-...
LLM_MODEL_OPENAI=gpt-4o-mini

LLM_MAX_TOKENS=1024
LLM_PROVIDER=anthropic

# =============================================
# SLACK (Socket Mode)
# =============================================
# Bot credentials (required for bot functionality)
SLACK_BOT_TOKEN=xoxb-your-bot-token
SLACK_APP_TOKEN=xapp-your-app-level-token
SLACK_SIGNING_SECRET=

# OAuth credentials (required for "Sign in with Slack")
SLACK_CLIENT_ID=
SLACK_CLIENT_SECRET=

# Notifications (optional)
SLACK_WEBHOOK_URL=

# =============================================
# EMAIL (optional)
# =============================================
RESEND_API_KEY=re_...
EMAIL_FROM=noreply@yourdomain.com

# =============================================
# CLOUDFLARE KV (production only)
# =============================================
CLOUDFLARE_ACCOUNT_ID=
CLOUDFLARE_KV_NAMESPACE_ID=
CLOUDFLARE_API_TOKEN=
SECRETS_ENCRYPTION_KEY=

# =============================================
# RATE LIMITING
# =============================================
RATE_LIMIT_WINDOW_MS=60000
RATE_LIMIT_MAX_REQUESTS=100

# =============================================
# TASKS
# =============================================
TASK_DRAFT_EXPIRY_HOURS=24

# =============================================
# CONTEXT MEMORY (optional)
# =============================================
JINA_API_KEY=jina_...                  # URL fetching via Jina Reader API
TAVILY_API_KEY=tvly-...                # Web search via Tavily
OPENAI_EMBEDDING_MODEL=text-embedding-3-small  # Embedding model for context memory
CONTEXT_BATCH_SIZE=20                  # Messages per extraction batch
CONTEXT_BATCH_INTERVAL_MS=300000       # Batch interval (5 min)
CONTEXT_RAW_RETENTION_DAYS=7           # Days to keep raw channel_messages

# =============================================
# LOGGING
# =============================================
LOG_LEVEL=debug
```

***

## Security Best Practices

<CardGroup cols={2}>
  <Card title="Never commit .env" icon="ban">
    The `.gitignore` already excludes `.env` files. Never override this.
  </Card>

  <Card title="Use different secrets per environment" icon="key">
    Generate unique JWT secrets, encryption keys for dev/staging/prod.
  </Card>

  <Card title="Rotate secrets regularly" icon="rotate">
    Change API keys and secrets periodically, especially after team changes.
  </Card>

  <Card title="Use a secrets manager in production" icon="vault">
    Consider tools like HashiCorp Vault, AWS Secrets Manager, or Doppler.
  </Card>
</CardGroup>

***

## Troubleshooting

<AccordionGroup>
  <Accordion title="App won't start - missing DATABASE_URL">
    Ensure you've copied `.env.example` to `.env` and the database is running:

    ```bash theme={null}
    cp .env.example .env
    make db-up
    ```
  </Accordion>

  <Accordion title="JWT_SECRET must be at least 32 characters">
    Generate a proper secret: `bash node -e
          "console.log(require('crypto').randomBytes(32).toString('hex'))" `
  </Accordion>

  <Accordion title="LLM returns mock responses">
    You need to set either `ANTHROPIC_API_KEY` or `OPENAI_API_KEY` for real LLM responses.
  </Accordion>

  <Accordion title="Slack bot won't start">
    **"You must provide an appToken"** error:

    * Set `SLACK_APP_TOKEN` in your `.env` (starts with `xapp-`)
    * Generate one in Slack App → Socket Mode → App-Level Tokens

    **"Invalid token"** error:

    * Verify `SLACK_BOT_TOKEN` is correct (starts with `xoxb-`)
    * Re-install the app to your workspace if needed
  </Accordion>

  <Accordion title="Slack bot not receiving messages">
    1. Ensure Socket Mode is enabled in your Slack App settings 2. Verify Event Subscriptions has
       `message.im` and `app_mention` subscribed 3. For channel messages, the bot must be invited to the
       channel 4. Check that `SLACK_SIGNING_SECRET` matches your app
  </Accordion>

  <Accordion title="Environment variable not loading">
    1. Restart the dev server after changing `.env`
    2. Check for typos in variable names
    3. Ensure no extra spaces around `=`
  </Accordion>
</AccordionGroup>
