generalbots/AGENTS.md
Rodrigo Rodriguez (Pragmatismo) 037db5c381 feat: Major workspace reorganization and documentation update
- Add comprehensive documentation in botbook/ with 12 chapters
- Add botapp/ Tauri desktop application
- Add botdevice/ IoT device support
- Add botlib/ shared library crate
- Add botmodels/ Python ML models service
- Add botplugin/ browser extension
- Add botserver/ reorganized server code
- Add bottemplates/ bot templates
- Add bottest/ integration tests
- Add botui/ web UI server
- Add CI/CD workflows in .forgejo/workflows/
- Add AGENTS.md and PROD.md documentation
- Add dependency management scripts (DEPENDENCIES.sh/ps1)
- Remove legacy src/ structure and migrations
- Clean up temporary and backup files
2026-04-19 08:14:25 -03:00

57 KiB
Raw Blame History

General Bots AI Agent Guidelines

  • stop saving .png on root! Use /tmp. never allow new files on root.
  • never push to alm without asking first - pbecause it is production!
  • NEVER deploy to production manually — ALWAYS use CI/CD pipeline
  • NEVER include sensitive data (IPs, tokens, passwords, keys) in AGENTS.md or any documentation
  • NEVER use scp, direct SSH binary copy, or manual deployment to system container
  • ALWAYS push to ALM → CI builds on alm-ci → CI deploys to system container automatically 8080 is server 3000 is client ui if you are in trouble with some tool, please go to the ofiical website to get proper install or instructions To test web is http://localhost:3000 (botui!) Use apenas a lingua culta ao falar . test login here http://localhost:3000/suite/auth/login.html

⚠️ CRITICAL SECURITY WARNING I AM IN DEV ENV, but sometimes, pasting from PROD, do not treat my env as prod! Just fix, to me and push to CI. So I can test in PROD, for a while. Use Playwrigth MCP to start localhost:3000/ now. NEVER CREATE FILES WITH SECRETS IN THE REPOSITORY ROOT

  • NEVER write internal IPs to logs or output
  • When debugging network issues, mask IPs (e.g., "10.x.x.x" instead of "10.16.164.222")
  • Use hostnames instead of IPs in configs and documentation See botserver/src/drive/local_file_monitor.rs to see how to load from /opt/gbo/data the list of development bots.
  • NEVER use cargo clean - causes 30min rebuilds, use ./reset.sh for database issues

Secret files MUST be placed in /tmp/ only:

  • /tmp/vault-token-gb - Vault root token
  • /tmp/vault-unseal-key-gb - Vault unseal key
  • vault-unseal-keys - FORBIDDEN (tracked by git)
  • start-and-unseal.sh - FORBIDDEN (contains secrets)

Why /tmp/?

  • Cleared on reboot (ephemeral)
  • Not tracked by git
  • Standard Unix security practice
  • Prevents accidental commits

📁 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/

🏗️ System Architecture Overview

Chat Flow Architecture

User Message (WebSocket)
│
▼
┌─────────────────────────────────┐
│  1. WebSocket Connection        │  botserver/src/websocket.rs
│     - Session established       │  UserSession created
│     - Redis connection          │  session_id generated
└──────────────┬──────────────────┘
│
▼
┌─────────────────────────────────┐
│  2. start.bas Execution         │  /opt/gbo/data/{bot}.gbai/...
│     - Runs ONCE per session     │  {bot}.gbdialog/start.bas
│     - ADD_SUGGESTION calls      │  Adds button suggestions
│     - Sets Redis flag           │  prevents re-run
└──────────────┬──────────────────┘
│
▼
┌─────────────────────────────────┐
│  3. Message Processing          │  stream_response()
│     - IF message_type == 6      │  TOOL_EXEC (bypass LLM)
│     - ELSE: KB injection        │  USE_KB context
│     - LLM processing            │  generate_response()
└──────────────┬──────────────────┘
│
▼
┌─────────────────────────────────┐
│  4. Tool Execution              │  TOOL_EXEC (type 6)
│     - Direct .ast execution     │  No LLM, no KB
│     - Rhai engine               │  ScriptService::run()
│     - Immediate response        │  Result in chat
└──────────────┬──────────────────┘
│
▼
┌─────────────────────────────────┐
│  5. LLM Response (if not tool) │  Groq/OpenAI/etc
│     - Prompt with context       │  System + KB + History
│     - Streaming response        │  WebSocket chunks
│     - Tool suggestions          │  LLM suggests tools
└──────────────┬──────────────────┘
│
▼
┌─────────────────────────────────┐
│  6. Frontend Display            │  botui HTMX/WebSocket
│     - Message appended          │  #chat-messages
│     - Suggestion buttons        │  From Redis suggestions:{bot}:{session}
│     - Tool buttons active       │  MessageType 6 triggers
└─────────────────────────────────┘

Message Types Reference

ID Name Purpose LLM Used?
0 EXTERNAL External message No
1 USER User message Yes
2 BOT_RESPONSE Bot response No
3 CONTINUE Continue processing No
4 SUGGESTION Suggestion button Yes
5 CONTEXT_CHANGE Context change No
6 TOOL_EXEC Direct tool execution No - Bypasses LLM

TOOL_EXEC (Type 6): When frontend sends message_type: 6, backend executes the tool .ast file directly via Rhai engine. NO KB injection, NO LLM call. Result appears immediately in chat.


📝 Bot Scripts Architecture

start.bas - Session Entry Point

Execution:

  • Runs on WebSocket connect
  • Runs again on first user message (blocking, once per session)
  • Sets Redis key: session:{session_id}:initialized
  • Subsequent messages skip start.bas

Purpose:

  • Load suggestion buttons via ADD_SUGGESTION "text"
  • Initialize bot memory
  • Set up context

Example:

' start.bas
ADD SUGGESTION "Check inventory"
ADD SUGGESTION "Create report"
ADD SUGGESTION "Send email"

TALK "Hello! I'm your assistant. How can I help?"

tables.bas - Database Schema

SPECIAL FILE - DO NOT CALL WITH CALL

  • Parsed automatically at compile time
  • Defines tables for sync_bot_tables()
  • Creates/updates database schema

Example:

' tables.bas
BEGIN TABLE customers
    id UUID PRIMARY KEY
    name VARCHAR(255)
    email VARCHAR(255)
    created_at TIMESTAMP
END TABLE

BEGIN TABLE orders
    id UUID PRIMARY KEY
    customer_id UUID REFERENCES customers
    total DECIMAL(10,2)
    status VARCHAR(50)
END TABLE

{tool}.bas - Tool Scripts

Location: /opt/gbo/data/{bot}.gbai/{bot}.gbdialog/{tool}.bas Compiled to: {tool}.ast (in memory or /opt/gbo/work/) Execution: Via CALL "tool" or TOOL_EXEC (type 6)

Example:

' detecta.bas - Inventory checker

items = GET FROM inventory WHERE quantity < 10

IF COUNT(items) = 0 THEN
    TALK "All items well stocked!"
