docs: Add JWT secret rotation and security considerations

- Add JWT to available rotation components
- Document security limitations and manual steps
- Add component table with service restart requirements
- Include verification and best practices documentation

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Rodrigo Rodriguez 2026-02-19 19:42:15 +00:00
parent 6d48dbba1b
commit cb84ad2b56
64 changed files with 688 additions and 139 deletions

View file

@ -55,7 +55,7 @@ On first run, botserver automatically:
- Installs required components (PostgreSQL, S3 storage, Cache, LLM) - Installs required components (PostgreSQL, S3 storage, Cache, LLM)
- Sets up database with migrations - Sets up database with migrations
- Downloads AI models - Downloads AI models
- Starts HTTP server at `http://127.0.0.1:8080` - Starts HTTP server at `http://127.0.0.1:9000`
### Run the Desktop App ### Run the Desktop App

View file

@ -35,7 +35,7 @@ By the end of this chapter, you will:
./botserver ./botserver
``` ```
Open `http://localhost:8080`. Start chatting. That's it. Open `http://localhost:9000`. Start chatting. That's it.
Everything installs automatically on first run—PostgreSQL, storage, cache, and your first bot. Everything installs automatically on first run—PostgreSQL, storage, cache, and your first bot.
@ -55,7 +55,7 @@ Everything installs automatically on first run—PostgreSQL, storage, cache, and
<p>Just three steps:</p> <p>Just three steps:</p>
<p>1⃣ Download <code>botserver</code></p> <p>1⃣ Download <code>botserver</code></p>
<p>2⃣ Run <code>./botserver</code></p> <p>2⃣ Run <code>./botserver</code></p>
<p>3⃣ Open your browser to localhost:8080</p> <p>3⃣ Open your browser to localhost:9000</p>
<p>The bootstrap process handles everything else automatically!</p> <p>The bootstrap process handles everything else automatically!</p>
<div class="wa-time">09:00</div> <div class="wa-time">09:00</div>
</div> </div>

View file

@ -4,7 +4,7 @@ After botserver starts, you can immediately start chatting with your bot. No pro
## Just Start Talking ## Just Start Talking
Open your browser to `http://localhost:8080` and start chatting: Open your browser to `http://localhost:9000` and start chatting:
``` ```
You: Hi! You: Hi!

View file

@ -120,7 +120,7 @@ psql $DATABASE_URL -c "SELECT version();"
curl http://localhost:8081/v1/models curl http://localhost:8081/v1/models
# Open UI # Open UI
open http://localhost:8080 open http://localhost:9000
``` ```
## Bot Deployment ## Bot Deployment

View file

@ -10,7 +10,7 @@ botserver certificates include `127.0.0.1` as a Subject Alternative Name (SAN),
| Component | Description | IP:Port | | Component | Description | IP:Port |
|-----------|-------------|---------| |-----------|-------------|---------|
| api | Main botserver API | `127.0.0.1:8443` (HTTPS) / `127.0.0.1:8080` (HTTP) | | api | Main botserver API | `127.0.0.1:8443` (HTTPS) / `127.0.0.1:9000` (HTTP) |
| tables | PostgreSQL database | `127.0.0.1:5432` | | tables | PostgreSQL database | `127.0.0.1:5432` |
| drive | Object storage (S3-compatible) | `127.0.0.1:9000` | | drive | Object storage (S3-compatible) | `127.0.0.1:9000` |
| cache | Redis cache | `127.0.0.1:6379` | | cache | Redis cache | `127.0.0.1:6379` |

View file

@ -105,7 +105,7 @@ Installation begins by downloading and running the botserver binary. The bootstr
Bot deployment uses object storage buckets. Each bot receives its own bucket for file storage. Bots are deployed to the drive rather than the work folder, which is reserved for internal operations as documented in the gbapp chapter. Bot deployment uses object storage buckets. Each bot receives its own bucket for file storage. Bots are deployed to the drive rather than the work folder, which is reserved for internal operations as documented in the gbapp chapter.
After startup, access the UI interface at `http://localhost:8080` to interact with your bots and monitor their operation. After startup, access the UI interface at `http://localhost:9000` to interact with your bots and monitor their operation.
## Use Cases ## Use Cases

View file

@ -28,13 +28,13 @@ Installing Cache...
Creating bots from templates... Creating bots from templates...
default.gbai deployed default.gbai deployed
announcements.gbai deployed announcements.gbai deployed
botserver ready at http://localhost:8080 botserver ready at http://localhost:9000
``` ```
### 3. Open Browser ### 3. Open Browser
``` ```
http://localhost:8080 http://localhost:9000
``` ```
Start chatting with your bot! Start chatting with your bot!

View file

@ -14,7 +14,7 @@ A session is a persistent conversation container that tracks everything about an
### UI Interface ### UI Interface
When a user opens `http://localhost:8080`, the browser receives a session token in the form of a UUID. This token is stored in localStorage for persistence across page loads. The session itself is created in PostgreSQL for durability and cached for fast access during active conversations. When a user opens `http://localhost:9000`, the browser receives a session token in the form of a UUID. This token is stored in localStorage for persistence across page loads. The session itself is created in PostgreSQL for durability and cached for fast access during active conversations.
### API Access ### API Access
@ -22,12 +22,12 @@ Programmatic access to sessions uses the REST API. A POST request to `/api/sessi
```bash ```bash
# Get new session # Get new session
curl -X POST http://localhost:8080/api/session curl -X POST http://localhost:9000/api/session
# Returns: {"session_id": "uuid-here", "token": "secret-token"} # Returns: {"session_id": "uuid-here", "token": "secret-token"}
# Use session # Use session
curl -H "Authorization: Bearer secret-token" \ curl -H "Authorization: Bearer secret-token" \
http://localhost:8080/api/chat http://localhost:9000/api/chat
``` ```
### Anonymous vs Authenticated ### Anonymous vs Authenticated

View file

@ -158,7 +158,7 @@ greeting,Welcome to support!
```bash ```bash
cp -r my-bot.gbai/ templates/ cp -r my-bot.gbai/ templates/
./botserver restart ./botserver restart
# Visit http://localhost:8080/my-bot # Visit http://localhost:9000/my-bot
``` ```
### Production Server ### Production Server

View file

@ -674,7 +674,7 @@ idleTimeout = 60 ' Return to welcome screen after inactivity
lastActivity = NOW lastActivity = NOW
' Initialize display ' Initialize display
' (Runs in browser kiosk mode at http://localhost:8088/embedded/) ' (Runs in browser kiosk mode at http://localhost:9000/embedded/)
TALK welcomeMessage TALK welcomeMessage

View file

@ -122,7 +122,7 @@ Select based on your needs:
```bash ```bash
# Templates are auto-deployed during bootstrap # Templates are auto-deployed during bootstrap
# Access at: http://localhost:8080/template-name # Access at: http://localhost:9000/template-name
``` ```
### 3. Customize Configuration ### 3. Customize Configuration

View file

@ -19,10 +19,10 @@ General Bots UI system built with HTMX and server-side rendering.
## Quick Access ## Quick Access
``` ```
http://localhost:8080 → Main interface http://localhost:9000 → Main interface
http://localhost:8080/chat → Chat app http://localhost:9000/chat → Chat app
http://localhost:8080/drive → File manager http://localhost:9000/drive → File manager
http://localhost:8080/console → Terminal mode http://localhost:9000/console → Terminal mode
``` ```
## Suite Applications ## Suite Applications

View file

@ -96,7 +96,7 @@ Bot responses support full Markdown rendering:
### WebSocket Connection ### WebSocket Connection
``` ```
ws://your-server:8080/ws ws://your-server:9000/ws
``` ```
**Message Types:** **Message Types:**

View file

@ -102,7 +102,7 @@ Default template for browser access:
```bash ```bash
./botserver ./botserver
# Browse to http://localhost:8080 # Browse to http://localhost:9000
# Loads Suite interface # Loads Suite interface
``` ```

View file

@ -90,7 +90,7 @@ Console mode supports any terminal with basic text output capabilities. UTF-8 su
## Tips ## Tips
Console mode operates in read-only fashion and does not accept bot commands. For interactive bot testing, use the web interface available at http://localhost:8080. The display refreshes automatically every few seconds to show current status. Output is buffered for performance to avoid slowing down the server during high activity periods. Console mode operates in read-only fashion and does not accept bot commands. For interactive bot testing, use the web interface available at http://localhost:9000. The display refreshes automatically every few seconds to show current status. Output is buffered for performance to avoid slowing down the server during high activity periods.
## Troubleshooting ## Troubleshooting

View file

@ -130,7 +130,7 @@ This means:
The widget connects via WebSocket for real-time updates: The widget connects via WebSocket for real-time updates:
``` ```
ws://localhost:8080/ws/dev ws://localhost:9000/ws/dev
``` ```
When connected: When connected:

View file

@ -63,7 +63,7 @@ Launch your preferred web browser by clicking its icon.
┌─────────────────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────────────────┐
│ 🌐 Browser [─][□][×]│ │ 🌐 Browser [─][□][×]│
├─────────────────────────────────────────────────────────────────────────┤ ├─────────────────────────────────────────────────────────────────────────┤
│ ← → ↻ │ https://your-company.bot:8080 │ ☆ │ │ │ ← → ↻ │ https://your-company.bot:9000 │ ☆ │ │
├─────────────────────────────────────────────────────────────────────────┤ ├─────────────────────────────────────────────────────────────────────────┤
│ │ │ │
│ Loading... │ │ Loading... │
@ -76,7 +76,7 @@ Launch your preferred web browser by clicking its icon.
Type your General Bots address in the address bar and press **Enter**. Type your General Bots address in the address bar and press **Enter**.
💡 **Tip**: Your URL will look something like: 💡 **Tip**: Your URL will look something like:
- `http://localhost:8080` (development) - `http://localhost:9000` (development)
- `https://bots.yourcompany.com` (production) - `https://bots.yourcompany.com` (production)
- `https://app.pragmatismo.cloud` (cloud hosted) - `https://app.pragmatismo.cloud` (cloud hosted)

