gb/AGENTS.md

18 KiB
Raw Blame History

General Bots AI Agent Guidelines

  • Use apenas a língua culta ao falar.
  • Never save files on root! Use /tmp for 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.rs to 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.ast automaticamente
  • Se cache, deletar .ast para forçar recompilação

🧪 Debugging & Testing Tools

🔍 Ver Erros de Execução

tail -f botserver.log | grep -i "error\|tool"

🧪 Testar Ferramenta Específica

  1. Identificar o erro no log:

    grep -A5 "Tool error" botserver.log
    
  2. Corrigir o arquivo .bas na fonte:

    • Dev local: ./botserver-stack/data/system/work/{bot}.gbai/{bot}.gbdialog/{tool}.bas
    • Production: /opt/gbo/data/{bot}.gbai/{bot}.gbdialog/{tool}.bas
  3. 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
  4. 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

  1. Start with Component Dependency Graph
  2. Review Module Responsibility Matrix
  3. Study Data Flow Patterns
  4. Reference Common Architectural Patterns
  5. Check Security Rules — violations are blocking
  6. Follow Code Patterns — consistency is mandatory

Absolute Prohibitions

Build & Deploy

  • NEVER search /target folder
  • NEVER build in release mode or use --release
  • NEVER run cargo build — use cargo check for verification
  • NEVER run cargo clean — causes 30min rebuilds; use ./reset.sh for 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 systemctl or ./restart.sh

Code Quality

  • NEVER use panic!(), todo!(), unimplemented!(), unwrap(), expect()
  • NEVER use Command::new() directly — use SafeCommand
  • NEVER return raw error strings to HTTP clients — use ErrorSanitizer
  • NEVER use #[allow()] or lint exceptions in Cargo.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 .md docs without checking botbook/ 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 governor crate 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 audit weekly; 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

  1. Read ENTIRE error list first
  2. Group errors by file
  3. For each file: view → fix ALL errors → write once
  4. 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:

  • PackageManager baixa e extrai binários para botserver-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

  1. Navigate to http://localhost:3000/<botname>
  2. Snapshot → verify welcome message + suggestion buttons + Portuguese accents
  3. Click suggestion → wait 3-5s → snapshot → fill data → submit
  4. 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-id in config.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 --workspace must pass with 0 warnings
  • cargo tree --duplicates / cargo machete / cargo audit weekly
  • Release profile: opt-level = "z", lto = true, codegen-units = 1, strip = true, panic = "abort"
  • Use default-features = false and 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)

  1. Push to ALM (triggers CI automatically)
  2. CI builds on alm-ci → deploys to system container via SSH
  3. Service auto-restarts on binary update
  4. 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

  1. 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\";"
  1. 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
  1. 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 &
  1. Wait for bootstrap (~90 seconds), then verify:
curl -sf http://localhost:8080/debug/healthz
cat /opt/gbo/conf/directory/admin-pat.txt
  1. 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-DIRECTORY on tables container
  • Zitadel v4.13.1 is the current version