ELSE
    response = "Low stock items:\n"
    FOR EACH item IN items
        response = response + "- " + item.name + ": " + item.quantity + "\n"
    NEXT
    TALK response
END IF

CALL Keyword

' Call in-memory procedure or .bas script
CALL "script_name"
CALL "procedure_name"

' If not in memory, looks for {name}.bas in bot's gbdialog folder

DETECT Keyword

' Analyze table for anomalies
' Requires table defined in tables.bas
result = DETECT "folha_salarios"

' Calls BotModels API at /api/anomaly/detect

💬 Common BASIC Keywords Reference

TALK - Bot Response

TALK "Hello, user!"
TALK "Result: " + result

' Multi-line with \n
TALK "Line 1\nLine 2\nLine 3"

HEAR - Listen for Input

HEAR "What's your name?" AS name
HEAR "Enter amount:" AS amount

' Used in voice/chat triggered tools
HEAR "check inventory" AS request

USE KB - Knowledge Base Context

' Inject KB content into LLM context
USE KB "manual"
USE KB "faq"
USE KB "cartas"

' Clear KB context
CLEAR KB

' Multiple KBs
USE KB "kb1"
USE KB "kb2"

Flow:

USE KB "manual"
↓
Bot searches .gbkb/ folder for documents
↓
Chunks text, creates embeddings
↓
Queries Qdrant for relevant chunks
↓
Injects into LLM prompt as context
↓
User question answered with KB context

USE WEBSITE - Web Scraping Context

' Scrape website and inject into context
USE WEBSITE "https://example.com/docs"
USE WEBSITE "https://api.example.com/swagger"

' Combined with USE KB
USE KB "manual"
USE WEBSITE "https://company.com/updates"
TALK "How can I help with our product?"

ADD SUGGESTION - Suggestion Buttons

' In start.bas - shown as quick reply buttons
ADD SUGGESTION "Check status"
ADD SUGGESTION "Create ticket"
ADD SUGGESTION "Contact support"

' Deduplicated with Redis SADD
' Key: suggestions:{bot_id}:{session_id}
' Read with SMEMBERS

Database Operations

' GET - Query records
customers = GET FROM customers WHERE status = "active"
order = GET FROM orders WHERE id = "123"

' SAVE - Insert/update
SAVE customer TO customers
SAVE order TO orders

' FIND - Search
result = FIND "term" IN products

' Array functions
first = FIRST(customers)
last = LAST(customers)
count = COUNT(customers)

File Operations

' Create file in .gbdrive/
CREATE FILE "reports/sales.txt" WITH report_content

' Read file
content = READ FILE "data/config.txt"

' Write file
WRITE FILE "logs/activity.log" WITH log_entry

' Upload to MinIO
UPLOAD data TO "exports/data.json"

HTTP Operations

' GET request
response = GET HTTP "https://api.example.com/data"

' POST request
result = POST HTTP "https://api.example.com/webhook" WITH json_data

' Webhook
WEBHOOK "https://callback.example.com" WITH payload

Task & Scheduling

' Create task
CREATE_TASK "Review report", "john", "2024-01-15", project_id

' Wait
WAIT 5  ' seconds

' Event handlers
ON EMAIL FROM "@company.com" DO CALL "process_email"
ON CHANGE customers DO CALL "notify_admin"

Memory & Context

' Bot-level memory (persists across sessions)
SET BOT MEMORY "company_name" = "Acme Corp"
name = GET BOT MEMORY "company_name"

' Session-level memory
REMEMBER "user_preference" = "dark_mode"
pref = RECALL "user_preference"

' Context variables
SET CONTEXT "current_order" = order_id

Multi-Bot Operations

' Add sub-bot
ADD BOT "sales" WITH TRIGGER "talk to sales"

' Delegate
DELEGATE TO "sales"

' Send message to another bot
SEND TO BOT "sales" MESSAGE "New lead available"

' Broadcast
BROADCAST MESSAGE "System maintenance in 5 minutes"

Control Flow

' IF/THEN/ELSE
IF condition THEN
    ' true branch
ELSE
    ' false branch
END IF

' FOR EACH loop
FOR EACH customer IN customers
    SEND MAIL TO customer.email WITH subj, body
    WAIT 1
NEXT

' SWITCH/CASE
SWITCH status
    CASE "active"
        TALK "Account active"
    CASE "inactive"
        TALK "Account inactive"
    DEFAULT
        TALK "Unknown status"
END SWITCH

Built-in Variables

Variable Description Example
TODAY Current date IF created_at == TODAY THEN
NOW Current datetime SET last_seen = NOW
USER Current user object USER.email, USER.id
SESSION Current session object SESSION.id
BOT Current bot object BOT.name, BOT.id

🧭 LLM Navigation Guide

Reading This Workspace

/opt/gbo/data is a place also for bots. For LLMs analyzing this codebase: 0. Bots are in drive, each bucket is a bot. Respect LOAD_ONLY.

  1. Start with Component Dependency Graph in README to understand relationships
  2. Review Module Responsibility Matrix for what each module does
  3. Study Data Flow Patterns to understand execution flow
  4. Reference Common Architectural Patterns before making changes
  5. Check Security Rules below - violations are blocking issues
  6. Follow Code Patterns below - consistency is mandatory

🔄 Reset Process Notes

reset.sh Behavior

  • Purpose: Cleans and restarts the development environment
  • Timeouts: The script can timeout during "Step 3/4: Waiting for BotServer to bootstrap"
  • Bootstrap Process: Takes 3-5 minutes to install all components (Vault, PostgreSQL, Valkey, MinIO, Zitadel, LLM)

Common Issues

  1. Script Timeout: reset.sh waits for "Bootstrap complete: admin user" message

    • If Zitadel isn't ready within 60s, admin user creation fails
    • Script continues waiting indefinitely
    • Solution: Check botserver.log for "Bootstrap process completed!" message
  2. Zitadel Not Ready: "Bootstrap check failed (Zitadel may not be ready)"

    • Directory service may need more than 60 seconds to start
    • Admin user creation deferred
    • Services still start successfully
  3. Services Exit After Start:

    • botserver/botui may exit after initial startup
    • Check logs for "dispatch failure" errors
    • Check Vault certificate errors: "tls: failed to verify certificate: x509"

Manual Service Management

# If reset.sh times out, manually verify services:
ps aux | grep -E "(botserver|botui)" | grep -v grep
curl http://localhost:8080/health
tail -f botserver.log botui.log

# Restart services manually:
./restart.sh

Reset Verification

After reset completes, verify:

  • PostgreSQL running (port 5432)
  • Valkey cache running (port 6379)
  • BotServer listening on port 8080
  • BotUI listening on port 3000
  • No errors in botserver.log

🔐 Security Directives - MANDATORY

1. Error Handling - NO PANICS IN PRODUCTION

// ❌ FORBIDDEN
value.unwrap()
value.expect("message")
panic!("error")
todo!()
unimplemented!()