View file

@ -21,7 +21,7 @@ The dashboard displays botserver at the center orchestrating all interactions, w
The monitoring dashboard is the **default homepage** when accessing Suite: The monitoring dashboard is the **default homepage** when accessing Suite:
```/dev/null/monitoring-url.txt#L1 ```/dev/null/monitoring-url.txt#L1
http://localhost:8080/monitoring http://localhost:9000/monitoring
``` ```
Or from within Suite: Or from within Suite:

View file

@ -71,7 +71,7 @@ Automatic dark mode activates based on system preference:
Connection handling is simplified for reliability: Connection handling is simplified for reliability:
```javascript ```javascript
const ws = new WebSocket('ws://localhost:8080/ws'); const ws = new WebSocket('ws://localhost:9000/ws');
ws.onmessage = (event) => { ws.onmessage = (event) => {
const data = JSON.parse(event.data); const data = JSON.parse(event.data);
@ -98,7 +98,7 @@ function sendMessage() {
The single.gbui template is perfect for embedding in existing websites: The single.gbui template is perfect for embedding in existing websites:
```html ```html
<iframe src="http://localhost:8080/ui/suite/single.gbui" <iframe src="http://localhost:9000/ui/suite/single.gbui"
width="400" width="400"
height="600"> height="600">
</iframe> </iframe>

View file

@ -23,7 +23,7 @@ General Bots Suite is your all-in-one workspace that combines communication, pro
### Opening the Suite ### Opening the Suite
1. **Open your web browser** (Chrome, Firefox, Safari, or Edge) 1. **Open your web browser** (Chrome, Firefox, Safari, or Edge)
2. **Go to your General Bots address** (example: `http://your-company.bot:8080`) 2. **Go to your General Bots address** (example: `http://your-company.bot:9000`)
3. **The Suite loads automatically** - you'll see the workspace in seconds 3. **The Suite loads automatically** - you'll see the workspace in seconds
### Your First Look ### Your First Look

View file

@ -59,7 +59,7 @@ The Suite interface provides multi-application integration with seamless navigat
The Suite interface is best suited for enterprise deployments requiring full functionality, power users working with multiple services simultaneously, desktop application distribution via Tauri builds, and multi-service integrations where context switching between modules matters. The Suite interface is best suited for enterprise deployments requiring full functionality, power users working with multiple services simultaneously, desktop application distribution via Tauri builds, and multi-service integrations where context switching between modules matters.
You can access the Suite interface via web at `http://localhost:8080/suite` or as a native desktop application using the `botui` Tauri app (see [BotUI Desktop](../botui/README.md)). You can access the Suite interface via web at `http://localhost:9000/suite` or as a native desktop application using the `botui` Tauri app (see [BotUI Desktop](../botui/README.md)).
## Minimal Interface ## Minimal Interface
@ -69,7 +69,7 @@ This lightweight interface provides core chat and basic interactions only, fast
The Minimal interface excels for mobile web access, embedded chatbots in external websites, low-bandwidth environments, quick access terminals and kiosks, and scenarios where simplicity matters more than features. The Minimal interface excels for mobile web access, embedded chatbots in external websites, low-bandwidth environments, quick access terminals and kiosks, and scenarios where simplicity matters more than features.
Access the Minimal interface at the root URL `http://localhost:8080` where it is served by default, explicitly at `http://localhost:8080/minimal`, or embedded via iframe or WebView in your own applications. Access the Minimal interface at the root URL `http://localhost:9000` where it is served by default, explicitly at `http://localhost:9000/minimal`, or embedded via iframe or WebView in your own applications.
## Configuration ## Configuration
@ -211,11 +211,11 @@ ls -la ui/suite/
ls -la ui/minimal/ ls -la ui/minimal/
# Test interfaces # Test interfaces
curl http://localhost:8080/ curl http://localhost:9000/
curl http://localhost:8080/suite/ curl http://localhost:9000/suite/
# Check static file serving # Check static file serving
curl http://localhost:8080/js/htmx-app.js curl http://localhost:9000/js/htmx-app.js
``` ```
## Customization ## Customization

View file

@ -4,18 +4,38 @@ Saves data to a database table using upsert (insert or update) semantics.
## Syntax ## Syntax
### Form 1: Save with object (classic)
```basic ```basic
SAVE "table", id, data SAVE "table", id, data
``` ```
### Form 2: Save with variables (direct)
```basic
SAVE "table", id, field1, field2, field3, ...
```
The variable names are used as column names automatically.
## Parameters ## Parameters
### Form 1 (with object)
| Parameter | Type | Description | | Parameter | Type | Description |
|-----------|------|-------------| |-----------|------|-------------|
| table | String | The name of the database table | | table | String | The name of the database table |
| id | String/Number | The unique identifier for the record | | id | String/Number | The unique identifier for the record |
| data | Object | A map/object containing field names and values | | data | Object | A map/object containing field names and values |
### Form 2 (with variables)
| Parameter | Type | Description |
|-----------|------|-------------|
| table | String | The name of the database table |
| id | String/Number | The unique identifier for the record |
| field1, field2, ... | Any | Variable references (names become column names) |
## Description ## Description
`SAVE` performs an upsert operation: `SAVE` performs an upsert operation:
@ -24,9 +44,55 @@ SAVE "table", id, data
The `id` parameter maps to the `id` column in the table. The `id` parameter maps to the `id` column in the table.
### Form 1 vs Form 2
**Form 1** (with object) is useful when you need custom column names or complex data structures:
```basic
data = #{
"customer_name": "João Silva",
"email": "joao@example.com"
}
SAVE "customers", "CUST-001", data
```
**Form 2** (with variables) is simpler - variable names become column names:
```basic
customerName = "João Silva"
email = "joao@example.com"
phone = "+5511999887766"
SAVE "customers", "CUST-001", customerName, email, phone
' Creates columns: customerName, email, phone
```
This eliminates the need for WITH blocks when variable names match your desired column names.
### Perfect for TOOL Functions
**This is especially useful for TOOL functions** where variables are automatically filled by user input and can be saved directly without needing WITH blocks:
```basic
' TOOL function parameters - automatically filled by LLM
PARAM nome AS STRING LIKE "João Silva" DESCRIPTION "Nome completo"
PARAM email AS EMAIL LIKE "joao@example.com" DESCRIPTION "Email"
PARAM telefone AS STRING LIKE "(21) 98888-8888" DESCRIPTION "Telefone"
' Generate unique ID
customerId = "CUST-" + FORMAT(NOW(), "yyyyMMddHHmmss")
' Save directly - variable names become column names automatically!
' No need for WITH block - just pass the variables directly
SAVE "customers", customerId, nome, email, telefone
RETURN customerId
```
In TOOL functions, the parameters (variables like `nome`, `email`, `telefone`) are automatically extracted from user input by the LLM. The direct SAVE syntax allows you to persist these variables immediately without manual object construction.
## Examples ## Examples
### Basic Save with Object ### Basic Save with Object (Form 1)
```basic ```basic
' Create data object using Rhai map syntax ' Create data object using Rhai map syntax
@ -40,32 +106,73 @@ data = #{
SAVE "customers", "CUST-001", data SAVE "customers", "CUST-001", data
``` ```
### Save Order Data ### Save with Variables - No WITH Block Needed (Form 2)
```basic
' Variable names become column names automatically
casamentoId = "CAS-20250117-1234"
protocolo = "CAS123456"
nomeNoivo = "Carlos Eduardo"
nomeNoiva = "Juliana Cristina"
telefoneNoivo = "(21) 98888-8888"
telefoneNoiva = "(21) 97777-7777"
emailNoivo = "carlos@example.com"
emailNoiva = "juliana@example.com"
tipoCasamento = "RELIGIOSO_COM_EFEITO_CIVIL"
dataPreferencial = "2026-12-15"
horarioPreferencial = "16:00"
' Save directly without WITH block
SAVE "casamentos", casamentoId, protocolo, nomeNoivo, nomeNoiva, telefoneNoivo, telefoneNoiva, emailNoivo, emailNoiva, tipoCasamento, dataPreferencial, horarioPreferencial
```
### Save Order Data (Direct Syntax - No Object)
```basic ```basic
order_id = "ORD-" + FORMAT(NOW(), "YYYYMMDDHHmmss") order_id = "ORD-" + FORMAT(NOW(), "YYYYMMDDHHmmss")
customer_id = "CUST-001"
customer_name = "João Silva"
total = 150.50
status = "pending"
order_data = #{ ' Save directly - variable names become columns
"customer_id": customer_id, SAVE "orders", order_id, customer_id, customer_name, total, status
"customer_name": customer_name,
"total": total,
"status": "pending",
"created_at": NOW()
}
SAVE "orders", order_id, order_data
TALK "Order " + order_id + " saved successfully!" TALK "Order " + order_id + " saved successfully!"
``` ```
### Save Event Registration
```basic
' Event registration form data
eventId = "EVT-" + FORMAT(NOW(), "YYYYMMDDHHmmss")
nome = "Maria Santos"
email = "maria@example.com"
telefone = "(11) 91234-5678"
dataEvento = "2025-03-15"
quantidadePessoas = 3
observacoes = "Precisa de cadeira de rodas"
' Direct save - no WITH block needed
SAVE "eventos", eventId, nome, email, telefone, dataEvento, quantidadePessoas, observacoes
TALK "Inscrição confirmada! ID: " + eventId
```
### Update Existing Record ### Update Existing Record
```basic ```basic
' If order exists, this updates it; otherwise creates it ' If order exists, this updates it; otherwise creates it
order_id = "ORD-20250117-0001"
status = "shipped"
shipped_at = NOW()
tracking_number = "TRACK123456"
' Use object for updates to specific columns
update_data = #{ update_data = #{
"status": "shipped", "status": status,
"shipped_at": NOW(), "shipped_at": shipped_at,
"tracking_number": tracking "tracking_number": tracking_number
} }
SAVE "orders", order_id, update_data SAVE "orders", order_id, update_data
@ -79,15 +186,10 @@ WEBHOOK "new-customer"
customer_id = "CUST-" + FORMAT(NOW(), "YYYYMMDDHHmmss") customer_id = "CUST-" + FORMAT(NOW(), "YYYYMMDDHHmmss")
phone = body.phone phone = body.phone
name = body.name name = body.name
source = "webhook"
customer_data = #{ ' Direct save with variables
"name": name, SAVE "customers", customer_id, phone, name, source
"phone": phone,
"source": "webhook",
"created_at": NOW()
}
SAVE "customers", customer_id, customer_data
' Notify via WhatsApp ' Notify via WhatsApp
TALK TO "whatsapp:" + phone, "Welcome " + name + "! Your account has been created." TALK TO "whatsapp:" + phone, "Welcome " + name + "! Your account has been created."
@ -121,12 +223,11 @@ WEBHOOK "create-order"
' Save order ' Save order
order_id = body.order_id order_id = body.order_id
order_data = #{ customer_id = body.customer_id
"customer_id": body.customer_id, total = body.total
"total": body.total, status = "pending"
"status": "pending"
} SAVE "orders", order_id, customer_id, total, status
SAVE "orders", order_id, order_data
' Save each line item ' Save each line item
FOR EACH item IN body.items FOR EACH item IN body.items
@ -146,6 +247,27 @@ TALK TO "whatsapp:" + body.customer_phone, "Order #" + order_id + " confirmed!"
result_status = "ok" result_status = "ok"
``` ```
### Comparison: WITH Block vs Direct Syntax
**Old way (WITH block):**
```basic
WITH casamento
id = casamentoId
protocolo = protocolo
noivo = nomeNoivo
noiva = nomeNoiva
END WITH
SAVE "casamentos", casamento
```
**New way (direct):**
```basic
' Variable names become column names automatically
SAVE "casamentos", casamentoId, protocolo, nomeNoivo, nomeNoiva
```
The direct syntax is cleaner and avoids the intermediate object creation. Use it when your variable names match your desired column names.
## Return Value ## Return Value
Returns an object with: Returns an object with:

View file

@ -4,11 +4,27 @@ Send email messages.
## Syntax ## Syntax
### Single Line
```basic ```basic
SEND MAIL to, subject, body SEND MAIL to, subject, body
SEND MAIL to, subject, body USING "account@example.com" SEND MAIL to, subject, body USING "account@example.com"
``` ```
### Multi-Line Block with Variable Substitution
```basic
BEGIN MAIL recipient
Subject: Email subject here
Dear ${customerName},
Your order ${orderId} is ready.
Thank you!
END MAIL
```
## Parameters ## Parameters
| Parameter | Type | Description | | Parameter | Type | Description |
@ -17,6 +33,7 @@ SEND MAIL to, subject, body USING "account@example.com"
| `subject` | String | Email subject line | | `subject` | String | Email subject line |
| `body` | String | Email body (plain text or HTML) | | `body` | String | Email body (plain text or HTML) |
| `account` | String | (Optional) Connected account to send through | | `account` | String | (Optional) Connected account to send through |
| `${variable}` | Expression | Variable substitution within MAIL blocks |
## Description ## Description
@ -25,6 +42,190 @@ The `SEND MAIL` keyword sends emails using either:
1. **Default SMTP** - Configuration from `config.csv` 1. **Default SMTP** - Configuration from `config.csv`
2. **Connected Account** - Send through Gmail, Outlook, etc. configured in Sources app 2. **Connected Account** - Send through Gmail, Outlook, etc. configured in Sources app
## BEGIN MAIL / END MAIL Blocks
The `BEGIN MAIL / END MAIL` block syntax allows you to write elegant multi-line emails with automatic variable substitution using `${variable}` syntax.
### Syntax
```basic
BEGIN MAIL recipient
Subject: Email subject ${variable}
Dear ${customerName},
Your order ${orderId} has been shipped.
Tracking: ${trackingNumber}
Best regards,
The Team
END MAIL
```
### How It Works
1. **First line after `BEGIN MAIL`**: Should contain the email recipient
2. **Line starting with `Subject:`**: Email subject line (supports `${variable}`)
3. **Blank line after subject**: Separates subject from body
4. **Body lines**: Email content with automatic `${variable}` substitution
5. **Each line** is converted to string concatenation with proper newline handling
**Input:**
```basic
nome = "João"
pedido = "12345"
BEGIN MAIL "cliente@example.com"
Subject: Confirmação do Pedido ${pedido}
Olá ${nome},
Seu pedido foi confirmado!
Atenciosamente,
Equipe de Vendas
END MAIL
```
**Converted to:**
```basic
SEND MAIL "cliente@example.com", "Confirmação do Pedido 12345", "Olá " + nome + ",\n\nSeu pedido foi confirmado!\n\nAtenciosamente,\nEquipe de Vendas"
```
### Variable Substitution Rules
- `${variableName}` - Replaced with the variable value
- `${FUNCTION(args)}` - Function calls are evaluated and substituted
- Plain text without `${}` is treated as a string literal
- Special characters like `$` (not followed by `{`) are preserved
- Newlines are preserved as `\n` in the final email body
### Examples
#### Simple Email
```basic
email = "customer@example.com"
nome = "Maria"
BEGIN MAIL email
Subject: Bem-vindo ao nosso serviço!
Olá ${nome},
Obrigado por se cadastrar!
Atenciosamente,
Equipe
END MAIL
```
#### With Function Calls
```basic
BEGIN MAIL "cliente@empresa.com"
Subject: Pedido ${pedidoId} - Confirmação
Prezado ${nomeCliente},
Confirmamos seu pedido #${pedidoId} no valor de ${FORMAT(total, "currency")}.
Entrega prevista para: ${FORMAT(dataEntrega, "dd/MM/yyyy")}
Atenciosamente,
Departamento de Vendas
END MAIL
```
#### HTML Email
```basic
BEGIN MAIL "cliente@exemplo.com"
Subject: Seu pedido foi enviado!
<h1>Confirmação de Pedido</h1>
<p>Olá ${nome},</p>
<p>Seu pedido <strong>${pedidoId}</strong> foi enviado com sucesso!</p>
<p>Valor: <em>${FORMAT(valor, "currency")}</em></p>
<p>Atenciosamente,<br>Loja Virtual</p>
END MAIL
```
### Real-World Example: Wedding Confirmation
```basic
PARAM nomeNoivo AS STRING LIKE "Carlos" DESCRIPTION "Nome do noivo"
PARAM nomeNoiva AS STRING LIKE "Ana" DESCRIPTION "Nome da noiva"
PARAM emailNoivo AS EMAIL LIKE "noivo@example.com" DESCRIPTION "Email do noivo"
PARAM emailNoiva AS EMAIL LIKE "noiva@example.com" DESCRIPTION "Email da noiva"
PARAM protocolo AS STRING LIKE "CAS123456" DESCRIPTION "Protocolo"
casamentoId = "CAS-" + FORMAT(NOW(), "yyyyMMddHHmmss")
tipoTexto = "Religioso Simples"
BEGIN MAIL emailNoivo
Subject: Confirmação de Casamento - Protocolo ${protocolo}
Queridos ${nomeNoivo} e ${nomeNoiva},
Parabéns pelo compromisso de amor que estão assumindo! Recebemos a solicitação de casamento no Santuário Cristo Redentor.
DADOS DA SOLICITAÇÃO:
Protocolo: ${protocolo}
ID: ${casamentoId}
Noivo: ${nomeNoivo}
Noiva: ${nomeNoiva}
Tipo: ${tipoTexto}
Nossa equipe verificará a disponibilidade e enviará todas as instruções necessárias em breve.
Que Deus abençoe a união de vocês!
Atenciosamente,
Secretaria do Santuário Cristo Redentor
Tel: (21) 4101-0770 | WhatsApp: (21) 99566-5883
END MAIL
```
### Multiple Recipients
Send the same email to multiple people:
```basic
BEGIN MAIL "team1@company.com"
Subject: Meeting Reminder
Team meeting tomorrow at 3 PM.
END MAIL
BEGIN MAIL "team2@company.com"
Subject: Meeting Reminder
Team meeting tomorrow at 3 PM.
END MAIL
```
Or use comma-separated recipients:
```basic
recipients = "john@company.com, jane@company.com, bob@company.com"
SEND MAIL recipients, "Meeting Update", "Meeting rescheduled to 4 PM"
```
### Advantages
1. **Cleaner Syntax** - No more repetitive string concatenation for email body
2. **Easier to Read** - Multi-line emails are natural to write and maintain
3. **Template-Like** - Write emails like templates with `${variable}` placeholders
4. **Automatic Newlines** - Blank lines in the block become `\n` in the email
5. **Perfect for TOOL Functions** - Variables are automatically filled by user input
## Examples
## Configuration ## Configuration
Default SMTP in `config.csv`: Default SMTP in `config.csv`:

View file

@ -4,18 +4,31 @@ Sends a message to the current conversation or to a specific recipient on any su
## Syntax ## Syntax
### Single Message
```basic ```basic
TALK message TALK message
TALK TO recipient, message TALK TO recipient, message
``` ```
### Multi-Line Block with Variable Substitution
```basic
BEGIN TALK
Line 1 with ${variable}
Line 2 with ${anotherVariable}
Plain text line
END TALK
```
## Parameters ## Parameters
| Parameter | Type | Description | | Parameter | Type | Description |
|-----------|------|-------------| |-----------|------|-------------|
| message | String | The message to send | | message | String | The message to send |
| recipient | String | Channel and address in format `channel:address` | | recipient | String | Channel and address in format `channel:address` |
| ${variable} | Expression | Variable substitution within TALK blocks |
## Description ## Description
@ -23,6 +36,151 @@ TALK TO recipient, message
- **TALK message** - Sends to the current conversation (web chat, WhatsApp, etc.) - **TALK message** - Sends to the current conversation (web chat, WhatsApp, etc.)
- **TALK TO recipient, message** - Sends to a specific recipient on any channel - **TALK TO recipient, message** - Sends to a specific recipient on any channel
- **BEGIN TALK / END TALK** - Multi-line block with automatic variable substitution
## BEGIN TALK / END TALK Blocks
The `BEGIN TALK / END TALK` block syntax allows you to write multiple messages with automatic variable substitution using `${variable}` syntax.
### Syntax
```basic
BEGIN TALK
Hello ${name}!
Your order ${orderId} is confirmed.
Total: ${FORMAT(total, "currency")}
Thank you for your purchase!
END TALK
```
Each line within the block becomes a separate `TALK` statement. The `${variable}` syntax is automatically converted to string concatenation.
### How It Works
**Input:**
```basic
nomeNoivo = "Carlos"
protocolo = "CAS123456"
BEGIN TALK
Solicitacao de Casamento enviada com sucesso!
PROTOCOLO: ${protocolo}
Noivo: ${nomeNoivo}
END TALK
```
**Converted to:**
```basic
TALK "Solicitacao de Casamento enviada com sucesso!"
TALK "PROTOCOLO: " + protocolo
TALK "Noivo: " + nomeNoivo
```
### Variable Substitution Rules
- `${variableName}` - Replaced with the variable value using string concatenation
- `${FUNCTION(args)}` - Function calls are evaluated and substituted
- Plain text without `${}` is treated as a string literal
- Special characters like `$` (not followed by `{`) are preserved as-is
### Examples
#### Simple Substitution
```basic
nome = "João"
idade = 30
BEGIN TALK
Olá ${nome}!
Você tem ${idade} anos.
END TALK
```
**Equivalent to:**
```basic
TALK "Olá " + nome + "!"
TALK "Você tem " + idade + " anos."
```
#### With Function Calls
```basic
total = 299.90
numero = 42
BEGIN TALK
Seu pedido: ${numero}
Total: ${FORMAT(total, "currency")}
Obrigado pela preferência!
END TALK
```
#### Mixed Content
```basic
nome = "Maria"
codigo = "PROMO2024"
desconto = 20
BEGIN TALK
🎉 Oferta Especial para ${nome}!
Use o código: ${codigo}
Desconto de ${desconto}%
Aproveite!
END TALK
```
### Real-World Example: Wedding Confirmation
```basic
PARAM nomeNoivo AS STRING LIKE "Carlos" DESCRIPTION "Nome do noivo"
PARAM nomeNoiva AS STRING LIKE "Ana" DESCRIPTION "Nome da noiva"
PARAM protocolo AS STRING LIKE "CAS123456" DESCRIPTION "Protocolo"
PARAM dataCasamento AS DATE LIKE "2026-12-15" DESCRIPTION "Data do casamento"
casamentoId = "CAS-" + FORMAT(NOW(), "yyyyMMddHHmmss")
dataDisplay = FORMAT(dataCasamento, "dd/MM/yyyy")
BEGIN TALK
✅ Solicitação de Casamento enviada com sucesso!
Protocolo: ${protocolo}
ID: ${casamentoId}
Noivo: ${nomeNoivo}
Noiva: ${nomeNoiva}
Data: ${dataDisplay}
Status: Aguardando verificação de disponibilidade
Contato: (21) 4101-0770
END TALK
```
This is much cleaner than writing individual TALK statements with manual concatenation:
**Old way:**
```basic
TALK "Solicitacao de Casamento enviada com sucesso!"
TALK "Protocolo: " + protocolo
TALK "ID: " + casamentoId
TALK "Noivo: " + nomeNoivo
TALK "Noiva: " + nomeNoiva
TALK "Data: " + dataDisplay
TALK "Status: Aguardando verificacao de disponibilidade"
TALK "Contato: (21) 4101-0770"
```
### Advantages
1. **Cleaner Syntax** - No more repetitive `TALK` statements and `+` concatenations
2. **Easier to Read** - Multi-line messages are more natural to write
3. **Less Error-Prone** - Automatic substitution reduces typos in variable names
4. **Template-Like** - Write messages like templates with `${variable}` placeholders
5. **Perfect for TOOL Functions** - Variables are automatically filled by user input
## TALK - Current Conversation
## TALK - Current Conversation ## TALK - Current Conversation

