18 KiB
General Bots AI Agent Guidelines
- Use apenas a língua culta ao falar.
- Never save files on root! Use
/tmpfor temp files. - Never push to ALM without asking first — it is production!
- If in trouble with a tool, go to the official website for install instructions.
- See
botserver/src/drive/local_file_monitor.rsto load bots from/opt/gbo/data.
📁 Workspace Structure
| Crate | Purpose | Port | Tech Stack |
|---|---|---|---|
| botserver | Main API server, business logic | 8080 | Axum, Diesel, Rhai BASIC |
| botui | Web UI server (dev) + proxy | 3000 | Axum, HTML/HTMX/CSS |
| botapp | Desktop app wrapper | - | Tauri 2 |
| botlib | Shared library | - | Core types, errors |
| botbook | Documentation | - | mdBook |
| bottest | Integration tests | - | tokio-test |
| botdevice | IoT/Device support | - | Rust |
| botplugin | Browser extension | - | JS |
Key Paths
- Binary:
target/debug/botserver - Run from:
botserver/directory - Env file:
botserver/.env - UI Files:
botui/ui/suite/ - Bot data:
/opt/gbo/data(primary) - Test web:
http://localhost:3000— Login:http://localhost:3000/suite/auth/login.html
📦 Data Directory Structure
# DEV LOCAL (quando botserver-stack existe)
├── ./botserver-stack/data/system/work/{bot}.gbai/{bot}.gbdialog/
│ ├── *.bas # Scripts compilados (gerado automático)
│ └── *.ast # Cache compilado (deletar para forçar recompilação)
# PRODUCTION (com container Incus)
├── /opt/gbo/data/ # FONTE dos bots
└── (compilação fica em memória ou /opt/gbo/work/ se existir)
IMPORTANTE:
- FONTE:
/opt/gbo/data/{bot}.gbai/{bot}.gbdialog/{tool}.bas - DEV LOCAL:
./botserver-stack/data/system/work/{bot}.gbai/{bot}.gbdialog/ - O botserver compila
.bas→.astautomaticamente - Se cache, deletar
.astpara forçar recompilação
🧪 Debugging & Testing Tools
🔍 Ver Erros de Execução
tail -f botserver.log | grep -i "error\|tool"
🧪 Testar Ferramenta Específica
-
Identificar o erro no log:
grep -A5 "Tool error" botserver.log -
Corrigir o arquivo
.basna fonte:- Dev local:
./botserver-stack/data/system/work/{bot}.gbai/{bot}.gbdialog/{tool}.bas - Production:
/opt/gbo/data/{bot}.gbai/{bot}.gbdialog/{tool}.bas
- Dev local:
-
Forçar recompilação (se necessário):
rm ./botserver-stack/data/system/work/{bot}.gbai/{bot}.gbdialog/{tool}.ast- Em dev local o AST fica em
./botserver-stack/... - Em production pode ficar em
/opt/gbo/work/...se existir
- Em dev local o AST fica em
-
Testar novamente no browser:
http://localhost:3000/{botname}
⚠️ Erros Comuns em Scripts BASIC
| Erro | Causa | Solução |
|---|---|---|
=== is not a valid operator |
BASIC usa ==, não === |
Substituir === por -- em strings |
Syntax error |
Erro de sintaxe BASIC | Verificar parênteses, vírgulas |
Tool execution failed |
Erro no script | Ver logs para stack trace |
📝 Exemplo: Corrigir Operador Inválido
# ERRADO (JavaScript syntax):
PRINT "=== RESULTADO ==="
# CORRETO (BASIC syntax):
PRINT "-- RESULTADO --"
🧭 LLM Navigation Guide
- Start with Component Dependency Graph
- Review Module Responsibility Matrix
- Study Data Flow Patterns
- Reference Common Architectural Patterns
- Check Security Rules — violations are blocking
- Follow Code Patterns — consistency is mandatory
❌ Absolute Prohibitions
Build & Deploy
- ❌ NEVER search
/targetfolder - ❌ NEVER build in release mode or use
--release - ❌ NEVER run
cargo build— usecargo checkfor verification - ❌ NEVER run
cargo clean— causes 30min rebuilds; use./reset.shfor DB issues - ❌ NEVER deploy manually — ALWAYS use CI/CD pipeline (push → ALM → alm-ci builds → deploys)
- ❌ NEVER use
scp, direct SSH binary copy, or manual deployment - ❌ NEVER run the binary directly — use
systemctlor./restart.sh
Code Quality
- ❌ NEVER use
panic!(),todo!(),unimplemented!(),unwrap(),expect() - ❌ NEVER use
Command::new()directly — useSafeCommand - ❌ NEVER return raw error strings to HTTP clients — use
ErrorSanitizer - ❌ NEVER use
#[allow()]or lint exceptions inCargo.toml— FIX the code - ❌ NEVER use
_prefix for unused vars — DELETE or USE them - ❌ NEVER leave unused imports, dead code, or commented-out code
- ❌ NEVER use CDN links — all assets must be local
- ❌ NEVER create
.mddocs without checkingbotbook/first - ❌ NEVER hardcode credentials — use
generate_random_string()or env vars
Security
- ❌ NEVER include sensitive data (IPs, tokens, keys) in docs or code
- ❌ NEVER write internal IPs to logs — mask them (e.g., "10.x.x.x")
- ❌ NEVER create files with secrets in repo root
Secret files MUST be placed in
/tmp/only (ephemeral, not tracked by git).
🔐 Security Directives — MANDATORY
1. Error Handling — No Panics
// ❌ FORBIDDEN: unwrap(), expect(), panic!(), todo!()
// ✅ REQUIRED:
value?
value.ok_or_else(|| Error::NotFound)?
value.unwrap_or_default()
if let Some(v) = value { ... }
2. Command Execution — SafeCommand
// ❌ FORBIDDEN: Command::new("cmd").arg(user_input).output()
// ✅ REQUIRED:
use crate::security::command_guard::SafeCommand;
SafeCommand::new("allowed_command")?.arg("safe_arg")?.execute()
3. Error Responses — ErrorSanitizer
// ❌ FORBIDDEN: Json(json!({ "error": e.to_string() }))
// ✅ REQUIRED:
use crate::security::error_sanitizer::log_and_sanitize;
let sanitized = log_and_sanitize(&e, "context", None);
(StatusCode::INTERNAL_SERVER_ERROR, sanitized)
4. SQL — sql_guard
// ❌ FORBIDDEN: format!("SELECT * FROM {}", user_table)
// ✅ REQUIRED:
use crate::security::sql_guard::{sanitize_identifier, validate_table_name};
let safe_table = sanitize_identifier(&user_table);
validate_table_name(&safe_table)?;
5. Rate Limiting
- General: 100 req/s, Auth: 10 req/s, API: 50 req/s per token, WebSocket: 10 msgs/s
- Use
governorcrate with per-IP and per-User tracking
6. CSRF Protection
- ALL state-changing endpoints (POST/PUT/DELETE/PATCH) MUST require CSRF token
- Use
tower_csrf, bound to user session. Exempt: Bearer Token endpoints
7. Security Headers (ALL responses)
Content-Security-Policy, Strict-Transport-Security, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy: geolocation=(), microphone=(), camera=()
8. Dependency Management
- App crates track
Cargo.lock; lib crates don't - Critical deps: exact versions (
=1.0.1); regular: caret (1.0) - Run
cargo auditweekly; update only via PR with testing
✅ Mandatory Code Patterns
impl MyStruct { fn new() -> Self { Self { } } } // Use Self, not type name
#[derive(PartialEq, Eq)] // Always derive both
format!("Hello {name}") // Inline format args
match x { A | B => do_thing(), C => other() } // Combine identical arms
📏 File Size Limits
- Max 450 lines per file — split proactively at 350 lines
- Split by:
types.rs,handlers.rs,operations.rs,utils.rs,mod.rs - Re-export all public items in
mod.rs
🔥 Error Fixing Workflow
Preferred: Offline Batch Fix
- Read ENTIRE error list first
- Group errors by file
- For each file: view → fix ALL errors → write once
- Verify with build/diagnostics only AFTER all fixes
⚡ Streaming Build Rule
Don't wait for cargo to finish — cancel at first errors, fix, re-run.
🧠 Memory Issues (process "Killed")
pkill -9 cargo; pkill -9 rustc; pkill -9 botserver
CARGO_BUILD_JOBS=1 cargo check -p botserver 2>&1 | tail -200
🔄 Modos de Execução
O botserver suporta dois modos de execução:
Modo 1: Local Standalone (sem Docker/Incus)
O botserver sobe tudo localmente (PostgreSQL, Valkey, MinIO, Vault, LLM).
cd /home/rodriguez/src/gb/botserver
cargo run -- --install # Instala dependências (PostgreSQL, Valkey, MinIO, etc.)
cargo run # Sobe tudo e inicia o servidor
O que acontece:
PackageManagerbaixa e extrai binários parabotserver-stack/bin/- Cria
botserver-stack/data/pgdata/com PostgreSQL - Inicia PostgreSQL na porta 5432
- Inicia Valkey na porta 6379
- Inicia MinIO na porta 9100
- Configura Vault para secrets
- Baixa modelo LLM (llama.cpp) para detecção de anomalias
- Ao final:
http://localhost:8080
Verificar se está rodando:
curl http://localhost:8080/health
curl http://localhost:5432 # PostgreSQL
curl http://localhost:6379 # Valkey
Testar com Playwright:
# Navegar para bot de teste
npx playwright open http://localhost:3000/salesianos
# Ou diretamente
npx playwright open http://localhost:3000/detecta
Modo 2: Container (Incus) — Produção
Os serviços rodam em containers Incus separados.
# Subir todos os containers
sudo incus start system tables vault directory drive cache llm vector_db
# Verificar status
sudo incus list
# Acessar container system (onde roda botserver)
sudo incus exec system -- bash
# Ver logs do botserver
sudo incus exec system -- journalctl -u botserver -f
Arquitetura de Containers:
| Container | Services | Portas |
|---|---|---|
| system | BotServer, Valkey | 8080, 6379 |
| tables | PostgreSQL | 5432 |
| vault | Vault | 8200 |
| directory | Zitadel | 9000 |
| drive | MinIO | 9100 |
| cache | Valkey (backup) | 6379 |
| llm | llama.cpp | 8081 |
| vector_db | Qdrant | 6333 |
reset.sh (Ambiente Local)
./reset.sh # Limpa e reinicia tudo localmente
Service Commands
ps aux | grep -E "(botserver|botui)" | grep -v grep
curl http://localhost:8080/health
./restart.sh # Restart services
🎭 Playwright Browser Testing
Browser Setup
If browser fails: pkill -9 -f brave; pkill -9 -f chrome; pkill -9 -f chromium → wait 3s → navigate again.
Bot Testing Flow
- Navigate to
http://localhost:3000/<botname> - Snapshot → verify welcome message + suggestion buttons + Portuguese accents
- Click suggestion → wait 3-5s → snapshot → fill data → submit
- Verify DB records and backend logs
Desktop UI Note
Chat window may cover other apps — click middle button (restore) to minimize, or navigate directly via URL.
WhatsApp Testing
- Webhook is global — bot routing by typing bot name as first message
- Single WhatsApp number serves ALL bots; routing via
whatsapp-idinconfig.csv
➕ Adding New Features
Checklist
- Which module owns this? (Check Module Responsibility Matrix)
- Database migrations needed?
- New API endpoints?
- Security: input validation, auth, rate limiting, error sanitization?
- Screens in botui?
- No
unwrap()/expect()?
Pattern: types → schema → Diesel model → business logic → API endpoint → BASIC keyword (if applicable) → tests → docs in botbook/
Commit & Deploy
cd botserver && git push alm main && git push origin main
cd .. && git add botserver && git commit -m "Update botserver: <desc>" && git push alm main && git push origin main
🎨 Frontend Standards
- HTMX-first — server returns HTML fragments, not JSON
- Local assets only — NO CDN links
- Use
hx-get,hx-post,hx-target,hx-swap; WebSocket via htmx-ws
🚀 Performance & Quality
cargo clippy --workspacemust pass with 0 warningscargo tree --duplicates/cargo machete/cargo auditweekly- Release profile:
opt-level = "z",lto = true,codegen-units = 1,strip = true,panic = "abort" - Use
default-features = falseand opt-in to needed features
🧪 Testing
- Unit: per-crate
tests/or#[cfg(test)]modules —cargo test -p <crate> - Integration:
bottest/crate —cargo test -p bottest - Coverage: 80%+ on critical paths; ALL error paths and security guards tested
🚢 Deploy Workflow (CI/CD Only)
- Push to ALM (triggers CI automatically)
- CI builds on alm-ci → deploys to system container via SSH
- Service auto-restarts on binary update
- Verify: check service status + logs after ~10 min
Container Architecture
| Container | Service | Port |
|---|---|---|
| system | BotServer + Valkey | 8080/6379 |
| tables | PostgreSQL | 5432 |
| vault | Vault | 8200 |
🔑 Core Directives Summary
- OFFLINE FIRST — fix all errors from list before compiling
- BATCH BY FILE — fix ALL errors in a file at once, write once
- VERIFY LAST — only compile after ALL fixes applied
- DELETE DEAD CODE — never keep unused code
- GIT WORKFLOW — always push to ALL repositories
- 0 warnings, 0 errors — loop until clean
🚨 FUNDAMENTAL: Submodule Push Rule (MANDATORY)
Every time you push the main repo, you MUST also push ALL submodules!
# After ANY main repo push, ALWAYS run:
cd botserver && git push origin main && git push alm main
cd ../botui && git push origin main && git push alm main
cd ../botlib && git push origin main && git push alm main
# ... repeat for ALL submodules
Why: CI builds based on submodule commits. If submodule isn't pushed, CI deploys old code.
Checklist before pushing:
- botserver pushed?
- botui pushed?
- botlib pushed?
- All other submodules pushed?
- Main repo points to new submodule commits?
🔐 Zitadel Setup (Directory Service)
Container Architecture
- directory container: Zitadel running on port 8080 internally
- tables container: PostgreSQL database on port 5432
- Use database PROD-DIRECTORY for Zitadel data
Network Access (Container Mode)
- Internal API:
http://<directory-ip>:8080 - External port 9000 redirected via iptables NAT to directory:8080
- Health check:
curl -sf http://localhost:8080/debug/healthz
Zitadel Installation Steps
- Reset database (on tables container):
psql -h localhost -U postgres -d postgres -c "SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = 'PROD-DIRECTORY' AND pid <> pg_backend_pid();"
psql -h localhost -U postgres -d postgres -c "DROP DATABASE IF EXISTS \"PROD-DIRECTORY\";"
psql -h localhost -U postgres -d postgres -c "CREATE DATABASE \"PROD-DIRECTORY\";"
- Create init config (on directory container):
cat > /opt/gbo/conf/directory/zitadel-init-steps.yaml << "EOF"
FirstInstance:
InstanceName: "BotServer"
DefaultLanguage: "en"
PatPath: "/opt/gbo/conf/directory/admin-pat.txt"
Org:
Name: "BotServer"
Machine:
Machine:
Username: "admin-sa"
Name: "Admin Service Account"
Pat:
ExpirationDate: "2099-01-01T00:00:00Z"
Human:
UserName: "admin"
FirstName: "Admin"
LastName: "User"
Email:
Address: "admin@localhost"
Verified: true
Password: "Admin123!"
PasswordChangeRequired: false
EOF
- Start Zitadel (on directory container):
pkill -9 zitadel || true
nohup env \
ZITADEL_DATABASE_POSTGRES_HOST=<tables-ip> \
ZITADEL_DATABASE_POSTGRES_PORT=5432 \
ZITADEL_DATABASE_POSTGRES_DATABASE=PROD-DIRECTORY \
ZITADEL_DATABASE_POSTGRES_USER_USERNAME=postgres \
ZITADEL_DATABASE_POSTGRES_USER_PASSWORD=postgres \
ZITADEL_DATABASE_POSTGRES_USER_SSL_MODE=disable \
ZITADEL_DATABASE_POSTGRES_ADMIN_USERNAME=postgres \
ZITADEL_DATABASE_POSTGRES_ADMIN_PASSWORD=postgres \
ZITADEL_DATABASE_POSTGRES_ADMIN_SSL_MODE=disable \
ZITADEL_EXTERNALSECURE=false \
ZITADEL_EXTERNALDOMAIN=<directory-ip> \
ZITADEL_EXTERNALPORT=9000 \
ZITADEL_TLS_ENABLED=false \
/opt/gbo/bin/zitadel start-from-init \
--masterkey MasterkeyNeedsToHave32Characters \
--tlsMode disabled \
--externalDomain <directory-ip> \
--externalPort 9000 \
--steps /opt/gbo/conf/directory/zitadel-init-steps.yaml \
> /opt/gbo/logs/zitadel.log 2>&1 &
- Wait for bootstrap (~90 seconds), then verify:
curl -sf http://localhost:8080/debug/healthz
cat /opt/gbo/conf/directory/admin-pat.txt
- Configure iptables (on system container):
iptables -t nat -A PREROUTING -p tcp --dport 9000 -j DNAT --to-destination <directory-ip>:8080
iptables -t nat -A OUTPUT -p tcp -d <external-ip> --dport 9000 -j DNAT --to-destination <directory-ip>:8080
Zitadel API Usage
PAT file location: /opt/gbo/conf/directory/admin-pat.txt (on directory container)
Get IAM Info (internal)
curl -s -H "Authorization: Bearer $PAT" http://<directory-ip>:8080/management/v1/iam
Get IAM Info (external via port 9000)
curl -s -H "Authorization: Bearer $PAT" -H "Host: <directory-ip>" http://<external-ip>:9000/management/v1/iam
Create Human User
curl -s -X POST \
-H "Authorization: Bearer $PAT" \
-H "Host: <directory-ip>" \
-H "Content-Type: application/json" \
http://<external-ip>:9000/management/v1/users/human \
-d '{
"userName": "janedoe",
"name": "Jane Doe",
"profile": {"firstName": "Jane", "lastName": "Doe"},
"email": {"email": "jane@example.com"}
}'
Zitadel API Endpoints Reference
| Endpoint | Method | Description |
|---|---|---|
/management/v1/iam |
GET | Get IAM info |
/management/v1/orgs/me |
GET | Get current org |
/management/v1/users/human |
POST | Create human user |
/management/v1/users/machine |
POST | Create machine user |
/oauth/v2/token |
POST | Get access token |
/debug/healthz |
GET | Health check |
Important Notes
- Zitadel listens on port 8080 internally
- External port 9000 is forwarded via iptables NAT
- Use Host header with directory IP for external API calls
- PAT file:
/opt/gbo/conf/directory/admin-pat.txt - Admin credentials:
admin/Admin123!(human user) - Database:
PROD-DIRECTORYon tables container - Zitadel v4.13.1 is the current version