// ✅ REQUIRED
value?
value.ok_or_else(|| Error::NotFound)?
value.unwrap_or_default()
value.unwrap_or_else(|e| { log::error!("{}", e); default })
if let Some(v) = value { ... }
match value { Ok(v) => v, Err(e) => return Err(e.into()) }

2. Command Execution - USE SafeCommand

// ❌ FORBIDDEN
Command::new("some_command").arg(user_input).output()

// ✅ REQUIRED
use crate::security::command_guard::SafeCommand;
SafeCommand::new("allowed_command")?
    .arg("safe_arg")?
    .execute()

3. Error Responses - USE ErrorSanitizer

// ❌ FORBIDDEN
Json(json!({ "error": e.to_string() }))
format!("Database error: {}", e)

// ✅ REQUIRED
use crate::security::error_sanitizer::log_and_sanitize;
let sanitized = log_and_sanitize(&e, "context", None);
(StatusCode::INTERNAL_SERVER_ERROR, sanitized)

4. SQL - USE 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 Strategy (IMP-07)

  • Default Limits:
    • General: 100 req/s (global)
    • Auth: 10 req/s (login endpoints)
    • API: 50 req/s (per token)
  • Implementation:
    • MUST use governor crate
    • MUST implement per-IP and per-User tracking
    • WebSocket connections MUST have message rate limits (e.g., 10 msgs/s)

6. CSRF Protection (IMP-08)

  • Requirement: ALL state-changing endpoints (POST, PUT, DELETE, PATCH) MUST require a CSRF token.
  • Implementation:
    • Use tower_csrf or similar middleware
    • Token MUST be bound to user session
    • Double-Submit Cookie pattern or Header-based token verification
    • Exemptions: API endpoints using Bearer Token authentication (stateless)

7. Security Headers (IMP-09)

  • Mandatory Headers on ALL Responses:
    • Content-Security-Policy: "default-src 'self'; script-src 'self'; object-src 'none';"
    • Strict-Transport-Security: "max-age=63072000; includeSubDomains; preload"
    • X-Frame-Options: "DENY" or "SAMEORIGIN"
    • X-Content-Type-Options: "nosniff"
    • Referrer-Policy: "strict-origin-when-cross-origin"
    • Permissions-Policy: "geolocation=(), microphone=(), camera=()"

8. Dependency Management (IMP-10)

  • Pinning:
    • Application crates (botserver, botui) MUST track Cargo.lock
    • Library crates (botlib) MUST NOT track Cargo.lock
  • Versions:
    • Critical dependencies (crypto, security) MUST use exact versions (e.g., =1.0.1)
    • Regular dependencies MAY use caret (e.g., 1.0)
  • Auditing:
    • Run cargo audit weekly
    • Update dependencies only via PR with testing

Mandatory Code Patterns

Use Self in Impl Blocks

impl MyStruct {
    fn new() -> Self { Self { } }  // ✅ Not MyStruct
}

Derive Eq with PartialEq

#[derive(PartialEq, Eq)]  // ✅ Always both
struct MyStruct { }

Inline Format Args

format!("Hello {name}")  // ✅ Not format!("{}", name)

Combine Match Arms

match x {
    A | B => do_thing(),  // ✅ Combine identical arms
    C => other(),
}

Absolute Prohibitions

  • NEVER search /target folder! It is binary compiled.
  • NEVER hardcode passwords, tokens, API keys, or any credentials in source code — ALWAYS use generate_random_string() or environment variables
  • NEVER build in release mode - ONLY debug builds allowed
  • NEVER use --release flag on ANY cargo command
  • NEVER run cargo build - use cargo check for syntax verification
  • NEVER compile directly for production - ALWAYS use push + CI/CD pipeline
  • NEVER use scp or manual transfer to deploy - ONLY CI/CD ensures correct deployment
  • NEVER manually copy binaries to production system container - ALWAYS push to ALM and let CI/CD build and deploy
  • NEVER SSH into system container to deploy binaries - CI workflow handles build, transfer, and restart via alm-ci SSH
  • ALWAYS push code to ALM → CI builds on alm-ci → CI deploys to system container via SSH from alm-ci
  • CI deploy path: alm-ci builds at /opt/gbo/data/botserver/target/debug/botserver → tar+gzip via SSH → /opt/gbo/bin/botserver on system container → restart
  • NEVER manually copy binaries to production system container - ALWAYS push to ALM and let CI/CD build and deploy
  • NEVER SSH into system container to deploy binaries - CI workflow handles build, transfer, and restart via alm-ci SSH
  • ALWAYS push code to ALM → CI builds on alm-ci → CI deploys to system container via SSH from alm-ci
  • CI deploy path: alm-ci builds at /opt/gbo/data/botserver/target/debug/botserver → tar+gzip via SSH → /opt/gbo/bin/botserver on system container → restart

Current Status: 0 clippy warnings (down from 61 - PERFECT SCORE in YOLO mode)

  • NEVER use panic!(), todo!(), unimplemented!()
  • NEVER use Command::new() directly - use SafeCommand
  • NEVER return raw error strings to HTTP clients
  • NEVER use #[allow()] in source code - FIX the code instead
  • NEVER add lint exceptions to Cargo.toml - FIX the code instead
  • NEVER use _ prefix for unused variables - DELETE or USE them
  • NEVER leave unused imports or dead code
  • NEVER use CDN links - all assets must be local
  • NEVER create .md documentation files without checking botbook/ first
  • NEVER comment out code - FIX it or DELETE it entirely

📏 File Size Limits - MANDATORY

Maximum 450 Lines Per File

When a file grows beyond this limit:

  1. Identify logical groups - Find related functions
  2. Create subdirectory module - e.g., handlers/
  3. Split by responsibility:
    • types.rs - Structs, enums, type definitions
    • handlers.rs - HTTP handlers and routes
    • operations.rs - Core business logic
    • utils.rs - Helper functions
    • mod.rs - Re-exports and configuration
  4. Keep files focused - Single responsibility
  5. Update mod.rs - Re-export all public items

NEVER let a single file exceed 450 lines - split proactively at 350 lines


🔥 Error Fixing Workflow

Mode 1: OFFLINE Batch Fix (PREFERRED)

When given error output:

  1. Read ENTIRE error list first
  2. Group errors by file
  3. For EACH file with errors: a. View file → understand context b. Fix ALL errors in that file c. Write once with all fixes
  4. Move to next file
  5. REPEAT until ALL errors addressed
  6. ONLY THEN → verify with build/diagnostics

NEVER run cargo build/check/clippy DURING fixing Fix ALL errors OFFLINE first, verify ONCE at the end

Mode 2: Interactive Loop

LOOP UNTIL (0 warnings AND 0 errors):
  1. Run diagnostics → pick file with issues
  2. Read entire file
  3. Fix ALL issues in that file
  4. Write file once with all fixes
  5. Verify with diagnostics
  6. CONTINUE LOOP
END LOOP

Streaming Build Rule

Do NOT wait for cargo to finish. As soon as the first errors appear in output, cancel/interrupt the build, fix those errors immediately, then re-run. This avoids wasting time on a full compile when errors are already visible.


