Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@
"docs/features/structured",
"docs/features/mini",
"docs/features/autoagents",
"docs/features/onboard"
"docs/features/onboard",
"docs/features/gateway-bind-aware-auth"
]
},
{
Expand Down
30 changes: 30 additions & 0 deletions docs/features/chat.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,38 @@ start_chat_server(config=config)
|----------|-------------|---------|
| `PRAISONAI_CHAT_PORT` | Server port | `8000` |
| `PRAISONAI_CHAT_HOST` | Server host | `0.0.0.0` |
| `CHAINLIT_HOST` | Host the UI binds to (drives auth mode) | `127.0.0.1` |
| `CHAINLIT_USERNAME` | Username for authentication | `admin` |
| `CHAINLIT_PASSWORD` | Password for authentication | `admin` |
| `PRAISONAI_ALLOW_DEFAULT_CREDS` | Allow admin/admin on external bind (unsafe) | `false` |
| `CHAINLIT_AUTH_SECRET` | Auth secret for sessions | Auto-generated |
Comment on lines +115 to 119
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm how PRAISONAI_CHAT_HOST and CHAINLIT_HOST are actually resolved in code.
rg -nP -C3 '\b(PRAISONAI_CHAT_HOST|CHAINLIT_HOST)\b'

Repository: MervinPraison/PraisonAIDocs

Length of output: 2517


🏁 Script executed:

#!/bin/bash
# Find the ChatConfig or configuration class to understand how config.host is set
fd -e py . praisonai/chat | head -20 | xargs grep -l "class.*Config\|config.host" 2>/dev/null

# Also search for where config is instantiated in chat/__init__.py
rg -A 10 "def run_chat" praisonai/chat/__init__.py | head -40

Repository: MervinPraison/PraisonAIDocs

Length of output: 97


🏁 Script executed:

#!/bin/bash
# Read the chat/__init__.py file to understand config initialization
wc -l praisonai/chat/__init__.py

Repository: MervinPraison/PraisonAIDocs

Length of output: 101


🏁 Script executed:

#!/bin/bash
# Read the entire chat/__init__.py file
cat -n praisonai/chat/__init__.py

Repository: MervinPraison/PraisonAIDocs

Length of output: 3825


🏁 Script executed:

#!/bin/bash
# Check if PRAISONAI_CHAT_HOST environment variable is actually read anywhere in the codebase
rg "PRAISONAI_CHAT_HOST" --type py

Repository: MervinPraison/PraisonAIDocs

Length of output: 53


🏁 Script executed:

#!/bin/bash
# Search for any CLI entry point or __main__ that might read PRAISONAI_CHAT_HOST
fd -e py . praisonai/chat | xargs grep -l "argparse\|click\|PRAISONAI_CHAT" 2>/dev/null

# Also search in other locations for CLI definitions
rg "PRAISONAI_CHAT" --type-list all | head -20

Repository: MervinPraison/PraisonAIDocs

Length of output: 526


🏁 Script executed:

#!/bin/bash
# Check the size and read app.py
wc -l praisonai/chat/app.py

Repository: MervinPraison/PraisonAIDocs

Length of output: 96


🏁 Script executed:

#!/bin/bash
# Read app.py to check for environment variable usage
cat -n praisonai/chat/app.py

Repository: MervinPraison/PraisonAIDocs

Length of output: 5325


The documentation incorrectly lists environment variables that don't match implementation behavior.

The table describes PRAISONAI_CHAT_HOST and CHAINLIT_HOST as independent environment variables, but the code reveals:

  • PRAISONAI_CHAT_HOST (default 0.0.0.0 in the table) is never read from the environment
  • CHAINLIT_HOST (default 127.0.0.1 in the table) is always programmatically set to the value of config.host at runtime, overriding any environment variable

The code in praisonai/chat/__init__.py line 88 shows CHAINLIT_HOST is set by the application, not independently configurable via environment. Users cannot rely on these environment variables; configuration is only available through the start_chat_server(host=...) function parameter.

Fix: Either document that configuration is only available programmatically via the function API, or if environment variable support is intended, implement environment variable reading in the code.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/features/chat.mdx` around lines 115 - 119, The docs list
PRAISONAI_CHAT_HOST and CHAINLIT_HOST as independently configurable via
environment, but the code sets CHAINLIT_HOST programmatically (overriding env)
and never reads PRAISONAI_CHAT_HOST; update either docs or code: either change
docs to state that chat host must be configured via the API
(start_chat_server(host=...)) and remove the env var rows, or implement env-var
support by reading PRAISONAI_CHAT_HOST / CHAINLIT_HOST at startup and falling
back to start_chat_server host parameter (or vice versa) so environment values
are respected; check the logic around CHAINLIT_HOST assignment in
praisonai.chat.__init__ (the code that sets CHAINLIT_HOST) and ensure consistent
precedence and documentation.


---

## Security

The chat UI enforces bind-aware authentication — stricter security when bound to external interfaces.

| Interface | Security Mode | Credentials |
|-----------|---------------|------------|
| **Loopback** (`127.0.0.1`, `localhost`) | Permissive | `admin/admin` allowed |
| **External** (`0.0.0.0`, LAN, public) | Strict | Custom credentials required |

<Warning>
Using default `admin/admin` credentials on external interfaces will cause a `UIStartupError` unless `PRAISONAI_ALLOW_DEFAULT_CREDS=1` is set (demo only).
</Warning>

```bash
# Safe for external deployment
export CHAINLIT_USERNAME=myuser
export CHAINLIT_PASSWORD=mypass
praisonai chat --host 0.0.0.0
```

<Card title="Bind-Aware Authentication" icon="shield" href="/docs/features/gateway-bind-aware-auth">
Complete security configuration guide
</Card>

## UI Features

### Chain of Thought Visualization
Expand Down
219 changes: 219 additions & 0 deletions docs/features/gateway-bind-aware-auth.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
---
title: "Bind-Aware Authentication"
sidebarTitle: "Bind-Aware Auth"
description: "Gateway and UI automatically enforce stricter auth when bound to external interfaces"
icon: "shield"
---

The gateway and chat UI change security behavior based on the interface they bind to — permissive on loopback, strict on external.

```mermaid
graph LR
subgraph "Bind-Aware Auth"
Bind[🔌 Bind Host] --> Check{🔍 Loopback?}
Check -->|Yes 127.0.0.1| Local[🟢 local mode<br/>no token required]
Check -->|No 0.0.0.0| Token[🔒 token mode<br/>auth required]
end

classDef bind fill:#6366F1,stroke:#7C90A0,color:#fff
classDef decision fill:#F59E0B,stroke:#7C90A0,color:#fff
classDef permissive fill:#10B981,stroke:#7C90A0,color:#fff
classDef strict fill:#8B0000,stroke:#7C90A0,color:#fff

class Bind bind
class Check decision
class Local permissive
class Token strict
```

## Quick Start

<Steps>
<Step title="Local development (loopback — permissive)">
```python
from praisonaiagents import Agent

agent = Agent(
name="Local Agent",
instructions="You are a helpful assistant.",
)

# Serve via gateway on loopback — no token needed
# $ praisonai gateway start --host 127.0.0.1
agent.start("hello")
```
</Step>
Comment on lines +32 to +45
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify whether GatewayConfig supports bind_host as the PR objective suggests,
# and how agents are actually served through the gateway.
rg -nP -C3 '\bbind_host\b'
rg -nP -C3 'class\s+GatewayConfig\b'

Repository: MervinPraison/PraisonAIDocs

Length of output: 436


🏁 Script executed:

cat -n praisonaiagents/gateway/config.py | head -250 | tail -80

Repository: MervinPraison/PraisonAIDocs

Length of output: 3656


🏁 Script executed:

# Search for how Agent uses GatewayConfig
rg -n 'Agent.*GatewayConfig|GatewayConfig.*Agent' -A5 -B5

Repository: MervinPraison/PraisonAIDocs

Length of output: 2379


🏁 Script executed:

# Look for gateway start or serve methods in Agent
rg -n 'def start\b' praisonaiagents/agent.py -A10

Repository: MervinPraison/PraisonAIDocs

Length of output: 135


🏁 Script executed:

# Find where Agent class is defined
fd 'agent\.py$|agents\.py$' praisonaiagents --type f

Repository: MervinPraison/PraisonAIDocs

Length of output: 812


🏁 Script executed:

# Look at the specific file being reviewed
cat -n docs/features/gateway-bind-aware-auth.mdx

Repository: MervinPraison/PraisonAIDocs

Length of output: 8842


Replace the Python snippet with Agent passing GatewayConfig(host="127.0.0.1") to actually exercise the gateway bind behavior.

The Step title says "Local development (loopback — permissive)" but the Python code creates a bare Agent without any gateway configuration and calls agent.start("hello"), which runs the agent locally rather than through the gateway's bind-aware auth. The bash comment alone doesn't demonstrate the feature. Show the gateway being configured on the agent itself using gateway=GatewayConfig(host="127.0.0.1") so the snippet matches the documentation's purpose.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/features/gateway-bind-aware-auth.mdx` around lines 32 - 45, Replace the
example's bare Agent instantiation with one that configures the gateway so the
snippet demonstrates bind-aware auth: construct a
GatewayConfig(host="127.0.0.1") and pass it to Agent via the gateway parameter
(e.g., Agent(..., gateway=GatewayConfig(...))) before calling
agent.start("hello"), ensuring the code shows the agent using the gateway bind
rather than running purely locally.


<Step title="External deployment (strict — token required)">
```bash
# Option A: Run onboarding (recommended)
praisonai onboard

# Option B: Set a token explicitly
export GATEWAY_AUTH_TOKEN=$(openssl rand -hex 16)
praisonai gateway start --host 0.0.0.0
```
</Step>
</Steps>

---

## How It Works

```mermaid
sequenceDiagram
participant User
participant Gateway
participant BindAwareAuth
participant WS as WebSocket Clients

User->>Gateway: praisonai gateway start --host <H>
Gateway->>BindAwareAuth: assert_external_bind_safe(config)
alt H is loopback (127.0.0.1, localhost, ::1)
BindAwareAuth-->>Gateway: OK (permissive, warn if no token)
else H is external (0.0.0.0, LAN, public)
alt auth_token set
BindAwareAuth-->>Gateway: OK (strict)
else no token
BindAwareAuth-->>Gateway: raise GatewayStartupError
Gateway-->>User: exit 1 + fix instructions
end
end
Gateway->>WS: accept connections
```

| Mode | Meaning | Trigger |
|---|---|---|
| `local` | Permissive — no token required | Loopback bind (default) |
| `token` | Token required (auto-generated if absent on loopback) | External bind (default) |
Comment on lines +87 to +88
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The description for token mode is slightly misleading. Auto-generation of the token occurs during loopback binds (to facilitate future external access), but token mode itself (triggered by external binds) strictly requires an existing token and will fail if one is not found. It is clearer to associate the auto-generation behavior with local mode.

| `local` | Permissive — no token required (auto-generated for future use) | Loopback bind (default) |
| `token` | Strict — token required | External bind (default) |

| `password` | Username/password auth | Chainlit UI |
| `trusted-proxy` | Auth handled upstream | Reverse proxy setups |

---

## Interface Detection

| Host | `is_loopback()` | Resolved mode |
|---|---|---|
| `127.0.0.1` | `True` | `local` |
| `127.255.255.255` | `True` | `local` |
| `localhost` | `True` | `local` |
| `::1` | `True` | `local` |
| `0.0.0.0` | `False` | `token` |
| `192.168.1.x` | `False` | `token` |
| `10.0.0.x` | `False` | `token` |
| `8.8.8.8` (public) | `False` | `token` |

---

## User Flows

**Flow A — "I want a quick local demo":** Run on `127.0.0.1`, no config needed. Token auto-generated, fingerprint logged (`gw_****abcd`), saved to `~/.praisonai/.env`.

**Flow B — "I want to share on my LAN":** Run `praisonai onboard` (30s, 3 prompts) OR `export GATEWAY_AUTH_TOKEN=$(openssl rand -hex 16)` → `praisonai gateway start --host 0.0.0.0`.

**Flow C — "I'm deploying to a VPS":** Same as B, but also set `CHAINLIT_USERNAME` / `CHAINLIT_PASSWORD` for the UI, and consider TLS.

**Flow D — "Lab/demo — I accept the risk of admin/admin on external":** `export PRAISONAI_ALLOW_DEFAULT_CREDS=1`.

---

## Environment Variables

| Variable | Scope | Effect |
|---|---|---|
| `GATEWAY_AUTH_TOKEN` | Gateway | Auth token. Required on external bind. Auto-generated + saved to `~/.praisonai/.env` (mode `0600`) on loopback when unset. |
| `CHAINLIT_HOST` | UI | Host the UI binds to (default `127.0.0.1`). Drives UI auth mode resolution. |
| `CHAINLIT_USERNAME` | UI | Username (default `admin`). |
| `CHAINLIT_PASSWORD` | UI | Password (default `admin`). |
| `PRAISONAI_ALLOW_DEFAULT_CREDS` | UI | Escape hatch. Set to `1`/`true`/`yes` to allow `admin/admin` on external bind. **Unsafe — demo only.** |
| `CHAINLIT_AUTH_SECRET` | UI | Session secret. Auto-generated if unset (ephemeral per-process). |

---

## Error Reference

**`GatewayStartupError`** — raised by `assert_external_bind_safe()` when binding externally without a token:
```
Cannot bind to 0.0.0.0 without an auth token.
Fix: praisonai onboard (30 seconds, 3 prompts)
Or: export GATEWAY_AUTH_TOKEN=$(openssl rand -hex 16)
```

**`UIStartupError`** — raised by `register_password_auth()` when `admin/admin` used on external bind:
```
Cannot bind to 0.0.0.0 with default admin/admin credentials.
Fix: export CHAINLIT_USERNAME=myuser CHAINLIT_PASSWORD=mypass
Lab: export PRAISONAI_ALLOW_DEFAULT_CREDS=1 (demo only)
```

---

## Token Fingerprinting

Logs now show `gw_****XXXX` (last 4 chars), never the raw token. This is implemented by `get_auth_token_fingerprint()` for safe logging. Retrieve the full token from `~/.praisonai/.env` if needed.

---

## Which Option When

```mermaid
graph TB
Q{Where will it run?}
Q -->|My laptop only| Local[127.0.0.1 — just run it]
Q -->|LAN for my team| LAN{Have a token?}
Q -->|Public internet| Public[0.0.0.0 + token + TLS + custom creds]
LAN -->|No| Onboard[praisonai onboard]
LAN -->|Yes| Export[export GATEWAY_AUTH_TOKEN=...]

classDef question fill:#F59E0B,stroke:#7C90A0,color:#fff
classDef safe fill:#10B981,stroke:#7C90A0,color:#fff
classDef action fill:#189AB4,stroke:#7C90A0,color:#fff
classDef strict fill:#8B0000,stroke:#7C90A0,color:#fff

class Q,LAN question
class Local safe
class Onboard,Export action
class Public strict
```

---

## Best Practices

<AccordionGroup>
<Accordion title="Use praisonai onboard for token setup">
Prefer `praisonai onboard` over manual token creation. It handles all the setup automatically and saves the token securely.
</Accordion>

<Accordion title="Never commit ~/.praisonai/.env">
The auto-generated `.env` file contains sensitive tokens. Add it to your `.gitignore` and never commit it to version control.
</Accordion>

<Accordion title="Set custom credentials before external binding">
Always set `CHAINLIT_USERNAME` and `CHAINLIT_PASSWORD` before binding to external interfaces. Never use `admin/admin` in production.
</Accordion>

<Accordion title="Use PRAISONAI_ALLOW_DEFAULT_CREDS only for demos">
The `PRAISONAI_ALLOW_DEFAULT_CREDS=1` escape hatch should only be used for ephemeral demos or testing. Never in production.
</Accordion>
</AccordionGroup>

---

## Related

<CardGroup cols={2}>
<Card title="Gateway Documentation" icon="gateway" href="/docs/gateway">
Core gateway functionality and configuration
</Card>
<Card title="Onboarding" icon="rocket" href="/docs/features/onboard">
Quick setup with automatic token generation
</Card>
<Card title="Troubleshooting" icon="wrench" href="/docs/guides/troubleshoot-gateway">
Common gateway issues and solutions
</Card>
<Card title="Chat Interface" icon="comments" href="/docs/features/chat">
Chainlit UI security and configuration
</Card>
</CardGroup>
10 changes: 9 additions & 1 deletion docs/features/onboard.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,13 @@ When onboarding completes successfully, the wizard installs:
|-----------|----------|---------|
| **Platform daemon** | System service (launchd/systemd/Windows Task) | Keeps bot running in background |
| **Bot configuration** | `~/.praisonai/config/` | Stores tokens and settings |
| **Gateway auth token** | `~/.praisonai/.env` (env var `GATEWAY_AUTH_TOKEN`) | Authentication for web dashboard |
| **Gateway auth token** | `~/.praisonai/.env` (mode `0600`) | Authentication for web dashboard |
| **Dashboard URL** | Printed in Done panel | Local web interface |

<Note>
Auth token is now auto-persisted to `~/.praisonai/.env` with secure permissions (mode `0600`) and shown as fingerprint `gw_****XXXX` in logs for security.
</Note>

---

## The ✅ Done Panel
Expand Down Expand Up @@ -356,6 +360,10 @@ If bots aren't responding or services seem down, run `praisonai doctor` for diag
</Accordion>
</AccordionGroup>

<Card title="Bind-Aware Authentication" icon="shield" href="/docs/features/gateway-bind-aware-auth">
Gateway and UI security behavior based on bind interface
</Card>

---

## Related
Expand Down
30 changes: 30 additions & 0 deletions docs/gateway.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,36 @@ sequenceDiagram

---

## Authentication

Authentication posture changes automatically based on the bind interface.

```mermaid
graph LR
Check{🔍 Interface}
Check -->|127.0.0.1| Local[🟢 Permissive<br/>No token required]
Check -->|0.0.0.0| External[🔒 Strict<br/>Token required]

classDef decision fill:#F59E0B,stroke:#7C90A0,color:#fff
classDef permissive fill:#10B981,stroke:#7C90A0,color:#fff
classDef strict fill:#8B0000,stroke:#7C90A0,color:#fff

class Check decision
class Local permissive
class External strict
```

| Interface | Mode | Auth Required |
|-----------|------|---------------|
| **Loopback** (`127.0.0.1`, `localhost`, `::1`) | Permissive | No |
| **External** (`0.0.0.0`, LAN IPs, public IPs) | Strict | Yes |

<Card title="Bind-Aware Authentication" icon="shield" href="/docs/features/gateway-bind-aware-auth">
Complete authentication security guide
</Card>

---

## Configuration Options

<Card title="Gateway Configuration" icon="code" href="/docs/sdk/reference/python/classes/GatewayConfig">
Expand Down
Loading