14 KiB
General Bots AI Agent Guidelines
NEVER INCLUDE HERE CREDENTIALS OR COMPANY INFORMATION, THIS IS COMPANY AGNOSTIC.
Use apenas a língua culta ao falar. Never save files to root — use /tmp for temp files. Never push to ALM without asking first (it is production). If a tool fails to install, check the official website for instructions. Local file support (/opt/gbo/data) has been removed; bots are loaded only from Drive (MinIO/S3).
Critical Production Rules
Always manage services via systemctl inside the system Incus container. Never run /opt/gbo/bin/botserver or /opt/gbo/bin/botui directly — they skip the .env file, which means Vault credentials fail to load and services break. The correct commands are sudo incus exec system -- systemctl start|stop|restart|status botserver and the same for ui. Systemctl handles env loading, auto-restart, and process lifecycle.
In development you may use cargo run or ./target/debug/botserver with botserver/.env. In production, always use systemctl start botserver with /opt/gbo/bin/.env.
Workspace Structure
The workspace has eight crates. botserver is the main API server (port 8080) using Axum, Diesel, and Rhai BASIC. botui is the web UI server and proxy (port 3000) using Axum, HTML/HTMX/CSS. botapp is a Tauri 2 desktop wrapper. botlib holds shared types and errors. botbook is mdBook documentation. bottest holds integration tests. botdevice handles IoT/device support. botplugin is a JS browser extension.
Key paths: binary at target/debug/botserver, always run from the botserver/ directory, env file at botserver/.env, UI files under botui/ui/suite/, bot data exclusively in Drive (MinIO/S3) under /{botname}.gbai/ buckets. Test at http://localhost:3000; login at http://localhost:3000/suite/auth/login.html.
Bot files in Drive follow this structure: {botname}.gbai/{botname}.gbdialog/ contains *.bas scripts, config.csv, and the .gbkb/ knowledge base folder. There is no local file monitoring — botserver compiles .bas to .ast in memory from Drive only.
Absolute Prohibitions
Never search the /target folder. Never build in release mode or use --release. Never run cargo build — use cargo check for verification. Never run cargo clean (causes 30-minute rebuilds); use ./reset.sh for DB issues. Never deploy manually via scp, SSH binary copy, or any method other than the CI/CD pipeline (push → ALM → alm-ci builds → deploys to system container). Never run the binary directly in production — use systemctl or ./restart.sh.
Never use panic!(), todo!(), unimplemented!(), unwrap(), or expect() in Rust code. 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 variables — 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. Never include sensitive data (IPs, tokens, keys) in docs or code; mask IPs in logs as 10.x.x.x. Never create files with secrets anywhere except /tmp/.
Build Pattern — Fix Fast Loop
When checking botserver, run cargo check -p botserver > /tmp/check.log 2>&1 &, capture the PID, then loop watching line count and kill the process once it exceeds 20 lines. After killing, check for errors with strings /tmp/check.log | grep "^error" | head -20. Fix errors immediately, then repeat. Never use --all-features (pulls docs/slides dependencies). This saves 10+ minutes per error cycle since full compilation takes 2–3 minutes. The key rule: kill at 20 lines, fix immediately, loop until clean.
If the process is killed by OOM, run pkill -9 cargo; pkill -9 rustc; pkill -9 botserver then retry with CARGO_BUILD_JOBS=1 cargo check -p botserver 2>&1 | tail -200.
Security Directives — Mandatory
For error handling, never use unwrap(), expect(), panic!(), or todo!(). Use value?, value.ok_or_else(|| Error::NotFound)?, value.unwrap_or_default(), or if let Some(v) = value { ... }.
For command execution, never use Command::new("cmd").arg(user_input).output(). Use SafeCommand::new("allowed_command")?.arg("safe_arg")?.execute() from crate::security::command_guard.
For error responses, never return Json(json!({ "error": e.to_string() })). Use log_and_sanitize(&e, "context", None) from crate::security::error_sanitizer and return (StatusCode::INTERNAL_SERVER_ERROR, sanitized).
For SQL, never use format!("SELECT * FROM {}", user_table). Use sanitize_identifier and validate_table_name from crate::security::sql_guard.
Rate limits: general 100 req/s, auth 10 req/s, API 50 req/s per token, WebSocket 10 msgs/s. Use the governor crate with per-IP and per-user tracking. All state-changing endpoints (POST/PUT/DELETE/PATCH) must require CSRF tokens via tower_csrf bound to the user session; Bearer Token endpoints are exempt. Every response must include these security headers: Content-Security-Policy, Strict-Transport-Security, X-Frame-Options: DENY, X-Content-Type-Options: nosniff, Referrer-Policy: strict-origin-when-cross-origin, and Permissions-Policy: geolocation=(), microphone=(), camera=().
For dependencies, app crates track Cargo.lock; lib crates do not. Critical deps use exact versions (=1.0.1); regular deps use caret (1.0). Run cargo audit weekly and update only via PR with testing.
Mandatory Code Patterns
Use Self not the type name in impl blocks. Always derive both PartialEq and Eq together. Use inline format args: format!("Hello {name}") not format!("Hello {}", name). Combine identical match arms: A | B => do_thing(). Maximum 450 lines per file — split proactively at 350 lines into types.rs, handlers.rs, operations.rs, utils.rs, and mod.rs, re-exporting all public items in mod.rs.
Error Fixing Workflow
Read the entire error list first. Group errors by file. For each file: view it, fix all errors, then write once. Only verify with cargo check after all fixes are applied — never compile after each individual fix. cargo clippy --workspace must pass with zero warnings.
Execution Modes
In local standalone mode (no incus), botserver manages all services itself. Run cargo run -- --install once to download and extract PostgreSQL, Valkey, MinIO, and Vault binaries into botserver-stack/bin/, initialize data directories, and download the LLM model. Then cargo run starts everything and serves at http://localhost:8080. Use ./reset.sh to wipe and restart the local environment.
In container (Incus) production mode, services run in separate named containers. Start them all with sudo incus start system tables vault directory drive cache llm vector_db. Access the system container with sudo incus exec system -- bash. View botserver logs with sudo incus exec system -- journalctl -u botserver -f. The container layout is: system runs BotServer on 8080; tables runs PostgreSQL on 5432; vault runs Vault on 8200; directory runs Zitadel on 8080 internally (external port 9000 via iptables NAT); drive runs MinIO on 9100; cache runs Valkey on 6379; llm runs llama.cpp on 8081; vector_db runs Qdrant on 6333.
Use the LOAD_ONLY variable in /opt/gbo/bin/.env to filter which bots are loaded and monitored by DriveMonitor, for example LOAD_ONLY=default,salesianos.
Debugging & Testing
To watch for errors live: tail -f botserver.log | grep -i "error\|tool". To debug a specific tool: grep Tool error in logs, fix the .bas file in MinIO at /{bot}.gbai/{bot}.gbdialog/{tool}.bas, then wait for DriveMonitor to recompile (automatic on file change, in-memory only, no local .ast cache). Test in browser at http://localhost:3000/{botname}.
Common BASIC errors: === is not a valid operator means you used JavaScript-style === — replace with == or use -- for string separators. Syntax error means bad BASIC syntax — check parentheses and commas. Tool execution failed means a runtime error — check logs for stack trace.
For Playwright testing, navigate to http://localhost:3000/<botname>, snapshot to verify welcome message and suggestion buttons including Portuguese accents, click a suggestion, wait 3–5 seconds, snapshot, fill data, submit, then verify DB records and backend logs. If the browser hangs, run pkill -9 -f brave; pkill -9 -f chrome; pkill -9 -f chromium, wait 3 seconds, and navigate again. The chat window may overlap other apps — click the middle (restore) button to minimize it or navigate directly via URL.
WhatsApp routing is global — one number serves all bots, with routing determined by the whatsapp-id key in each bot's config.csv. The bot name is sent as the first message to route correctly.
Bot Scripts Architecture
start.bas is the entry point executed on WebSocket connect and on the first user message (once per session). It loads suggestion buttons via ADD_SUGGESTION_TOOL and marks the session in Redis to prevent re-runs. {tool}.bas files implement individual tools (e.g. detecta.bas). tables.bas is a special file — never call it with CALL; it is parsed automatically at compile time by process_table_definitions() and its table definitions are synced to the database via sync_bot_tables(). init_folha.bas handles initialization for specific features.
The CALL keyword can invoke in-memory procedures or .bas scripts by name. If the target is not in memory, botserver looks for {name}.bas in the bot's gbdialog folder in Drive. The DETECT keyword analyzes a database table for anomalies: it requires the table to exist (defined in tables.bas) and calls the BotModels API at /api/anomaly/detect.
Tool buttons use MessageType::TOOL_EXEC (id 6). When the frontend sends message_type: 6 via WebSocket, the backend executes the named tool directly in stream_response(), bypassing KB injection and LLM entirely. The result appears in chat without any "/tool" prefix text. Other message types are: 0 EXTERNAL, 1 USER, 2 BOT_RESPONSE, 3 CONTINUE, 4 SUGGESTION, 5 CONTEXT_CHANGE.
Submodule Push Rule — Mandatory
Every time you push the main repo, you must also push all submodules. CI builds based on submodule commits — if a submodule is not pushed, CI deploys old code. Always push botserver, botui, and botlib to both origin and alm remotes before or alongside the main repo push.
The deploy workflow is: push to ALM → CI triggers on alm-ci → builds inside system container via SSH (to match glibc 2.36 on Debian 12 Bookworm, not the CI runner's glibc 2.41) → deploys binary → service auto-restarts. Verify by checking service status and logs about 10 minutes after pushing.
Zitadel Setup (Directory Service)
Zitadel runs in the directory container on port 8080 internally. External port 9000 is forwarded to it via iptables NAT on the system container. The database is PROD-DIRECTORY on the tables container. The PAT file is at /opt/gbo/conf/directory/admin-pat.txt on the directory container. Admin credentials are username admin, password Admin123!. Current version is Zitadel v4.13.1. Known bug: Web console UI will return 404 for environment.json when accessed via reverse proxy public domain. Use http://:9000/ui/console for administrative interface instead.
To reinstall: drop and recreate PROD-DIRECTORY on the tables container, write the init YAML to /opt/gbo/conf/directory/zitadel-init-steps.yaml (defining org name, admin user, and PAT expiry), then start Zitadel with env vars for the PostgreSQL host/port/database/credentials, ZITADEL_EXTERNALSECURE=false, ZITADEL_EXTERNALDOMAIN=<directory-ip>, ZITADEL_EXTERNALPORT=9000, and ZITADEL_TLS_ENABLED=false. Pass --masterkey MasterkeyNeedsToHave32Characters, --tlsMode disabled, and --steps <yaml-path>. Bootstrap takes about 90 seconds; verify with curl -sf http://localhost:8080/debug/healthz.
Key API endpoints: Use v2 API endpoints for all operations: POST /v2/organizations/{org_id}/domains to add domains, POST /v2/users/new to create users, POST /oauth/v2/token for access tokens, GET /debug/healthz for health. When calling externally via port 9000, include Host: <directory-ip> header. The v1 Management API is deprecated and not functional in this version.
Frontend Standards & Performance
HTMX-first: the server returns HTML fragments, not JSON. Use hx-get, hx-post, hx-target, hx-swap, and WebSocket via htmx-ws. All assets must be local — no CDN links.
Release profile must use opt-level = "z", lto = true, codegen-units = 1, strip = true, and panic = "abort". Use default-features = false and opt into only needed features. Run cargo tree --duplicates, cargo machete, and cargo audit weekly.
Testing: unit tests live in per-crate tests/ folders or #[cfg(test)] modules, run with cargo test -p <crate>. Integration tests live in bottest/, run with cargo test -p bottest. Aim for 80%+ coverage on critical paths; all error paths and security guards must be tested.
Core Directives Summary
Fix offline first — read all errors before compiling again. Batch by file — fix all errors in a file at once and write once. Verify last — only run cargo check after all fixes are applied. Delete dead code — never keep unused code. Git workflow — always push to all repositories (origin and alm). Target zero warnings and zero errors — loop until clean.