🧠 Memory Management

When compilation fails due to 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

🎭 Playwright Browser Testing - YOLO Mode

When user requests to start YOLO mode with Playwright:

  1. Start the browser - Use mcp__playwright__browser_navigate to open http://localhost:3000/{botname}
  2. Take snapshot - Use mcp__playwright__browser_snapshot to see current page state
  3. Test user flows - Use click, type, fill_form, etc.
  4. Verify results - Check for expected content, errors in console, network requests
  5. Validate backend - Check database and services to confirm process completion
  6. Report findings - Always include screenshot evidence with browser_take_screenshot

⚠️ IMPORTANT - Desktop UI Navigation:

  • The desktop may have a maximized chat window covering other apps
  • To access CRM/sidebar icons, click the middle button (restore/down arrow) in the chat window header to minimize it
  • Or navigate directly via URL: http://localhost:3000/suite/crm (after login)

Bot-Specific Testing URL Pattern: http://localhost:3000/<botname>

Backend Validation Checks: After UI interactions, validate backend state via psql or tail logs.


Adding New Features Workflow

Step 1: Plan the Feature

Understand requirements:

  1. What problem does this solve?
  2. Which module owns this functionality? (Check Module Responsibility Matrix)
  3. What data structures are needed?
  4. What are the security implications?

Design checklist:

  • Does it fit existing architecture patterns?
  • Will it require database migrations?
  • Does it need new API endpoints?
  • Will it affect existing features?
  • What are the error cases?

Step 2: Implement the Feature

Follow the pattern:

// 1. Add types to botlib if shared across crates
// botlib/src/models.rs
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewFeature {
    pub id: Uuid,
    pub name: String,
}