View file

@ -21,7 +21,7 @@ This script works identically whether the user is:
## Supported Channels ## Supported Channels
### Web (Default) ### Web (Default)
The primary channel. Users access via browser at `http://localhost:8080`. The primary channel. Users access via browser at `http://localhost:9000`.
### WhatsApp Business ### WhatsApp Business
Requires WhatsApp Business API configuration. Messages are automatically formatted for WhatsApp's constraints. Requires WhatsApp Business API configuration. Messages are automatically formatted for WhatsApp's constraints.

View file

@ -292,7 +292,7 @@ bot.example.com {
# WebSocket (sticky sessions) # WebSocket (sticky sessions)
handle /ws* { handle /ws* {
reverse_proxy botserver-1:8080 botserver-2:8080 { reverse_proxy botserver-1:9000 botserver-2:9000 {
lb_policy cookie lb_policy cookie
health_uri /api/health health_uri /api/health
health_interval 10s health_interval 10s
@ -301,7 +301,7 @@ bot.example.com {
# API (round robin) # API (round robin)
handle /api/* { handle /api/* {
reverse_proxy botserver-1:8080 botserver-2:8080 { reverse_proxy botserver-1:9000 botserver-2:9000 {
lb_policy round_robin lb_policy round_robin
fail_duration 30s fail_duration 30s
} }
@ -400,7 +400,7 @@ VAULT_ADDR=https://localhost:8200
VAULT_TOKEN=hvs.your-token-here VAULT_TOKEN=hvs.your-token-here
# Directory for user auth (Zitadel) # Directory for user auth (Zitadel)
DIRECTORY_URL=https://localhost:8080 DIRECTORY_URL=https://localhost:9000
DIRECTORY_CLIENT_ID=your-client-id DIRECTORY_CLIENT_ID=your-client-id
DIRECTORY_CLIENT_SECRET=your-client-secret DIRECTORY_CLIENT_SECRET=your-client-secret

View file

@ -87,7 +87,7 @@ scale-rule-queue-action,up
for i in {2..5}; do for i in {2..5}; do
lxc launch images:debian/12 botserver-$i lxc launch images:debian/12 botserver-$i
lxc config device add botserver-$i port-$((8080+i)) proxy \ lxc config device add botserver-$i port-$((8080+i)) proxy \
listen=tcp:0.0.0.0:$((8080+i)) connect=tcp:127.0.0.1:8080 listen=tcp:0.0.0.0:$((8080+i)) connect=tcp:127.0.0.1:9000
done done
``` ```
@ -207,7 +207,7 @@ update_load_balancer() {
upstreams="" upstreams=""
for container in $(lxc list -c n --format csv | grep "^botserver-"); do for container in $(lxc list -c n --format csv | grep "^botserver-"); do
ip=$(lxc list $container -c 4 --format csv | cut -d' ' -f1) ip=$(lxc list $container -c 4 --format csv | cut -d' ' -f1)
upstreams="$upstreams\n to $ip:8080" upstreams="$upstreams\n to $ip:9000"
done done
# Update Caddy config # Update Caddy config
@ -268,12 +268,12 @@ bot.example.com {
# Health check endpoint (no load balancing) # Health check endpoint (no load balancing)
handle /api/health { handle /api/health {
reverse_proxy localhost:8080 reverse_proxy localhost:9000
} }
# WebSocket connections (sticky sessions) # WebSocket connections (sticky sessions)
handle /ws* { handle /ws* {
reverse_proxy botserver-1:8080 botserver-2:8080 botserver-3:8080 { reverse_proxy botserver-1:9000 botserver-2:9000 botserver-3:9000 {
lb_policy cookie lb_policy cookie
lb_try_duration 5s lb_try_duration 5s
health_uri /api/health health_uri /api/health
@ -284,7 +284,7 @@ bot.example.com {
# API requests (round robin) # API requests (round robin)
handle /api/* { handle /api/* {
reverse_proxy botserver-1:8080 botserver-2:8080 botserver-3:8080 { reverse_proxy botserver-1:9000 botserver-2:9000 botserver-3:9000 {
lb_policy round_robin lb_policy round_robin
lb_try_duration 5s lb_try_duration 5s
health_uri /api/health health_uri /api/health
@ -295,7 +295,7 @@ bot.example.com {
# Static files (any instance) # Static files (any instance)
handle { handle {
reverse_proxy botserver-1:8080 botserver-2:8080 botserver-3:8080 { reverse_proxy botserver-1:9000 botserver-2:9000 botserver-3:9000 {
lb_policy first lb_policy first
} }
} }
@ -353,7 +353,7 @@ bot.example.com {
window 1m window 1m
} }
} }
reverse_proxy botserver:8080 reverse_proxy botserver:9000
} }
} }
``` ```

View file

@ -162,7 +162,7 @@ Use the Vault CLI or API:
```bash ```bash
# Directory (Zitadel) - includes URL, no longer in .env # Directory (Zitadel) - includes URL, no longer in .env
vault kv put gbo/directory \ vault kv put gbo/directory \
url=https://localhost:8080 \ url=https://localhost:9000 \
project_id=your-project-id \ project_id=your-project-id \
client_id=your-client-id \ client_id=your-client-id \
client_secret=your-client-secret client_secret=your-client-secret
@ -506,7 +506,7 @@ If you're currently using environment variables:
```bash ```bash
# .env - TOO MANY SECRETS! # .env - TOO MANY SECRETS!
DATABASE_URL=postgres://user:password@localhost/db DATABASE_URL=postgres://user:password@localhost/db
DIRECTORY_URL=https://localhost:8080 DIRECTORY_URL=https://localhost:9000
DIRECTORY_CLIENT_ID=your-client-id DIRECTORY_CLIENT_ID=your-client-id
DIRECTORY_CLIENT_SECRET=your-client-secret DIRECTORY_CLIENT_SECRET=your-client-secret
REDIS_PASSWORD=redis-secret REDIS_PASSWORD=redis-secret
@ -528,7 +528,7 @@ VAULT_TOKEN=hvs.xxxxx
```bash ```bash
# EVERYTHING in Vault # EVERYTHING in Vault
vault kv put gbo/directory \ vault kv put gbo/directory \
url=https://localhost:8080 \ url=https://localhost:9000 \
project_id=12345 \ project_id=12345 \
client_id=xxx \ client_id=xxx \
client_secret=xxx client_secret=xxx
@ -579,7 +579,7 @@ fi
# Store everything in Vault # Store everything in Vault
vault kv put gbo/directory \ vault kv put gbo/directory \
url="${DIRECTORY_URL:-https://localhost:8080}" \ url="${DIRECTORY_URL:-https://localhost:9000}" \
project_id="${DIRECTORY_PROJECT_ID:-}" \ project_id="${DIRECTORY_PROJECT_ID:-}" \
client_id="${ZITADEL_CLIENT_ID:-}" \ client_id="${ZITADEL_CLIENT_ID:-}" \
client_secret="${ZITADEL_CLIENT_SECRET:-}" client_secret="${ZITADEL_CLIENT_SECRET:-}"

View file

@ -9,7 +9,7 @@ This API is on the development roadmap. The endpoints documented below represent
## Base URL ## Base URL
``` ```
http://localhost:8080/api/v1/admin http://localhost:9000/api/v1/admin
``` ```
## Authentication ## Authentication

View file

@ -9,7 +9,7 @@ This API is on the development roadmap. The endpoints documented below represent
## Base URL ## Base URL
``` ```
http://localhost:8080/api/v1/ai http://localhost:9000/api/v1/ai
``` ```
## Authentication ## Authentication

View file

@ -9,7 +9,7 @@ This API is on the development roadmap. The endpoints documented below represent
## Base URL ## Base URL
``` ```
http://localhost:8080/api/v1/analytics http://localhost:9000/api/v1/analytics
``` ```
## Authentication ## Authentication

View file

@ -9,7 +9,7 @@ This API is on the development roadmap. The endpoints documented below represent
## Base URL ## Base URL
``` ```
http://localhost:8080/api/v1/backup http://localhost:9000/api/v1/backup
``` ```
## Authentication ## Authentication

View file

@ -9,7 +9,7 @@ This API is on the development roadmap. The endpoints documented below represent
## Base URL ## Base URL
``` ```
http://localhost:8080/api/v1/compliance http://localhost:9000/api/v1/compliance
``` ```
## Authentication ## Authentication

View file

@ -14,7 +14,7 @@ The Document Processing API enables:
## Base URL ## Base URL
``` ```
http://localhost:8080/api/v1/documents http://localhost:9000/api/v1/documents
``` ```
## Authentication ## Authentication
@ -47,7 +47,7 @@ curl -X POST \
-H "Authorization: Bearer token123" \ -H "Authorization: Bearer token123" \
-F "file=@document.pdf" \ -F "file=@document.pdf" \
-F 'process_options={"extract_text":true,"extract_metadata":true}' \ -F 'process_options={"extract_text":true,"extract_metadata":true}' \
http://localhost:8080/api/v1/documents/upload http://localhost:9000/api/v1/documents/upload
``` ```
**Response:** **Response:**
@ -532,7 +532,7 @@ import requests
# Upload and process document # Upload and process document
with open('document.pdf', 'rb') as f: with open('document.pdf', 'rb') as f:
response = requests.post( response = requests.post(
'http://localhost:8080/api/v1/documents/upload', 'http://localhost:9000/api/v1/documents/upload',
headers={'Authorization': 'Bearer token123'}, headers={'Authorization': 'Bearer token123'},
files={'file': f}, files={'file': f},
data={'process_options': '{"extract_text": true}'} data={'process_options': '{"extract_text": true}'}
@ -542,7 +542,7 @@ document_id = response.json()['document_id']
# Get extracted text # Get extracted text
text_response = requests.get( text_response = requests.get(
f'http://localhost:8080/api/v1/documents/{document_id}/text', f'http://localhost:9000/api/v1/documents/{document_id}/text',
headers={'Authorization': 'Bearer token123'} headers={'Authorization': 'Bearer token123'}
) )

View file

@ -22,7 +22,7 @@ async function authenticate() {
**cURL:** **cURL:**
```bash ```bash
# Session validation # Session validation
curl -X GET http://localhost:8080/auth/validate \ curl -X GET http://localhost:9000/auth/validate \
-H "Authorization: Bearer YOUR_SESSION_TOKEN" -H "Authorization: Bearer YOUR_SESSION_TOKEN"
``` ```
@ -55,7 +55,7 @@ async function createGroup() {
import requests import requests
def create_group(): def create_group():
url = "http://localhost:8080/api/groups/create" url = "http://localhost:9000/api/groups/create"
headers = { headers = {
"Authorization": "Bearer YOUR_TOKEN", "Authorization": "Bearer YOUR_TOKEN",
"Content-Type": "application/json" "Content-Type": "application/json"
@ -96,7 +96,7 @@ async function addMember(groupId, userId) {
**cURL:** **cURL:**
```bash ```bash
curl -X GET http://localhost:8080/api/admin/system/status \ curl -X GET http://localhost:9000/api/admin/system/status \
-H "Authorization: Bearer ADMIN_TOKEN" -H "Authorization: Bearer ADMIN_TOKEN"
``` ```
@ -113,7 +113,7 @@ import (
func getSystemStatus(token string) { func getSystemStatus(token string) {
client := &http.Client{} client := &http.Client{}
req, _ := http.NewRequest("GET", req, _ := http.NewRequest("GET",
"http://localhost:8080/api/admin/system/status", nil) "http://localhost:9000/api/admin/system/status", nil)
req.Header.Add("Authorization", "Bearer " + token) req.Header.Add("Authorization", "Bearer " + token)
resp, err := client.Do(req) resp, err := client.Do(req)
@ -164,7 +164,7 @@ class BotChat {
} }
connect() { connect() {
this.ws = new WebSocket('ws://localhost:8080/ws'); this.ws = new WebSocket('ws://localhost:9000/ws');
this.ws.onopen = () => { this.ws.onopen = () => {
console.log('Connected to bot'); console.log('Connected to bot');
@ -370,7 +370,7 @@ function handleConversationCompleted(conversationData) {
1. Import the API collection (when available) 1. Import the API collection (when available)
2. Set environment variables for: 2. Set environment variables for:
- `base_url`: http://localhost:8080 - `base_url`: http://localhost:9000
- `token`: Your session token - `token`: Your session token
3. Run requests individually or as collection 3. Run requests individually or as collection

View file

@ -15,7 +15,7 @@ The Group Membership API enables:
## Base URL ## Base URL
``` ```
http://localhost:8080/api/v1/groups http://localhost:9000/api/v1/groups
``` ```
## Authentication ## Authentication

View file

@ -9,7 +9,7 @@ This API is on the development roadmap. The endpoints documented below represent
## Base URL ## Base URL
``` ```
http://localhost:8080/api/v1/ml http://localhost:9000/api/v1/ml
``` ```
## Authentication ## Authentication

View file

@ -9,7 +9,7 @@ This API is on the development roadmap. The endpoints documented below represent
## Base URL ## Base URL
``` ```
http://localhost:8080/api/v1/monitoring http://localhost:9000/api/v1/monitoring
``` ```
## Authentication ## Authentication

View file

@ -14,7 +14,7 @@ The Notifications API enables:
## Base URL ## Base URL
``` ```
http://localhost:8080/api/v1/notifications http://localhost:9000/api/v1/notifications
``` ```
## Authentication ## Authentication
@ -446,7 +446,7 @@ curl -X POST \
"title": "Hello", "title": "Hello",
"message": "This is a test notification" "message": "This is a test notification"
}' \ }' \
http://localhost:8080/api/v1/notifications/send http://localhost:9000/api/v1/notifications/send
``` ```
### Schedule Broadcast ### Schedule Broadcast
@ -463,7 +463,7 @@ curl -X POST \
}, },
"schedule": "2024-01-20T02:00:00Z" "schedule": "2024-01-20T02:00:00Z"
}' \ }' \
http://localhost:8080/api/v1/notifications/broadcast http://localhost:9000/api/v1/notifications/broadcast
``` ```
## Best Practices ## Best Practices

View file

@ -9,7 +9,7 @@ This API is on the development roadmap. The endpoints documented below represent
## Base URL ## Base URL
``` ```
http://localhost:8080/api/v1/reports http://localhost:9000/api/v1/reports
``` ```
## Authentication ## Authentication

View file

@ -9,7 +9,7 @@ This API is on the development roadmap. The endpoints documented below represent
## Base URL ## Base URL
``` ```
http://localhost:8080/api/v1/security http://localhost:9000/api/v1/security
``` ```
## Authentication ## Authentication

View file

@ -14,7 +14,7 @@ The Storage API allows you to:
## Base URL ## Base URL
``` ```
http://localhost:8080/api/v1/storage http://localhost:9000/api/v1/storage
``` ```
## Authentication ## Authentication
@ -268,7 +268,7 @@ curl -X PUT \
-H "Authorization: Bearer token123" \ -H "Authorization: Bearer token123" \
-H "Content-Type: application/pdf" \ -H "Content-Type: application/pdf" \
--data-binary @document.pdf \ --data-binary @document.pdf \
http://localhost:8080/api/v1/storage/buckets/mybot.gbai/objects/docs/manual.pdf http://localhost:9000/api/v1/storage/buckets/mybot.gbai/objects/docs/manual.pdf
``` ```
### Download File ### Download File
@ -276,7 +276,7 @@ curl -X PUT \
```bash ```bash
curl -X GET \ curl -X GET \
-H "Authorization: Bearer token123" \ -H "Authorization: Bearer token123" \
http://localhost:8080/api/v1/storage/buckets/mybot.gbai/objects/docs/manual.pdf \ http://localhost:9000/api/v1/storage/buckets/mybot.gbai/objects/docs/manual.pdf \
-o downloaded.pdf -o downloaded.pdf
``` ```
@ -285,7 +285,7 @@ curl -X GET \
```bash ```bash
curl -X GET \ curl -X GET \
-H "Authorization: Bearer token123" \ -H "Authorization: Bearer token123" \
"http://localhost:8080/api/v1/storage/buckets/mybot.gbai/objects?prefix=docs/" "http://localhost:9000/api/v1/storage/buckets/mybot.gbai/objects?prefix=docs/"
``` ```
## Storage Organization ## Storage Organization

View file

@ -15,7 +15,7 @@ The Tasks API enables:
## Base URL ## Base URL
``` ```
http://localhost:8080/api/v1/tasks http://localhost:9000/api/v1/tasks
``` ```
## Authentication ## Authentication

View file

@ -15,7 +15,7 @@ The User Security API enables:
## Base URL ## Base URL
``` ```
http://localhost:8080/api/v1/security http://localhost:9000/api/v1/security
``` ```
## Authentication ## Authentication

View file

@ -359,7 +359,7 @@ Register new user (if self-registration enabled).
For full user management, access Zitadel admin console: For full user management, access Zitadel admin console:
1. **Access Console**: `http://localhost:8080` (or your Zitadel URL) 1. **Access Console**: `http://localhost:9000` (or your Zitadel URL)
2. **Create Users**: Organization → Users → Add 2. **Create Users**: Organization → Users → Add
3. **Manage Roles**: Users → Select User → Authorizations 3. **Manage Roles**: Users → Select User → Authorizations
4. **Reset Passwords**: Users → Select User → Actions → Reset Password 4. **Reset Passwords**: Users → Select User → Actions → Reset Password

View file

@ -159,7 +159,7 @@ Run General Bots on your own infrastructure with single binary deployment, conta
./botserver ./botserver
``` ```
Access at `http://localhost:8080` and start building. Access at `http://localhost:9000` and start building.
## Summary ## Summary

View file

@ -85,7 +85,7 @@ app.example.com {
| ⚠️ | Password Rotation | Directory | HIPAA | Configure 90-day rotation policy | | ⚠️ | Password Rotation | Directory | HIPAA | Configure 90-day rotation policy |
| 📝 | Access Reviews | Directory | All | Quarterly manual review of user permissions | | 📝 | Access Reviews | Directory | All | Quarterly manual review of user permissions |
**Configuration**: Directory Admin Console (`http://localhost:8080`) **Configuration**: Directory Admin Console (`http://localhost:9000`)
**Key Settings**: **Key Settings**:
- Password min length: 12 characters - Password min length: 12 characters
@ -206,7 +206,7 @@ directory = "oidc"
[directory."oidc"] [directory."oidc"]
type = "oidc" type = "oidc"
issuer = "http://localhost:8080" issuer = "http://localhost:9000"
``` ```
**DNS Records**: **DNS Records**:

View file

@ -180,7 +180,7 @@ cargo test --all
RUSTFLAGS="-Z sanitizer=address" cargo +nightly test RUSTFLAGS="-Z sanitizer=address" cargo +nightly test
# Verify rate limiting # Verify rate limiting
curl -X POST http://localhost:8080/api/test \ curl -X POST http://localhost:9000/api/test \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
--data '{}' \ --data '{}' \
--parallel --parallel-max 1000 --parallel --parallel-max 1000

View file

@ -123,7 +123,7 @@ Document WebSocket protocols with connection details, message formats for both d
### Connection ### Connection
``` ```
ws://localhost:8080/ws ws://localhost:9000/ws
``` ```
### Message Format ### Message Format

View file

@ -67,7 +67,7 @@ return {
'botserver/nvim-botserver', 'botserver/nvim-botserver',
config = function() config = function()
require('botserver').setup({ require('botserver').setup({
server_url = 'http://localhost:8080', server_url = 'http://localhost:9000',
default_bot = 'edu' default_bot = 'edu'
}) })
end end

View file

@ -313,7 +313,7 @@ cargo test --all-features
3. **Port Already in Use** 3. **Port Already in Use**
- Change SERVER_PORT in .env - Change SERVER_PORT in .env
- Kill existing process: `lsof -i :8080` - Kill existing process: `lsof -i :9000`
4. **Compilation Errors** 4. **Compilation Errors**
- Update Rust: `rustup update` - Update Rust: `rustup update`

View file

@ -229,7 +229,7 @@ cd botserver
Open in your browser: Open in your browser:
``` ```
http://mybot.local:8088 http://mybot.local:9000
``` ```
## Common Beginner Mistakes ## Common Beginner Mistakes

View file

@ -83,7 +83,7 @@ wget https://huggingface.co/TheBloke/TinyLlama-1.1B-Chat-v1.0-GGUF/resolve/main/
--threads 4 --threads 4
# Verify # Verify
curl http://localhost:8080/v1/models curl http://localhost:9000/v1/models
``` ```
### Systemd Service ### Systemd Service
@ -127,7 +127,7 @@ sudo systemctl start llama-server
```env ```env
# Use local llama.cpp # Use local llama.cpp
LLM_PROVIDER=llamacpp LLM_PROVIDER=llamacpp
LLM_API_URL=http://127.0.0.1:8080 LLM_API_URL=http://127.0.0.1:9000
LLM_MODEL=tinyllama LLM_MODEL=tinyllama
# Memory limits # Memory limits
@ -263,7 +263,7 @@ llama.cpp exposes an OpenAI-compatible API:
### Chat Completion ### Chat Completion
```bash ```bash
curl http://localhost:8080/v1/chat/completions \ curl http://localhost:9000/v1/chat/completions \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{ -d '{
"model": "tinyllama", "model": "tinyllama",
@ -277,7 +277,7 @@ curl http://localhost:8080/v1/chat/completions \
### Streaming ### Streaming
```bash ```bash
curl http://localhost:8080/v1/chat/completions \ curl http://localhost:9000/v1/chat/completions \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
-d '{ -d '{
"model": "tinyllama", "model": "tinyllama",
@ -289,8 +289,8 @@ curl http://localhost:8080/v1/chat/completions \
### Health Check ### Health Check
```bash ```bash
curl http://localhost:8080/health curl http://localhost:9000/health
curl http://localhost:8080/v1/models curl http://localhost:9000/v1/models
``` ```
## Monitoring ## Monitoring

View file

@ -231,7 +231,7 @@ BotDevice can work offline with local LLM:
1. Install botserver on the device (see [Local LLM](./local-llm.md)) 1. Install botserver on the device (see [Local LLM](./local-llm.md))
2. Configure to use localhost: 2. Configure to use localhost:
```javascript ```javascript
window.BOTSERVER_URL = "http://127.0.0.1:8088"; window.BOTSERVER_URL = "http://127.0.0.1:9000";
``` ```
3. Run llama.cpp with small model (TinyLlama on 4GB+ devices) 3. Run llama.cpp with small model (TinyLlama on 4GB+ devices)

View file

@ -22,9 +22,9 @@ cd botserver
``` ```
That's it! After ~10-15 minutes: That's it! After ~10-15 minutes:
- botserver runs on port 8088 - botserver runs on port 9000
- llama.cpp runs on port 8080 with TinyLlama - llama.cpp runs on port 8081 with TinyLlama
- Embedded UI available at `http://your-device:8088/embedded/` - Embedded UI available at `http://your-device:9000/embedded/`
## Step-by-Step Guide ## Step-by-Step Guide
@ -87,17 +87,17 @@ ssh pi@raspberrypi.local 'sudo systemctl status botserver'
ssh pi@raspberrypi.local 'sudo systemctl status llama-server' ssh pi@raspberrypi.local 'sudo systemctl status llama-server'
# Test botserver # Test botserver
curl http://raspberrypi.local:8088/health curl http://raspberrypi.local:9000/health
# Test llama.cpp # Test llama.cpp
curl http://raspberrypi.local:8080/v1/models curl http://raspberrypi.local:9000/v1/models
``` ```
### Step 5: Access the Interface ### Step 5: Access the Interface
Open in your browser: Open in your browser:
``` ```
http://raspberrypi.local:8088/embedded/ http://raspberrypi.local:9000/embedded/
``` ```
Or set up kiosk mode (auto-starts on boot): Or set up kiosk mode (auto-starts on boot):
@ -142,11 +142,11 @@ Key settings:
```env ```env
# Server # Server
HOST=0.0.0.0 HOST=0.0.0.0
PORT=8088 PORT=9000
# Local LLM # Local LLM
LLM_PROVIDER=llamacpp LLM_PROVIDER=llamacpp
LLM_API_URL=http://127.0.0.1:8080 LLM_API_URL=http://127.0.0.1:8081
LLM_MODEL=tinyllama LLM_MODEL=tinyllama
# Memory limits for small devices # Memory limits for small devices

View file

@ -100,7 +100,7 @@ lxc config set botserver limits.memory 4GB
lxc config set botserver limits.cpu 2 lxc config set botserver limits.cpu 2
# Forward ports # Forward ports
lxc config device add botserver http proxy listen=tcp:0.0.0.0:80 connect=tcp:127.0.0.1:8080 lxc config device add botserver http proxy listen=tcp:0.0.0.0:80 connect=tcp:127.0.0.1:9000
lxc config device add botserver https proxy listen=tcp:0.0.0.0:443 connect=tcp:127.0.0.1:8443 lxc config device add botserver https proxy listen=tcp:0.0.0.0:443 connect=tcp:127.0.0.1:8443
# Set environment for Vault # Set environment for Vault
@ -118,7 +118,7 @@ services:
botserver: botserver:
image: generalbots/botserver:latest image: generalbots/botserver:latest
ports: ports:
- "8080:8080" - "8080:9000"
environment: environment:
- VAULT_ADDR=http://vault:8200 - VAULT_ADDR=http://vault:8200
volumes: volumes:

View file

@ -404,6 +404,7 @@ botserver rotate-secret <component>
| `email` | SMTP password | | `email` | SMTP password |
| `directory` | Zitadel client secret | | `directory` | Zitadel client secret |
| `encryption` | Master encryption key (⚠️ dangerous) | | `encryption` | Master encryption key (⚠️ dangerous) |
| `jwt` | JWT signing secret (⚠️ invalidates refresh tokens) |
**Examples:** **Examples:**
@ -496,6 +497,73 @@ botserver version --all
--- ---
## Security Considerations
### Current Limitations
⚠️ **Manual Service Updates Required**
After rotating credentials, you MUST manually update each service:
- **Database (tables):** Run the provided SQL command to update PostgreSQL user password
- **Drive (MinIO):** Run the provided `mc admin` commands to update S3 credentials
- **Cache (Redis):** Run the provided `redis-cli` command to update password
- **Directory (Zitadel):** Update client secret via admin console
⚠️ **Service Restart Required**
After rotating **JWT secret**, you MUST restart botserver:
```bash
botserver restart
```
All users will need to re-login (refresh tokens invalidated). Access tokens (15-minute expiry) will expire naturally.
⚠️ **No Automatic Rollback**
If verification fails, you must manually restore from backups:
```bash
# Database: Re-run SQL with old password
# JWT: Restore .env.backup.<timestamp>
# Other: Use backup values shown in rotation output
```
### Available Components for Rotation
| Component | Credential Type | Manual Update Required | Service Restart |
|-----------|----------------|------------------------|-----------------|
| `tables` | PostgreSQL password | ✅ Run SQL command | ❌ No |
| `drive` | MinIO S3 credentials | ✅ Run mc commands | ❌ No |
| `cache` | Redis/Valkey password | ✅ Run redis-cli | ❌ No |
| `email` | SMTP password | ✅ Update mail server | ❌ No |
| `directory` | Zitadel client secret | ✅ Update via console | ❌ No |
| `encryption` | Master encryption key | ⚠️ Re-encrypt all data | ❌ No |
| `jwt` | JWT signing secret | ❌ No | ✅ **Yes** |
### Best Practices
1. **Test in staging first** - Never rotate in production without testing
2. **Schedule during low traffic** - Rotate JWT outside peak hours
3. **Have rollback plan ready** - Save backup paths shown during rotation
4. **Monitor logs** - Check for authentication failures after rotation:
```bash
tail -f /var/log/botserver/app.log | grep -i "authentication\\|jwt\\|token"
```
5. **Rotate regularly** - Every 90 days for production, per security compliance
6. **After JWT rotation** - Verify all services are healthy before declaring success
### Verification
The `rotate-secret` command includes automatic verification where possible:
- **Database:** Tests PostgreSQL connection with new credentials
- **JWT:** Checks health endpoint (requires service to be running)
- **Other:** Displays manual verification instructions
If verification fails:
1. Check the error message for specific failure details
2. Restore from backup if needed
3. Re-run rotation after fixing the issue
---
## Complete Setup Example ## Complete Setup Example
Here's a complete workflow to set up Vault and migrate secrets. Here's a complete workflow to set up Vault and migrate secrets.

View file

@ -63,7 +63,7 @@ ss -tlnp | grep LISTEN
1. **Port already in use** 1. **Port already in use**
```bash ```bash
# Find what's using the port # Find what's using the port
lsof -i :8080 lsof -i :9000
lsof -i :5432 lsof -i :5432
# Kill conflicting process # Kill conflicting process

View file

@ -14,7 +14,7 @@ The Security Protection module provides comprehensive host-level security throug
▼ HTMX/API calls ▼ HTMX/API calls
┌─────────────────────────────────────────────────────────────┐ ┌─────────────────────────────────────────────────────────────┐
│ botserver (port 8088) │ │ botserver (port 9000) │
│ /api/security/protection/* │ │ /api/security/protection/* │
└─────────────────────────────────────────────────────────────┘ └─────────────────────────────────────────────────────────────┘

View file

@ -79,7 +79,7 @@
<text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">botserver</text> <text x="80" y="20" text-anchor="middle" font-size="11" font-weight="600" class="white-text">botserver</text>
<text x="80" y="55" text-anchor="middle" font-size="10" class="main-text">(Rust)</text> <text x="80" y="55" text-anchor="middle" font-size="10" class="main-text">(Rust)</text>
<text x="80" y="72" text-anchor="middle" font-size="10" font-weight="500" class="main-text">Port 8088</text> <text x="80" y="72" text-anchor="middle" font-size="10" font-weight="500" class="main-text">Port 9000</text>
<text x="80" y="95" text-anchor="middle" font-size="9" class="mono-text">ARM64 Binary</text> <text x="80" y="95" text-anchor="middle" font-size="9" class="mono-text">ARM64 Binary</text>
</g> </g>

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.6 KiB

View file

@ -90,7 +90,7 @@
<text x="85" y="52" text-anchor="middle" font-size="10" class="main-text">Rust Runtime</text> <text x="85" y="52" text-anchor="middle" font-size="10" class="main-text">Rust Runtime</text>
<text x="85" y="68" text-anchor="middle" font-size="9" class="mono-text">Session Manager</text> <text x="85" y="68" text-anchor="middle" font-size="9" class="mono-text">Session Manager</text>
<text x="85" y="84" text-anchor="middle" font-size="9" class="mono-text">Port 8088</text> <text x="85" y="84" text-anchor="middle" font-size="9" class="mono-text">Port 9000</text>
</g> </g>
<!-- Arrow 2 --> <!-- Arrow 2 -->

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View file

@ -46,7 +46,7 @@
<g transform="translate(320, 80)"> <g transform="translate(320, 80)">
<rect width="240" height="280" rx="10" fill="#E8F5E9" stroke="#388E3C" stroke-width="2"/> <rect width="240" height="280" rx="10" fill="#E8F5E9" stroke="#388E3C" stroke-width="2"/>
<text x="120" y="30" text-anchor="middle" class="box-label">botserver (Rust)</text> <text x="120" y="30" text-anchor="middle" class="box-label">botserver (Rust)</text>
<text x="120" y="50" text-anchor="middle" class="small-text">Port 8088</text> <text x="120" y="50" text-anchor="middle" class="small-text">Port 9000</text>
<rect x="20" y="70" width="200" height="40" rx="4" fill="#C8E6C9" stroke="#388E3C"/> <rect x="20" y="70" width="200" height="40" rx="4" fill="#C8E6C9" stroke="#388E3C"/>
<text x="120" y="95" text-anchor="middle" class="small-text">BASIC Interpreter</text> <text x="120" y="95" text-anchor="middle" class="small-text">BASIC Interpreter</text>
<rect x="20" y="120" width="200" height="40" rx="4" fill="#C8E6C9" stroke="#388E3C"/> <rect x="20" y="120" width="200" height="40" rx="4" fill="#C8E6C9" stroke="#388E3C"/>

Before

Width:  |  Height:  |  Size: 6.9 KiB

After

Width:  |  Height:  |  Size: 6.9 KiB

View file

@ -299,7 +299,7 @@ Or continue reading for the full journey:
<div class="wa-bubble"> <div class="wa-bubble">
<p>Great! Just run:</p> <p>Great! Just run:</p>
<p><code>./botserver</code></p> <p><code>./botserver</code></p>
<p>Then open http://localhost:8080</p> <p>Then open http://localhost:9000</p>
<p>That's it! 🚀</p> <p>That's it! 🚀</p>
<div class="wa-time">09:00</div> <div class="wa-time">09:00</div>
</div> </div>

View file

@ -250,7 +250,7 @@ chmod +x botserver
### 2. Open Browser ### 2. Open Browser
``` ```
http://localhost:8080 http://localhost:9000
``` ```
### 3. Start Chatting ### 3. Start Chatting