// 2. Add database schema if needed
// botserver/migrations/YYYY-MM-DD-HHMMSS_feature_name/up.sql
CREATE TABLE new_features (
    id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    name VARCHAR(255) NOT NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

// 3. Add Diesel model
// botserver/src/core/shared/models/core.rs
#[derive(Queryable, Insertable)]
#[diesel(table_name = new_features)]
pub struct NewFeatureDb {
    pub id: Uuid,
    pub name: String,
    pub created_at: DateTime<Utc>,
}

// 4. Add business logic
// botserver/src/features/new_feature.rs
pub async fn create_feature(
    state: &AppState,
    name: String,
) -> Result<NewFeature, Error> {
    // Implementation
}

// 5. Add API endpoint
// botserver/src/api/routes.rs
async fn create_feature_handler(
    Extension(state): Extension<Arc<AppState>>,
    Json(payload): Json<CreateFeatureRequest>,
) -> Result<Json<NewFeature>, (StatusCode, String)> {
    // Handler implementation
}

Security checklist:

  • Input validation (use sanitize_identifier for SQL)
  • Authentication required?
  • Authorization checks?
  • Rate limiting needed?
  • Error messages sanitized? (use log_and_sanitize)
  • No unwrap() or expect() in production code

Step 3: Add BASIC Keywords (if applicable)

For features accessible from .bas scripts:

// botserver/src/basic/keywords/new_feature.rs
pub fn new_feature_keyword(
    state: Arc<AppState>,
    user_session: UserSession,
    engine: &mut Engine,
) {
    let state_clone = state.clone();
    let session_clone = user_session.clone();

    engine
        .register_custom_syntax(
            ["NEW_FEATURE", "$expr$"],
            true,
            move |context, inputs| {
                let param = context.eval_expression_tree(&inputs[0])?.to_string();
                
                // Call async function from sync context using separate thread
                let (tx, rx) = std::sync::mpsc::channel();
                std::thread::spawn(move || {
                    let rt = tokio::runtime::Builder::new_current_thread()
                        .enable_all().build().ok();
                    let result = if let Some(rt) = rt {
                        rt.block_on(async {
                            create_feature(&state_clone, param).await
                        })
                    } else {
                        Err("Failed to create runtime".into())
                    };
                    let _ = tx.send(result);
                });
                let result = rx.recv().unwrap_or(Err("Channel error".into()));
                
                match result {
                    Ok(feature) => Ok(Dynamic::from(feature.name)),
                    Err(e) => Err(format!("Failed: {}", e).into()),
                }
            },
        )
        .expect("valid syntax registration");
}

Step 4: Test the Feature

Local testing:

# 1. Run migrations
diesel migration run

# 2. Build and restart
./restart.sh

# 3. Test via API
curl -X POST http://localhost:9000/api/features \
  -H "Content-Type: application/json" \
  -d '{"name": "test"}'

# 4. Test via BASIC script
# Create test.bas in /opt/gbo/data/testbot.gbai/testbot.gbdialog/
# NEW_FEATURE "test"

# 5. Check logs
tail -f botserver.log | grep -i "new_feature"

Integration test:

// bottest/tests/new_feature_test.rs
#[tokio::test]
async fn test_create_feature() {
    let state = setup_test_state().await;
    let result = create_feature(&state, "test".to_string()).await;
    assert!(result.is_ok());
}

Step 5: Document the Feature

Update documentation:

  • Add to botbook/src/features/ if user-facing
  • Add to module README.md if developer-facing
  • Add inline code comments for complex logic
  • Update API documentation

Example documentation:

## NEW_FEATURE Keyword

Creates a new feature with the given name.

**Syntax:**
```basic
NEW_FEATURE "feature_name"

Example:

NEW_FEATURE "My Feature"
TALK "Feature created!"

Returns: Feature name as string


### Step 6: Commit & Deploy

**Commit pattern:**
```bash
git add .
git commit -m "feat: Add NEW_FEATURE keyword

- Adds new_features table with migrations
- Implements create_feature business logic
- Adds NEW_FEATURE BASIC keyword
- Includes API endpoint at POST /api/features
- Tests: Unit tests for business logic, integration test for API"

git push alm main
git push origin main

🧪 Testing Strategy

Unit Tests

  • Location: Each crate has tests/ directory or inline #[cfg(test)] modules
  • Naming: Test functions use test_ prefix or describe what they test
  • Running: cargo test -p <crate_name> or cargo test for all

Integration Tests

  • Location: bottest/ crate contains integration tests
  • Scope: Tests full workflows across multiple crates
  • Running: cargo test -p bottest

Coverage Goals

  • Critical paths: 80%+ coverage required
  • Error handling: ALL error paths must have tests
  • Security: All security guards must have tests

WhatsApp Integration Testing

Prerequisites

  1. Enable WhatsApp Feature: Build botserver with whatsapp feature enabled:
    cargo build -p botserver --bin botserver --features whatsapp
    
  2. Bot Configuration: Ensure the bot has WhatsApp credentials configured in config.csv:
    • whatsapp-api-key - API key from Meta Business Suite
    • whatsapp-verify-token - Custom token for webhook verification
    • whatsapp-phone-number-id - Phone Number ID from Meta
    • whatsapp-business-account-id - Business Account ID from Meta

Using Localtunnel (lt) as Reverse Proxy

Check database for message storage

psql -h localhost -U postgres -d botserver -c "SELECT * FROM messages WHERE bot_id = '<bot_id>' ORDER BY created_at DESC LIMIT 5;"

🐛 Debugging Rules

🚨 CRITICAL ERROR HANDLING RULE

STOP EVERYTHING WHEN ERRORS APPEAR

When ANY error appears in logs during startup or operation:

  1. IMMEDIATELY STOP - Do not continue with other tasks
  2. IDENTIFY THE ERROR - Read the full error message and context
  3. FIX THE ERROR - Address the root cause, not symptoms
  4. VERIFY THE FIX - Ensure error is completely resolved
  5. ONLY THEN CONTINUE - Never ignore or work around errors

NEVER restart servers to "fix" errors - FIX THE ACTUAL PROBLEM

Log Locations

Component Log File What's Logged
botserver botserver.log API requests, errors, script execution, client navigation events
botui botui.log UI rendering, WebSocket connections
drive_monitor In botserver logs with [drive_monitor] prefix File sync, compilation
client errors In botserver logs with CLIENT: prefix JavaScript errors, navigation events

🔧 Bug Fixing Workflow

Step 1: Reproduce & Diagnose

Identify the symptom:

# Check recent errors
grep -E " E | W " botserver.log | tail -20

# Check specific component
grep "component_name" botserver.log | tail -50

# Monitor live
tail -f botserver.log | grep -E "ERROR|WARN"

Trace the data flow:

  1. Find where the bug manifests (UI, API, database, cache)
  2. Work backwards through the call chain
  3. Check logs at each layer

Example: "Suggestions not showing"

# 1. Check if frontend is requesting suggestions
grep "GET /api/suggestions" botserver.log | tail -5

# 2. Check if suggestions exist in cache
/opt/gbo/bin/botserver-stack/bin/cache/bin/valkey-cli --scan --pattern "suggestions:*"

# 3. Check if suggestions are being generated
grep "ADD_SUGGESTION" botserver.log | tail -10

# 4. Verify the Redis key format
grep "Adding suggestion to Redis key" botserver.log | tail -5

Step 2: Find the Code

Use code search tools:

# Find function/keyword implementation
cd botserver/src && grep -r "ADD_SUGGESTION_TOOL" --include="*.rs"

# Find where Redis keys are constructed
grep -r "suggestions:" --include="*.rs" | grep format

# Find struct definition
grep -r "pub struct UserSession" --include="*.rs"

Check module responsibility:

Step 3: Fix the Bug

Identify root cause:

  • Wrong variable used? (e.g., user_id instead of bot_id)
  • Missing validation?
  • Race condition?
  • Configuration issue?

Make minimal changes:

// ❌ BAD: Rewrite entire function
fn add_suggestion(...) {
    // 100 lines of new code
}

// ✅ GOOD: Fix only the bug
fn add_suggestion(...) {
    // Change line 318:
    - let key = format!("suggestions:{}:{}", user_session.user_id, session_id);
    + let key = format!("suggestions:{}:{}", user_session.bot_id, session_id);
}

Search for similar bugs:

# If you fixed user_id -> bot_id in one place, check all occurrences
grep -n "user_session.user_id" botserver/src/basic/keywords/add_suggestion.rs

Step 4: Test Locally

Verify the fix:

# 1. Build
cargo check -p botserver

# 2. Restart
./restart.sh

# 3. Test the specific feature
# - Open browser to http://localhost:3000/<botname>
# - Trigger the bug scenario
# - Verify it's fixed

# 4. Check logs for errors
tail -20 botserver.log | grep -E "ERROR|WARN"

Step 5: Commit & Deploy

Commit with clear message:

cd botserver
git add src/path/to/file.rs
git commit -m "Fix: Use bot_id instead of user_id in suggestion keys

- Root cause: Wrong field used in Redis key format
- Impact: Suggestions stored under wrong key, frontend couldn't retrieve
- Files: src/basic/keywords/add_suggestion.rs (5 occurrences)
- Testing: Verified suggestions now appear in UI"

Push to remotes:

# Push submodule
git push alm main
git push origin main

# Update root repository
cd ..
git add botserver
git commit -m "Update botserver: Fix suggestion key bug"
git push alm main
git push origin main

Production deployment:

  • ALM push triggers CI/CD pipeline
  • Wait ~10 minutes for build + deploy
  • Service auto-restarts on binary update
  • Test in production after deployment

Step 6: Document

Add to AGENTS-PROD.md if production-relevant:

  • Common symptom
  • Diagnosis commands
  • Fix procedure
  • Prevention tips

Update code comments if needed:

// Redis key format: suggestions:bot_id:session_id
// Note: Must use bot_id (not user_id) to match frontend queries
let key = format!("suggestions:{}:{}", user_session.bot_id, session_id);

🎨 Frontend Standards

HTMX-First Approach

  • Use HTMX to minimize JavaScript
  • Server returns HTML fragments, not JSON
  • Use hx-get, hx-post, hx-target, hx-swap
  • WebSocket via htmx-ws extension

Local Assets Only - NO CDN

<!-- ✅ CORRECT -->
<script src="js/vendor/htmx.min.js"></script>

<!-- ❌ WRONG -->
<script src="https://unpkg.com/htmx.org@1.9.10"></script>

🚀 Performance & Size Standards

Binary Size Optimization

  • Release Profile: Always maintain opt-level = "z", lto = true, codegen-units = 1, strip = true, panic = "abort".
  • Dependencies:
    • Run cargo tree --duplicates weekly
    • Run cargo machete to remove unused dependencies
    • Use default-features = false and explicitly opt-in to needed features

Linting & Code Quality

  • Clippy: Code MUST pass cargo clippy --workspace with 0 warnings.
  • No Allow: NEVER use #[allow(clippy::...)] in source code - FIX the code instead.

🔧 Technical Debt

Critical Issues to Address

  • Error handling debt: instances of unwrap()/expect() in production code
  • Performance debt: excessive clone()/to_string() calls
  • File size debt: files exceeding 450 lines

Weekly Maintenance Tasks

cargo tree --duplicates   # Find duplicate dependencies
cargo machete            # Remove unused dependencies
cargo build --release && ls -lh target/release/botserver  # Check binary size
cargo audit              # Security audit

📋 Continuation Prompt

When starting a new session or continuing work:

Continue on gb/ workspace. Follow AGENTS.md strictly:

1. Check current state with build/diagnostics
2. Fix ALL warnings and errors - NO #[allow()] attributes
3. Delete unused code, don't suppress warnings
4. Remove unused parameters, don't prefix with _
5. Replace ALL unwrap()/expect() with proper error handling
6. Verify after each fix batch
7. Loop until 0 warnings, 0 errors
8. Refactor files >450 lines

🔑 Memory & Main Directives

LOOP AND COMPACT UNTIL 0 WARNINGS - MAXIMUM PRECISION

  • 0 warnings
  • 0 errors
  • Trust project diagnostics
  • Respect all rules
  • No #[allow()] in source code
  • Real code fixes only

Remember:

  • OFFLINE FIRST - Fix all errors from list before compiling
  • BATCH BY FILE - Fix ALL errors in a file at once
  • WRITE ONCE - Single edit per file with all fixes
  • VERIFY LAST - Only compile/diagnostics after ALL fixes
  • DELETE DEAD CODE - Don't keep unused code around
  • GIT WORKFLOW - ALWAYS push to ALL repositories (github, pragmatismo)

Deploy in Prod Workflow

CI/CD Pipeline (Primary Method)

  1. Push to ALM — triggers CI/CD automatically:

    cd botserver
    git push alm main
    git push origin main
    cd ..
    git add botserver
    git commit -m "Update botserver: <description>"
    git push alm main
    git push origin main
    
  2. Wait for CI programmatically — poll Forgejo API until build completes:

    # ALM is at http://<ALM_HOST>:4747 (port 4747, NOT 3000)
    # The runner is in container alm-ci, registered with token from DB
    
    # Method 1: Poll API for latest workflow run status
    ALM_URL="http://<ALM_HOST>:4747"
    REPO="GeneralBots/BotServer"
    MAX_WAIT=600  # 10 minutes
    ELAPSED=0
    
    while [ $ELAPSED -lt $MAX_WAIT ]; do
      STATUS=$(curl -sf "$ALM_URL/api/v1/repos/$REPO/actions/runs?per_page=1" | python3 -c "import sys,json; runs=json.load(sys.stdin); print(runs[0]['status'] if runs else 'unknown')")
      if [ "$STATUS" = "completed" ] || [ "$STATUS" = "failure" ] || [ "$STATUS" = "cancelled" ]; then
        echo "CI finished with status: $STATUS"
        break
      fi
      echo "CI status: $STATUS (waiting ${ELAPSED}s...)"
      sleep 15
      ELAPSED=$((ELAPSED + 15))
    done
    
    # Method 2: Check runner logs directly
    ssh <PROD_HOST> "sudo incus exec alm-ci -- tail -20 /opt/gbo/logs/forgejo-runner.log"
    
    # Method 3: Check binary timestamp after CI completes
    sleep 240
    ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 <PROD_HOST> \
      "sudo incus exec system -- stat -c '%y' /opt/gbo/bin/botserver"
    
  3. Restart in prod — after binary updates:

    ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 <PROD_HOST> \
      "sudo incus exec system -- pkill -f botserver || true"
    sleep 2
    ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 <PROD_HOST> \
      "sudo incus exec system -- bash -c 'cd /opt/gbo/bin && RUST_LOG=info nohup ./botserver --noconsole > /opt/gbo/logs/stdout.log 2>&1 &'"
    
  4. Verify deployment:

    # Wait for bootstrap (~2 min)
    sleep 120
    # Check health
    ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 <PROD_HOST> \
      "sudo incus exec system -- curl -s -o /dev/null -w '%{http_code}' http://localhost:8080/health"
    # Check logs
    ssh -o StrictHostKeyChecking=no -o ConnectTimeout=10 <PROD_HOST> \
      "sudo incus exec system -- tail -30 /opt/gbo/logs/stdout.log"
    

Production Container Architecture

Container Service Port Notes
system BotServer 8080 Main API server
vault Vault 8200 Secrets management (isolated)
tables PostgreSQL 5432 Database
cache Valkey 6379 Cache
drive MinIO 9100 Object storage
directory Zitadel 9000 Identity provider
meet LiveKit 7880 Video conferencing
vectordb Qdrant 6333 Vector database
llm llama.cpp 8081 Local LLM
email Stalwart 25/587 Mail server
alm Forgejo 4747 Git server (NOT 3000!)
alm-ci Forgejo Runner - CI runner
proxy Caddy 80/443 Reverse proxy

Important: ALM (Forgejo) listens on port 4747, not 3000. The runner token is stored in the action_runner_token table in the PROD-ALM database.

CI Runner Troubleshooting

Symptom Cause Fix
Runner not connecting Wrong ALM port (3000 vs 4747) Use port 4747 in runner registration
registration file not found .runner file missing or wrong format Re-register: forgejo-runner register --instance http://<ALM_HOST>:4747 --token <TOKEN> --name gbo --labels ubuntu-latest:docker://node:20-bookworm --no-interactive
unsupported protocol scheme .runner file has wrong JSON format Delete .runner and re-register
connection refused to ALM iptables blocking or ALM not running Check sudo incus exec alm -- ss -tlnp | grep 4747
CI not picking up jobs Runner not registered or labels mismatch Check runner labels match workflow runs-on field

🖥️ Production Operations Guide

⚠️ CRITICAL SAFETY RULES

  1. NEVER modify iptables rules without explicit confirmation — always confirm the exact rules, source IPs, ports, and destinations before applying
  2. NEVER touch the PROD project without asking first — no changes to production services, configs, or containers without user approval
  3. ALWAYS backup files to /tmp before editing — e.g. cp /path/to/file /tmp/$(basename /path/to/file).bak-$(date +%Y%m%d%H%M%S)

Infrastructure Overview

  • Host OS: Ubuntu LTS
  • Container engine: Incus (LXC-based)
  • Base path: /opt/gbo/ (General Bots Operations)
  • Data path: /opt/gbo/data — shared data, configs, bot definitions
  • Bin path: /opt/gbo/bin — compiled binaries
  • Conf path: /opt/gbo/conf — service configurations
  • Log path: /opt/gbo/logs — application logs

Container Architecture

Role Service Typical Port Notes
dns CoreDNS 53 DNS resolution, zone files in /opt/gbo/data
proxy Caddy 80/443 Reverse proxy, TLS termination
tables PostgreSQL 5432 Primary database
email Stalwart 993/465/587 Mail server (IMAPS, SMTPS, Submission)
system BotServer + Valkey 8080/6379 Main API + cache
webmail Roundcube behind proxy PHP-FPM webmail frontend
alm Forgejo 4747 Git/ALM server (NOT 3000!)
alm-ci Forgejo Runner - CI/CD runner
drive MinIO 9000/9100 Object storage
table-editor NocoDB behind proxy Database UI, connects to tables
vault Vault 8200 Secrets management
directory Zitadel 9000 Identity provider
meet LiveKit 7880 Video conferencing
vectordb Qdrant 6333 Vector database
llm llama.cpp 8081 Local LLM inference

Container Management

# List all containers
sudo incus list

# Start/Stop/Restart
sudo incus start <container>
sudo incus stop <container>
sudo incus restart <container>

# Exec into container
sudo incus exec <container> -- bash

# View container logs
sudo incus log <container>
sudo incus log <container> --show-log

# File operations
sudo incus file pull <container>/path/to/file /local/dest
sudo incus file push /local/src <container>/path/to/dest

# Create snapshot before changes
sudo incus snapshot create <container> pre-change-$(date +%Y%m%d%H%M%S)

Service Management (inside container)

# Check if process is running
sudo incus exec <container> -- pgrep -a <process-name>

# Restart service (systemd)
sudo incus exec <container> -- systemctl restart <service>

# Follow logs
sudo incus exec <container> -- journalctl -u <service> -f

# Check listening ports
sudo incus exec <container> -- ss -tlnp

Quick Health Check

# Check all containers status
sudo incus list --format csv

# Quick service check across containers
for c in dns proxy tables system email webmail alm alm-ci drive table-editor; do
  echo -n "$c: "
  sudo incus exec $c -- pgrep -a $(case $c in
    dns) echo "coredns";;
    proxy) echo "caddy";;
    tables) echo "postgres";;
    system) echo "botserver";;
    email) echo "stalwart";;
    webmail) echo "php-fpm";;
    alm) echo "forgejo";;
    alm-ci) echo "runner";;
    drive) echo "minio";;
    table-editor) echo "nocodb";;
  esac) >/dev/null && echo OK || echo FAIL
done

Network & NAT

Port Forwarding Pattern

External ports on the host are DNAT'd to container IPs via iptables. NAT rules live in /etc/iptables.rules.

Critical rule pattern — always use the external interface (-i <iface>) to avoid loopback issues:

-A PREROUTING -i <external-iface> -p tcp --dport <port> -j DNAT --to-destination <container-ip>:<port>

Typical Port Map

External Service Notes
53 DNS Public DNS resolution
80/443 HTTP/HTTPS Via Caddy proxy
5432 PostgreSQL Restricted access only
993 IMAPS Secure email retrieval
465 SMTPS Secure email sending
587 SMTP Submission STARTTLS
25 SMTP Often blocked by ISPs
4747 Forgejo Behind proxy
9000 MinIO API Internal only
8200 Vault Isolated

Network Diagnostics

# Check NAT rules
sudo iptables -t nat -L -n | grep DNAT

# Test connectivity from container
sudo incus exec <container> -- ping -c 3 8.8.8.8

# Test DNS resolution
sudo incus exec <container> -- dig <domain>

# Test port connectivity
nc -zv <container-ip> <port>

Key Service Operations

DNS (CoreDNS)

  • Config: /opt/gbo/conf/Corefile
  • Zones: /opt/gbo/data/<domain>.zone
  • Test: dig @<dns-container-ip> <domain>

Database (PostgreSQL)

  • Data: /opt/gbo/data
  • Backup: pg_dump -U postgres -F c -f /tmp/backup.dump <dbname>
  • Restore: pg_restore -U postgres -d <dbname> /tmp/backup.dump

Email (Stalwart)

  • Config: /opt/gbo/conf/config.toml
  • DKIM: Check TXT records for selector._domainkey.<domain>
  • Webmail: Behind proxy
  • Admin: Accessible via configured admin port

Recovery from crash:

# Check if service starts with config validation
sudo incus exec email -- /opt/gbo/bin/stalwart -c /opt/gbo/conf/config.toml --help

# Check error logs
sudo incus exec email -- cat /opt/gbo/logs/stderr.log

# Restore from snapshot if config corrupted
sudo incus snapshot list email
sudo incus copy email/<snapshot> email-temp
sudo incus start email-temp
sudo incus file pull email-temp/opt/gbo/conf/config.toml /tmp/config.toml
sudo incus file push /tmp/config.toml email/opt/gbo/conf/config.toml

Proxy (Caddy)

  • Config: /opt/gbo/conf/config
  • Backup before edit: cp /opt/gbo/conf/config /opt/gbo/conf/config.bak-$(date +%Y%m%d)
  • Validate: caddy validate --config /opt/gbo/conf/config
  • Reload: caddy reload --config /opt/gbo/conf/config

Storage (MinIO)

  • Console: Behind proxy
  • Internal API: http://:9000
  • Data: /opt/gbo/data

Bot System (system)

  • Service: BotServer + Valkey (Redis-compatible)
  • Binary: /opt/gbo/bin/botserver
  • Valkey: port 6379

Git/ALM (Forgejo)

  • Port: 4747 (NOT 3000!)
  • Behind proxy: Access via configured hostname
  • CI Runner: Separate container, registered with token from DB

CI/CD (Forgejo Runner)

  • Config: /opt/gbo/bin/config.yaml
  • Init: /etc/systemd/system/alm-ci-runner.service (runs as gbuser, NOT root)
  • Logs: /opt/gbo/logs/out.log, /opt/gbo/logs/err.log
  • Auto-start: Via systemd (enabled)
  • Runner user: gbuser (uid 1000) — all /opt/gbo/ files owned by gbuser:gbuser
  • sccache: Installed at /usr/local/bin/sccache, configured via RUSTC_WRAPPER=sccache in workflow
  • Workspace: /opt/gbo/data/ (NOT /opt/gbo/ci/)
  • Cargo cache: /home/gbuser/.cargo/ (registry + git db)
  • Rustup: /home/gbuser/.rustup/
  • SSH keys: /home/gbuser/.ssh/id_ed25519 (for deploy to system container)
  • Deploy mechanism: CI builds binary → tar+gzip via SSH → /opt/gbo/bin/botserver on system container

Backup & Recovery

Snapshot Recovery

# List snapshots
sudo incus snapshot list <container>

# Restore from snapshot
sudo incus copy <container>/<snapshot> <container>-restored
sudo incus start <container>-restored

# Get files from snapshot without starting
sudo incus file pull <container>/<snapshot>/path/to/file .

Backup Scripts

  • Host config backup: /opt/gbo/bin/backup-local-host.sh
  • Remote backup to S3: /opt/gbo/bin/backup-remote.sh

Troubleshooting

Container Won't Start

# Check status
sudo incus list
sudo incus info <container>

# Check logs
sudo incus log <container> --show-log

# Try starting with verbose
sudo incus start <container> -v

Service Not Running

# Find process
sudo incus exec <container> -- pgrep -a <process>

# Check listening ports
sudo incus exec <container> -- ss -tlnp | grep <port>

# Check application logs
sudo incus exec <container> -- tail -50 /opt/gbo/logs/stderr.log

Email Delivery Issues

# Check mail server is running
sudo incus exec email -- pgrep -a stalwart

# Check IMAP/SMTP ports
nc -zv <email-ip> 993
nc -zv <email-ip> 465
nc -zv <email-ip> 587

# Check DKIM DNS records
dig TXT <selector>._domainkey.<domain>

# Check mail logs
sudo incus exec email -- tail -100 /opt/gbo/logs/email.log

Maintenance

Update Container

# Stop container
sudo incus stop <container>

# Create snapshot backup
sudo incus snapshot create <container> pre-update-$(date +%Y%m%d)

# Update packages
sudo incus exec <container> -- apt update && apt upgrade -y

# Restart
sudo incus start <container>

Disk Space Management

# Check host disk usage
df -h /

# Check btrfs pool (if applicable)
sudo btrfs filesystem df /var/lib/incus

# Clean old logs in container
sudo incus exec <container> -- find /opt/gbo/logs -name "*.log.*" -mtime +7 -delete

Container Tricks & Optimizations

Resource Limits

# Set CPU limit
sudo incus config set <container> limits.cpu 2

# Set memory limit
sudo incus config set <container> limits.memory 4GiB

# Set disk limit
sudo incus config device set <container> root size 20GiB

Profile Management

# List profiles
sudo incus profile list

# Apply profile to container
sudo incus profile add <container> <profile>

# Clone container for testing
sudo incus copy <source> <target> --ephemeral

Network Optimization

# Add static DHCP-like assignment
sudo incus config device add <container> eth0 nic nictype=bridged parent=<bridge>

# Set custom DNS for container
sudo incus config set <container> raw.lxc "lxc.net.0.ipv4.address=<ip>"

Quick Container Cloning for Testing

# Snapshot and clone for safe testing
sudo incus snapshot create <container> test-base
sudo incus copy <container>/test-base <container>-test
sudo incus start <container>-test
# ... test safely ...
sudo incus stop <container>-test
sudo incus delete <container>-test

AutoTask & BASIC Keywords Reference

AutoTask System Overview

AutoTask is an AI-driven task execution system that:

  1. Analyzes user intent - "Send email to all customers", "Create weekly report"
  2. Plans execution steps - Break down into actionable tasks
  3. Generates BASIC scripts - Using available keywords to accomplish the task
  4. Executes scripts - Run immediately or schedule for later

File Locations

.gbdrive/
├── reports/           # Generated reports
├── documents/         # Created documents
├── exports/           # Data exports
└── apps/{appname}/    # HTMX apps (synced to SITES_ROOT)

.gbdialog/
├── schedulers/        # Scheduled jobs (cron-based)
├── tools/             # Voice/chat triggered tools
└── handlers/          # Event handlers

Complete BASIC Keywords Reference

Data Operations

Keyword Syntax Description
GET GET FROM {table} WHERE {condition} Query database records
SET SET {variable} = {value} Set variable value
SAVE SAVE {data} TO {table} Insert/update database record
FIND FIND {value} IN {table} Search for specific value
FIRST FIRST({array}) Get first element
LAST LAST({array}) Get last element
FORMAT FORMAT "{template}", var1, var2 Format string with variables

Communication

Keyword Syntax Description
SEND MAIL SEND MAIL TO "{email}" WITH subject, body Send email
SEND TEMPLATE SEND TEMPLATE "{name}" TO "{email}" Send email template
SEND SMS SEND SMS TO "{phone}" MESSAGE "{text}" Send SMS
TALK TALK "{message}" Respond to user
HEAR HEAR "{phrase}" AS {variable} Listen for user input

File Operations

Keyword Syntax Description
CREATE FILE CREATE FILE "{path}" WITH {content} Create file in .gbdrive
READ FILE READ FILE "{path}" Read file content
WRITE FILE WRITE FILE "{path}" WITH {content} Write to file
DELETE FILE DELETE FILE "{path}" Delete file
COPY FILE COPY FILE "{source}" TO "{dest}" Copy file
MOVE FILE MOVE FILE "{source}" TO "{dest}" Move/rename file
LIST FILES LIST FILES "{path}" List directory contents
UPLOAD UPLOAD {data} TO "{path}" Upload file
DOWNLOAD DOWNLOAD "{url}" TO "{path}" Download file

HTTP Operations

Keyword Syntax Description
GET HTTP GET HTTP "{url}" HTTP GET request
POST HTTP POST HTTP "{url}" WITH {data} HTTP POST request
PUT HTTP PUT HTTP "{url}" WITH {data} HTTP PUT request
DELETE HTTP DELETE HTTP "{url}" HTTP DELETE request
WEBHOOK WEBHOOK "{url}" WITH {data} Send webhook

AI/LLM Operations

Keyword Syntax Description
LLM LLM "{prompt}" Call LLM with prompt
USE KB USE KB "{knowledge_base}" Use knowledge base for context
CLEAR KB CLEAR KB Clear knowledge base context
USE TOOL USE TOOL "{tool_name}" Enable external tool
CLEAR TOOLS CLEAR TOOLS Disable all tools
USE WEBSITE USE WEBSITE "{url}" Scrape website for context

Task & Scheduling

Keyword Syntax Description
CREATE_TASK CREATE_TASK "{title}", "{assignee}", "{due}", {project} Create task
WAIT WAIT {seconds} Pause execution
ON ON "{event}" DO {action} Event handler
ON EMAIL ON EMAIL FROM "{filter}" DO {action} Email trigger
ON CHANGE ON CHANGE {table} DO {action} Database change trigger

Bot & Memory

Keyword Syntax Description
SET BOT MEMORY SET BOT MEMORY "{key}" = {value} Store bot-level data
GET BOT MEMORY GET BOT MEMORY "{key}" Retrieve bot-level data
REMEMBER REMEMBER "{key}" = {value} Store session data
SET CONTEXT SET CONTEXT "{key}" = {value} Set conversation context
ADD SUGGESTION ADD SUGGESTION "{text}" Add response suggestion
CLEAR SUGGESTIONS CLEAR SUGGESTIONS Clear suggestions

User & Session

Keyword Syntax Description
SET USER SET USER "{property}" = {value} Update user property
TRANSFER TO HUMAN TRANSFER TO HUMAN Escalate to human agent
ADD_MEMBER ADD_MEMBER "{group}", "{email}", "{role}" Add user to group

Documents & Content

Keyword Syntax Description
CREATE DRAFT CREATE DRAFT "{title}" WITH {content} Create document draft
CREATE SITE CREATE SITE "{name}" WITH {config} Create website
SAVE FROM UNSTRUCTURED SAVE FROM UNSTRUCTURED {data} TO {table} Parse and save data

Multi-Bot Operations

Keyword Syntax Description
ADD BOT ADD BOT "{name}" WITH TRIGGER "{phrase}" Add sub-bot
REMOVE BOT REMOVE BOT "{name}" Remove sub-bot
LIST BOTS LIST BOTS List active bots
DELEGATE TO DELEGATE TO "{bot}" Delegate to another bot
SEND TO BOT SEND TO BOT "{name}" MESSAGE "{msg}" Inter-bot message
BROADCAST MESSAGE BROADCAST MESSAGE "{msg}" Broadcast to all bots

Social Media

Keyword Syntax Description
POST TO SOCIAL POST TO SOCIAL "{platform}" MESSAGE "{text}" Social media post
GET SOCIAL FEED GET SOCIAL FEED "{platform}" Get social feed

Control Flow

Keyword Syntax Description
IF/THEN/ELSE/END IF IF condition THEN ... ELSE ... END IF Conditional
FOR EACH/NEXT FOR EACH item IN collection ... NEXT Loop
SWITCH/CASE/END SWITCH SWITCH var CASE val ... END SWITCH Switch statement
PRINT PRINT {value} Debug output

Built-in Variables

Variable Description
TODAY Current date
NOW Current datetime
USER Current user object
SESSION Current session object
BOT Current bot object