Compare commits

..

No commits in common. "main" and "pragmatismo" have entirely different histories.

52 changed files with 1339 additions and 16174 deletions

View file

@ -1,5 +1,5 @@
[build]
# rustc-wrapper = "sccache"
rustc-wrapper = "sccache"
[target.x86_64-unknown-linux-gnu]
linker = "clang"

View file

@ -1,8 +0,0 @@
# General Bots Environment Configuration
# Copy this file to .env and fill in values
# NEVER commit .env to version control
# Vault connection
VAULT_ADDR=https://127.0.0.1:8200
VAULT_TOKEN=<your-vault-token-here>
VAULT_CACERT=./botserver-stack/vault/certs/ca.crt

View file

@ -1,16 +0,0 @@
name: Botapp CI
on:
push:
branches: [main]
paths:
- 'botapp/**'
jobs:
build:
runs-on: gbo
steps:
- name: Build
run: |
cd /opt/gbo/work/botapp
git reset --hard HEAD && git clean -fd
git pull
cargo build

View file

@ -1,16 +0,0 @@
name: Botbook CI
on:
push:
branches: [main]
paths:
- 'botbook/**'
jobs:
build:
runs-on: gbo
steps:
- name: Build
run: |
cd /opt/gbo/work/botbook
git reset --hard HEAD && git clean -fd
git pull
cargo build

View file

@ -1,16 +0,0 @@
name: Botdevice CI
on:
push:
branches: [main]
paths:
- 'botdevice/**'
jobs:
build:
runs-on: gbo
steps:
- name: Build
run: |
cd /opt/gbo/work/botdevice
git reset --hard HEAD && git clean -fd
git pull
cargo build

View file

@ -1,16 +0,0 @@
name: Botmodels CI
on:
push:
branches: [main]
paths:
- 'botmodels/**'
jobs:
build:
runs-on: gbo
steps:
- name: Build
run: |
cd /opt/gbo/work/botmodels
git reset --hard HEAD && git clean -fd
git pull
cargo build

View file

@ -1,16 +0,0 @@
name: Botplugin CI
on:
push:
branches: [main]
paths:
- 'botplugin/**'
jobs:
build:
runs-on: gbo
steps:
- name: Build
run: |
cd /opt/gbo/work/botplugin
git reset --hard HEAD && git clean -fd
git pull
cargo build

View file

@ -1,29 +0,0 @@
name: BotServer CI
on:
push:
branches: [main]
paths:
- 'botserver/**'
- 'botlib/**'
- 'Cargo.lock'
- '.forgejo/workflows/botserver.yaml'
jobs:
build:
runs-on: gbo
steps:
- name: Build
run: |
cd /opt/gbo/work/botserver
git reset --hard HEAD && git clean -fd
git pull
git submodule update --init --recursive botlib botserver
cargo build -p botserver
- name: Deploy
run: |
sudo incus exec system -- systemctl stop botserver || true
sudo incus exec system -- pkill -x botserver || true
sleep 1
sudo incus file push /opt/gbo/work/botserver/target/debug/botserver system:/opt/gbo/bin/botserver --mode=0755
sudo incus exec system -- systemctl start botserver
sleep 2
sudo incus exec system -- pgrep -x botserver && echo "✅ BotServer Deployed" || echo "❌ Failed"

View file

@ -1,16 +0,0 @@
name: Bottest CI
on:
push:
branches: [main]
paths:
- 'bottest/**'
jobs:
build:
runs-on: gbo
steps:
- name: Build
run: |
cd /opt/gbo/work/bottest
git reset --hard HEAD && git clean -fd
git pull
cargo build

View file

@ -1,20 +0,0 @@
name: BotUI CI
on:
push:
branches: [main]
paths:
- 'botui/**'
- 'botlib/**'
jobs:
build:
runs-on: gbo
steps:
- name: Build
run: |
cd /opt/gbo/work/botui
git reset --hard HEAD && git clean -fd
git pull
cargo build
- name: Deploy
run: |
echo "BotUI deployed"

56
.gitignore vendored
View file

@ -2,23 +2,15 @@
target/
*.out
bin/
*.png
*.jpg
# Logs
*.log
*logfile*
*-log*
.vscode
.zed
.gemini
.claude
# Temporary files
.tmp*
.tmp/*
.tmp
# Environment files
.env
@ -32,24 +24,19 @@ work/
# Documentation build
docs/book
.ruff_cache
.goutputstream*
# Installers (keep gitkeep)
botserver-installers/*
!botserver-installers/.gitkeep
botserver-stack
TODO*
work
.swp
# Lock file
# Cargo.lock (should be tracked)
# Lock file (regenerated from Cargo.toml)
Cargo.lock
.kiro
config
# Data directory (contains bot configs and API keys)
data/
# Playwright
node_modules/
/test-results/
@ -57,38 +44,3 @@ node_modules/
/blob-report/
/playwright/.cache/
/playwright/.auth/
.playwright*
.ruff_cache
.opencode
config/directory_config.json
# CI cache bust: Fri Feb 13 22:33:51 UTC 2026
# Secrets - NEVER commit these files
vault-unseal-keys
start-and-unseal.sh
vault-token-*
init.json
*.pem
*.key
*.crt
*.cert
$null
AppData/
build_errors*.txt
build_errors_utf8.txt
check.json
clippy*.txt
errors.txt
errors_utf8.txt
vault-unseal-keysdefault-vault.tar
prompts/sec-bots.md
AGENTS-PROD.md
# Backup files
*.head
*.bad2
*.bad
*.check
*.fix
*.bak

23
.gitmodules vendored
View file

@ -1,43 +1,42 @@
[submodule "botapp"]
path = botapp
url = ../botapp.git
url = https://alm.pragmatismo.com.br/GeneralBots/botapp.git
[submodule "botserver"]
path = botserver
url = ../BotServer.git
url = https://alm.pragmatismo.com.br/GeneralBots/botserver.git
[submodule "botlib"]
path = botlib
url = ../botlib.git
url = https://alm.pragmatismo.com.br/GeneralBots/botlib.git
[submodule "botui"]
path = botui
url = ../botui.git
url = https://alm.pragmatismo.com.br/GeneralBots/botui.git
[submodule "botbook"]
path = botbook
url = ../botbook.git
url = https://alm.pragmatismo.com.br/GeneralBots/botbook.git
[submodule "bottest"]
path = bottest
url = ../bottest.git
url = https://alm.pragmatismo.com.br/GeneralBots/bottest.git
[submodule "botdevice"]
path = botdevice
url = ../botdevice.git
url = https://alm.pragmatismo.com.br/GeneralBots/botdevice.git
[submodule "botmodels"]
path = botmodels
url = ../botmodels.git
url = https://alm.pragmatismo.com.br/GeneralBots/botmodels.git
[submodule "botplugin"]
path = botplugin
url = ../botplugin.git
url = https://alm.pragmatismo.com.br/GeneralBots/botplugin.git
[submodule "bottemplates"]
path = bottemplates
url = ../bottemplates.git
url = https://alm.pragmatismo.com.br/GeneralBots/bottemplates.git
[submodule ".github"]
path = .github
url = ../.github.git
url = https://alm.pragmatismo.com.br/GeneralBots/.github.git

9
.idea/gb.iml generated Normal file
View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

10
.idea/libraries/botserver_installers.xml generated Normal file
View file

@ -0,0 +1,10 @@
<component name="libraryTable">
<library name="botserver-installers">
<CLASSES>
<root url="jar://$PROJECT_DIR$/botserver/botserver-installers/llama-b7345-bin-ubuntu-x64.zip!/" />
<root url="jar://$PROJECT_DIR$/botserver/botserver-installers/vault_1.15.4_linux_amd64.zip!/" />
</CLASSES>
<JAVADOC />
<SOURCES />
</library>
</component>

6
.idea/misc.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project>

8
.idea/modules.xml generated Normal file
View file

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/gb.iml" filepath="$PROJECT_DIR$/.idea/gb.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>

52
.idea/workspace.xml generated Normal file
View file

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ChangeListManager">
<list default="true" id="32fd08b0-7933-467d-9a46-1a53fd2da15c" name="Changes" comment="">
<change beforePath="$PROJECT_DIR$/botserver" beforeDir="false" afterPath="$PROJECT_DIR$/botserver" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
<option name="LAST_RESOLUTION" value="IGNORE" />
</component>
<component name="Git.Settings">
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
</component>
<component name="ProjectColorInfo"><![CDATA[{
"associatedIndex": 1
}]]></component>
<component name="ProjectId" id="38qdWTFkX8Nem4LzgigXpAycSN7" />
<component name="ProjectViewState">
<option name="hideEmptyMiddlePackages" value="true" />
<option name="showLibraryContents" value="true" />
</component>
<component name="PropertiesComponent"><![CDATA[{
"keyToString": {
"ModuleVcsDetector.initialDetectionPerformed": "true",
"RunOnceActivity.ShowReadmeOnStart": "true",
"RunOnceActivity.git.unshallow": "true",
"RunOnceActivity.typescript.service.memoryLimit.init": "true",
"git-widget-placeholder": "main",
"last_opened_file_path": "/home/rodriguez/src/gb",
"vue.rearranger.settings.migration": "true"
}
}]]></component>
<component name="SharedIndexes">
<attachedChunks>
<set>
<option value="bundled-jdk-30f59d01ecdd-2fc7cc6b9a17-intellij.indexing.shared.core-IU-253.30387.90" />
</set>
</attachedChunks>
</component>
<component name="TaskManager">
<task active="true" id="Default" summary="Default task">
<changelist id="32fd08b0-7933-467d-9a46-1a53fd2da15c" name="Changes" comment="" />
<created>1769531070022</created>
<option name="number" value="Default" />
<option name="presentableId" value="Default" />
<updated>1769531070022</updated>
<workItem from="1769531115917" duration="176000" />
</task>
<servers />
</component>
</project>

198
.kiro/settings/lsp.json Normal file
View file

@ -0,0 +1,198 @@
{
"languages": {
"typescript": {
"name": "typescript-language-server",
"command": "typescript-language-server",
"args": [
"--stdio"
],
"file_extensions": [
"ts",
"js",
"tsx",
"jsx"
],
"project_patterns": [
"package.json",
"tsconfig.json"
],
"exclude_patterns": [
"**/node_modules/**",
"**/dist/**"
],
"multi_workspace": false,
"initialization_options": {
"preferences": {
"disableSuggestions": false
}
},
"request_timeout_secs": 60
},
"python": {
"name": "pyright",
"command": "pyright-langserver",
"args": [
"--stdio"
],
"file_extensions": [
"py"
],
"project_patterns": [
"pyproject.toml",
"setup.py",
"requirements.txt",
"pyrightconfig.json"
],
"exclude_patterns": [
"**/__pycache__/**",
"**/venv/**",
"**/.venv/**",
"**/.pytest_cache/**"
],
"multi_workspace": false,
"initialization_options": {},
"request_timeout_secs": 60
},
"rust": {
"name": "rust-analyzer",
"command": "rust-analyzer",
"args": [],
"file_extensions": [
"rs"
],
"project_patterns": [
"Cargo.toml"
],
"exclude_patterns": [
"**/target/**"
],
"multi_workspace": false,
"initialization_options": {
"cargo": {
"buildScripts": {
"enable": true
}
},
"diagnostics": {
"enable": true,
"enableExperimental": true
},
"workspace": {
"symbol": {
"search": {
"scope": "workspace"
}
}
}
},
"request_timeout_secs": 60
},
"java": {
"name": "jdtls",
"command": "jdtls",
"args": [],
"file_extensions": [
"java"
],
"project_patterns": [
"pom.xml",
"build.gradle",
"build.gradle.kts",
".project"
],
"exclude_patterns": [
"**/target/**",
"**/build/**",
"**/.gradle/**"
],
"multi_workspace": false,
"initialization_options": {
"settings": {
"java": {
"compile": {
"nullAnalysis": {
"mode": "automatic"
}
},
"configuration": {
"annotationProcessing": {
"enabled": true
}
}
}
}
},
"request_timeout_secs": 60
},
"ruby": {
"name": "solargraph",
"command": "solargraph",
"args": [
"stdio"
],
"file_extensions": [
"rb"
],
"project_patterns": [
"Gemfile",
"Rakefile"
],
"exclude_patterns": [
"**/vendor/**",
"**/tmp/**"
],
"multi_workspace": false,
"initialization_options": {},
"request_timeout_secs": 60
},
"go": {
"name": "gopls",
"command": "gopls",
"args": [],
"file_extensions": [
"go"
],
"project_patterns": [
"go.mod",
"go.sum"
],
"exclude_patterns": [
"**/vendor/**"
],
"multi_workspace": false,
"initialization_options": {
"usePlaceholders": true,
"completeUnimported": true
},
"request_timeout_secs": 60
},
"cpp": {
"name": "clangd",
"command": "clangd",
"args": [
"--background-index"
],
"file_extensions": [
"cpp",
"cc",
"cxx",
"c",
"h",
"hpp",
"hxx"
],
"project_patterns": [
"CMakeLists.txt",
"compile_commands.json",
"Makefile"
],
"exclude_patterns": [
"**/build/**",
"**/cmake-build-**/**"
],
"multi_workspace": false,
"initialization_options": {},
"request_timeout_secs": 60
}
}
}

24
.vscode/launch.json vendored Normal file
View file

@ -0,0 +1,24 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug executable 'botserver'",
"cargo": {
"args": ["run", "--bin=botserver", "--package=botserver", "--manifest-path=${workspaceFolder}/botserver/Cargo.toml"],
"filter": {
"name": "botserver",
"kind": "bin"
}
},
"args": [],
"env": {
"RUST_LOG": "trace,aws_sigv4=off,aws_smithy_checksums=off,mio=off,reqwest=off,aws_runtime=off,aws_smithy_http_client=off,rustls=off,hyper_util=off,aws_smithy_runtime=off,aws_smithy_runtime_api=off,tracing=off,aws_sdk_s3=off"
},
"cwd": "${workspaceFolder}/botserver"
},
]
}

5
.vscode/settings.json vendored Normal file
View file

@ -0,0 +1,5 @@
{
"git.ignoreLimitWarning": true,
"Codegeex.SidebarUI.LanguagePreference": "English",
"Codegeex.RepoIndex": true
}

7
.zed/settings.json Normal file
View file

@ -0,0 +1,7 @@
{
"languages": {
"Rust": {
"enable_language_server": false,
},
},
}

1870
AGENTS.md

File diff suppressed because it is too large Load diff

11680
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -9,7 +9,6 @@ members = [
"bottest",
"botui",
]
exclude = ["backup-to-s3"]
[workspace.lints.rust]
@ -110,7 +109,6 @@ url = "2.5"
dirs = "5.0"
tempfile = "3"
walkdir = "2.5.0"
notify = "8.0"
# ─── COMPRESSION / ARCHIVES ───
flate2 = "1.0"
@ -176,7 +174,7 @@ indicatif = "0.18.0"
# ─── MEMORY ALLOCATOR ───
tikv-jemallocator = "0.6"
tikv-jemalloc-ctl = { version = "0.6", default-features = false, features = ["stats"] }
tikv-jemalloc-ctl = { version = "0.6", default-features = false }
# ─── SECRETS / VAULT ───
vaultrs = "0.7"
@ -198,7 +196,7 @@ csv = "1.3"
tonic = { version = "0.14.2", default-features = false }
# ─── STATIC FILES ───
rust-embed = { version = "8.5", features = ["interpolate-folder-path"] }
rust-embed = "8.5"
mime_guess = "2.0"
# ─── TAURI (Desktop/Mobile) ───

204
DEPENDENCIES-DEV.sh Normal file
View file

@ -0,0 +1,204 @@
#!/bin/bash
#
# DEPENDENCIES-DEV.sh - Development Dependencies for General Bots
#
# This script installs additional packages needed for BUILDING botserver from source.
# Only install these if you plan to compile the code yourself.
#
# Usage: sudo ./DEPENDENCIES-DEV.sh
#
set -e
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
echo -e "${GREEN}========================================${NC}"
echo -e "${GREEN} General Bots Development Dependencies${NC}"
echo -e "${GREEN}========================================${NC}"
# Check root
if [ "$EUID" -ne 0 ]; then
echo -e "${RED}Error: Run as root (use sudo)${NC}"
exit 1
fi
# Detect OS
if [ -f /etc/os-release ]; then
. /etc/os-release
OS=$ID
else
echo -e "${RED}Error: Cannot detect OS${NC}"
exit 1
fi
echo -e "${YELLOW}OS: $OS${NC}"
install_debian_ubuntu() {
apt-get update
apt-get install -y \
build-essential \
gcc \
g++ \
clang \
llvm-dev \
libclang-dev \
cmake \
make \
git \
pkg-config \
libssl-dev \
libpq-dev \
liblzma-dev \
zlib1g-dev \
libabseil-dev \
protobuf-compiler \
libprotobuf-dev \
automake \
bison \
flex \
gperf \
libtool \
m4 \
nasm \
python3 \
python3-pip \
nodejs \
npm
# Cross-compilation toolchains
apt-get install -y \
gcc-aarch64-linux-gnu \
gcc-arm-linux-gnueabihf \
gcc-x86-64-linux-gnu || true
}
install_fedora_rhel() {
dnf groupinstall -y "Development Tools"
dnf install -y \
gcc \
gcc-c++ \
clang \
llvm-devel \
clang-devel \
cmake \
make \
git \
pkgconf-devel \
openssl-devel \
libpq-devel \
xz-devel \
zlib-devel \
abseil-cpp-devel \
protobuf-compiler \
protobuf-devel \
automake \
bison \
flex \
gperf \
libtool \
m4 \
nasm \
python3 \
python3-pip \
nodejs \
npm
}
install_arch() {
pacman -Sy --noconfirm \
base-devel \
gcc \
clang \
llvm \
cmake \
make \
git \
pkgconf \
openssl \
postgresql-libs \
xz \
zlib \
abseil-cpp \
protobuf \
automake \
bison \
flex \
gperf \
libtool \
m4 \
nasm \
python \
python-pip \
nodejs \
npm
}
install_alpine() {
apk add --no-cache \
build-base \
gcc \
g++ \
clang \
llvm-dev \
clang-dev \
cmake \
make \
git \
pkgconf-dev \
openssl-dev \
postgresql-dev \
xz-dev \
zlib-dev \
abseil-cpp-dev \
protobuf-dev \
protoc \
automake \
bison \
flex \
gperf \
libtool \
m4 \
nasm \
python3 \
py3-pip \
nodejs \
npm
}
case $OS in
ubuntu|debian|linuxmint|pop)
install_debian_ubuntu
;;
fedora|rhel|centos|rocky|almalinux)
install_fedora_rhel
;;
arch|manjaro)
install_arch
;;
alpine)
install_alpine
;;
*)
echo -e "${RED}Unsupported OS: $OS${NC}"
echo "Required development packages:"
echo " - build-essential/base-devel"
echo " - gcc, g++, clang"
echo " - cmake, make, git"
echo " - Development headers for:"
echo " - OpenSSL, PostgreSQL, XZ, zlib"
echo " - Abseil, Protobuf, LLVM"
exit 1
;;
esac
echo -e "${GREEN}Development dependencies installed!${NC}"
echo ""
echo "Install Rust if not already installed:"
echo " curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
echo ""
echo "Then build with:"
echo " cargo build --release"

View file

@ -1,77 +0,0 @@
<#
.SYNOPSIS
Installs runtime dependencies for General Bots on Windows.
.DESCRIPTION
This script downloads and configures the system libraries required to build
and run BotServer on Windows. It downloads PostgreSQL binaries (for libpq)
and sets the PQ_LIB_DIR environment variable permanently.
.EXAMPLE
PS> .\DEPENDENCIES.ps1
#>
$ErrorActionPreference = 'Stop'
# ─── COLORS ───
function Write-Step { param($msg) Write-Host " * $msg" -ForegroundColor Green }
function Write-Warn { param($msg) Write-Host " ! $msg" -ForegroundColor Yellow }
function Write-Err { param($msg) Write-Host " x $msg" -ForegroundColor Red }
Write-Host "========================================" -ForegroundColor Green
Write-Host " General Bots Runtime Dependencies" -ForegroundColor Green
Write-Host " (Windows)" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host ""
# ─── PostgreSQL binaries (libpq.lib for Diesel ORM) ───
$PgsqlDir = "C:\pgsql\pgsql"
$PgsqlLib = "$PgsqlDir\lib\libpq.lib"
$PgsqlZipUrl = "https://get.enterprisedb.com/postgresql/postgresql-17.4-1-windows-x64-binaries.zip"
$PgsqlZip = "$env:TEMP\pgsql.zip"
if (Test-Path $PgsqlLib) {
Write-Step "PostgreSQL binaries already present at $PgsqlDir"
} else {
Write-Host "`nDownloading PostgreSQL binaries..." -ForegroundColor Cyan
Write-Host " URL: $PgsqlZipUrl"
Write-Host " This may take a few minutes (~300MB)...`n"
Invoke-WebRequest -Uri $PgsqlZipUrl -OutFile $PgsqlZip -UseBasicParsing
Write-Host "Extracting to C:\pgsql ..."
if (Test-Path "C:\pgsql") { Remove-Item "C:\pgsql" -Recurse -Force }
Expand-Archive -Path $PgsqlZip -DestinationPath "C:\pgsql" -Force
Remove-Item $PgsqlZip -Force -ErrorAction SilentlyContinue
if (Test-Path $PgsqlLib) {
Write-Step "PostgreSQL binaries installed successfully."
} else {
Write-Err "Failed to find libpq.lib after extraction!"
exit 1
}
}
# Set PQ_LIB_DIR permanently for the current user
$CurrentPqDir = [System.Environment]::GetEnvironmentVariable("PQ_LIB_DIR", "User")
if ($CurrentPqDir -ne "$PgsqlDir\lib") {
[System.Environment]::SetEnvironmentVariable("PQ_LIB_DIR", "$PgsqlDir\lib", "User")
$env:PQ_LIB_DIR = "$PgsqlDir\lib"
Write-Step "PQ_LIB_DIR set to '$PgsqlDir\lib' (User environment variable)"
} else {
Write-Step "PQ_LIB_DIR already configured."
}
# ─── Summary ───
Write-Host ""
Write-Host "========================================" -ForegroundColor Green
Write-Host " Dependencies installed!" -ForegroundColor Green
Write-Host "========================================" -ForegroundColor Green
Write-Host ""
Write-Host "You can now build and run:" -ForegroundColor Cyan
Write-Host " cargo build -p botserver"
Write-Host " cargo build -p botui"
Write-Host " .\restart.ps1"
Write-Host ""
Write-Host "NOTE: If this is the first time, restart your terminal" -ForegroundColor Yellow
Write-Host " so PQ_LIB_DIR takes effect." -ForegroundColor Yellow

967
PROD.md
View file

@ -1,967 +0,0 @@
# Production Environment Guide
## CRITICAL RULES — READ FIRST
NEVER INCLUDE HERE CREDENTIALS OR COMPANY INFORMATION, THIS IS COMPANY AGNOSTIC.
Always manage services with `systemctl` inside the `system` Incus container. Never run `/opt/gbo/bin/botserver` or `/opt/gbo/bin/botui` directly — they will fail because they won't load the `.env` file containing Vault credentials and paths. The correct commands are `sudo incus exec system -- systemctl start|stop|restart|status botserver` and the same for `ui`. Systemctl handles environment loading, auto-restart, logging, and dependencies.
Never push secrets (API keys, passwords, tokens) to git. Never commit `init.json` (it contains Vault unseal keys). All secrets must come from Vault — only `VAULT_*` variables are allowed in `.env`. Never deploy manually via scp or ssh; always use CI/CD. Always push all submodules (botserver, botui, botlib) before or alongside the main repo. Always ask before pushing to ALM.
---
## Infrastructure Overview
The host machine is accessed via `ssh user@<hostname>`, running Incus (an LXD fork) as hypervisor. All services run inside named Incus containers. You enter containers with `sudo incus exec <container> -- <command>` and list them with `sudo incus list`.
### Container Architecture
| Container | Service | Technology | Binary Path | Logs Path | Data Path | Notes |
|-----------|---------|------------|-------------|-----------|-----------|-------|
| **system** | BotServer + BotUI | Rust/Axum | `/opt/gbo/bin/botserver`<br>`/opt/gbo/bin/botui` | `/opt/gbo/logs/out.log`<br>`/opt/gbo/logs/err.log` | `/opt/gbo/work/` | Main API + UI proxy |
| **tables** | PostgreSQL | PostgreSQL 15+ | `/usr/lib/postgresql/*/bin/postgres` | `/opt/gbo/logs/postgresql/` | `/opt/gbo/data/pgdata/` | Primary database |
| **vault** | HashiCorp Vault | Vault | `/opt/gbo/bin/vault` | `/opt/gbo/logs/vault/` | `/opt/gbo/data/vault/` | Secrets management |
| **cache** | Valkey | Valkey (Redis fork) | `/opt/gbo/bin/valkey-server` | `/opt/gbo/logs/valkey/` | `/opt/gbo/data/valkey/` | Distributed cache |
| **drive** | MinIO | MinIO | `/opt/gbo/bin/minio` | `/opt/gbo/logs/minio/` | `/opt/gbo/data/minio/` | Object storage (S3 API) |
| **directory** | Zitadel | Zitadel (Go) | `/opt/gbo/bin/zitadel` | `/opt/gbo/logs/zitadel.log` | `PROD-DIRECTORY` DB | Identity provider |
| **llm** | llama.cpp | C++/CUDA | `/opt/gbo/bin/llama-server` | `/opt/gbo/logs/llm/` | `/opt/gbo/models/` | Local LLM inference |
| **vectordb** | Qdrant | Qdrant (Rust) | `/opt/gbo/bin/qdrant` | `/opt/gbo/logs/qdrant/` | `/opt/gbo/data/qdrant/` | Vector database |
| **alm** | Forgejo | Forgejo (Go) | `/opt/gbo/bin/forgejo` | `/opt/gbo/logs/forgejo/` | `/opt/gbo/data/forgejo/` | Git server (port 4747) |
| **alm-ci** | Forgejo Runner | Docker/runner | `/opt/gbo/bin/forgejo-runner` | `/opt/gbo/logs/forgejo-runner.log` | `/opt/gbo/data/ci/` | CI/CD runner |
| **proxy** | Caddy | Caddy | `/opt/gbo/bin/caddy` | `/opt/gbo/logs/caddy/` | `/opt/gbo/conf/` | Reverse proxy |
| **email** | Stalwart | Stalwart (Rust) | `/opt/gbo/bin/stalwart` | `/opt/gbo/logs/email/` | `/opt/gbo/data/email/` | Mail server |
| **webmail** | Roundcube | PHP | `/usr/share/roundcube/` | `/var/log/php/` | `/var/lib/roundcube/` | Webmail frontend |
| **dns** | CoreDNS | CoreDNS (Go) | `/opt/gbo/bin/coredns` | `/opt/gbo/logs/dns/` | `/opt/gbo/conf/Corefile` | DNS resolution |
| **meet** | LiveKit | LiveKit (Go) | `/opt/gbo/bin/livekit-server` | `/opt/gbo/logs/meet/` | `/opt/gbo/data/meet/` | Video conferencing |
| **table-editor** | NocoDB | NocoDB | `/opt/gbo/bin/nocodb` | `/opt/gbo/logs/nocodb/` | `/opt/gbo/data/nocodb/` | Database UI |
### Network Access
Externally, services are exposed via reverse proxy (Caddy). Internally, containers communicate via private IPs:
| Service | External URL | Internal Address |
|---------|--------------|------------------|
| BotServer | `https://<system-domain>` | `http://<system-ip>:8080` |
| BotUI | `https://<chat-domain>` | `http://<system-ip>:3000` |
| Zitadel | `https://<login-domain>` | `http://<directory-ip>:8080` |
| Forgejo | `https://<alm-domain>` | `http://<alm-ip>:4747` |
| Webmail | `https://<webmail-domain>` | `http://<webmail-ip>:80` |
| Roundcube | `https://<roundcube-domain>` | `http://<webmail-ip>:80` |
**Note:** BotUI's `BOTSERVER_URL` must be `http://<system-ip>:8080` internally, NOT the external HTTPS URL.
---
## Daily Operations
### Daily Health Check (5 minutes)
Run this every morning or after any deploy:
```bash
# 1. Container status
sudo incus list
# 2. Service health - all should show "active (running)"
sudo incus exec system -- systemctl is-active botserver
sudo incus exec system -- systemctl is-active ui
sudo incus exec directory -- systemctl is-active directory 2>/dev/null || echo "Directory check failed"
sudo incus exec drive -- pgrep -f minio > /dev/null && echo "MinIO OK" || echo "MinIO DOWN"
sudo incus exec tables -- pgrep -f postgres > /dev/null && echo "PostgreSQL OK" || echo "PostgreSQL DOWN"
# 3. IPv4 connectivity check - all containers should have IPv4
sudo incus list -c n4 | grep -E "(system|tables|vault|directory|drive|cache|llm|vector_db)" | grep -v "10\." && echo "WARNING: Missing IPv4" || echo "IPv4 OK"
# 4. Application health endpoint
curl -sf https://<system-domain>/api/health && echo "Health OK" || echo "Health FAILED"
# 5. Recent errors (last 10 lines)
sudo incus exec system -- tail -10 /opt/gbo/logs/err.log | grep -i "error\|panic\|failed" | head -5
```
**Expected Result:** All services "active", all containers have IPv4, health endpoint returns 200, no critical errors.
### Weekly Deep Check (15 minutes)
Run every Monday morning:
```bash
# 1. Disk space on all containers
for c in system tables vault directory drive cache llm vector_db; do
echo "=== $c ==="
sudo incus exec $c -- df -h / 2>/dev/null | tail -1
done
# 2. Database connection pool status
sudo incus exec tables -- psql -h localhost -U postgres -d botserver -c "SELECT count(*), state FROM pg_stat_activity GROUP BY state;"
# 3. Vault status (should be unsealed)
sudo incus exec vault -- curl -ksf https://localhost:8200/v1/sys/health | grep -q '"sealed":false' && echo "Vault unsealed" || echo "Vault SEALED - CRITICAL"
# 4. CI runner status
sudo incus exec alm-ci -- pgrep -f forgejo > /dev/null && echo "CI runner OK" || echo "CI runner DOWN"
# 5. MinIO buckets health
sudo incus exec drive -- bash -c 'export PATH=/opt/gbo/bin:$PATH && mc admin info local' 2>&1 | head -10
# 6. Backup verification - check latest snapshot exists
sudo incus snapshot list system | head -5
```
### Quick Status Dashboard
One-line status of everything:
```bash
echo "=== GBO Status Dashboard $(date) ==="
echo "Containers:"
sudo incus list -c n4,s | grep -E "(system|tables|vault|directory|drive|cache|llm|vector_db|alm-ci)" | awk '{print $1 ": " $3 " " $4}'
echo ""
echo "Services:"
for svc in botserver ui; do
sudo incus exec system -- systemctl is-active $svc 2>/dev/null && echo " $svc: ACTIVE" || echo " $svc: DOWN"
done
echo ""
echo "Health:"
curl -s -o /dev/null -w "%{http_code}" https://<system-domain>/api/health 2>/dev/null | grep -q "200" && echo " API: OK" || echo " API: FAIL"
```
---
## Alert Response Playbook
### Alert: "No IPv4 on container"
**Symptoms:** Container shows empty IPV4 column in `incus list`
**Quick Fix:**
```bash
# Identify container
CONTAINER=<name>
IP=<unused-ip-in-range> # e.g., 10.x.x.x
GATEWAY=<gateway-ip>
# Set static IP
sudo incus config device set $CONTAINER eth0 ipv4.address $IP
# Configure network inside
sudo incus exec $CONTAINER -- bash -c "cat > /etc/network/interfaces << 'EOF'
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address $IP
netmask 255.255.255.0
gateway $GATEWAY
dns-nameservers 8.8.8.8 8.8.4.4
EOF"
# Restart
sudo incus restart $CONTAINER
# Verify
sudo incus exec $CONTAINER -- ip addr show eth0
```
**Prevention:** Always configure static IP when creating new containers.
---
### Alert: "ALM botserver problem" / CI Build Failed
**Symptoms:** Deploy not working, CI status shows failure
**Quick Diagnostics:**
```bash
# Check CI database for recent runs
sudo incus exec tables -- bash -c 'export PGPASSWORD=<password>; psql -h localhost -U postgres -d PROD-ALM -c "SELECT id, status, created FROM action_run ORDER BY id DESC LIMIT 5;"'
# Status codes: 0=pending, 1=success, 2=failure, 3=cancelled, 6=running
```
**Quick Fixes:**
1. **If stuck at status 6 (running):**
```bash
RUN_ID=<stuck-run-id>
sudo incus exec tables -- bash -c "export PGPASSWORD=<password>; psql -h localhost -U postgres -d PROD-ALM -c \"UPDATE action_task SET status = 0 WHERE id = $RUN_ID; UPDATE action_run_job SET status = 0 WHERE run_id = $RUN_ID; UPDATE action_run SET status = 0 WHERE id = $RUN_ID;\""
```
2. **If /tmp permission denied:**
```bash
sudo incus exec alm-ci -- chmod 1777 /tmp
sudo incus exec alm-ci -- touch /tmp/build.log && chmod 666 /tmp/build.log
```
3. **If CI runner down:**
```bash
sudo incus exec alm-ci -- pkill -9 forgejo
sleep 2
sudo incus exec alm-ci -- bash -c 'cd /opt/gbo/bin && nohup ./forgejo-runner daemon --config config.yaml >> /opt/gbo/logs/forgejo-runner.log 2>&1 &'
```
**After fix:** Push a trivial change to re-trigger CI.
---
### Alert: "Email container stopping reach Internet"
**Symptoms:** Email notifications failing, container cannot resolve external domains
**Quick Diagnostics:**
```bash
# Test DNS from email container
sudo incus exec email -- nslookup google.com
# Check network config
sudo incus exec email -- cat /etc/resolv.conf
sudo incus exec email -- ip route
```
**Quick Fixes:**
1. **If IPv6-only (no IPv4):** Follow "No IPv4 on container" playbook above.
2. **If DNS not working:**
```bash
# Force Google DNS
sudo incus exec email -- bash -c 'echo "nameserver 8.8.8.8" > /etc/resolv.conf'
# Or configure via interfaces file
sudo incus exec email -- bash -c "cat > /etc/network/interfaces << 'EOF'
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address <email-container-ip>
netmask 255.255.255.0
gateway <gateway>
dns-nameservers 8.8.8.8 8.8.4.4
EOF"
sudo incus restart email
```
3. **If firewall blocking:** Check iptables rules on host for email container IP.
---
### Alert: "Vault sealed"
**Symptoms:** All services failing, Vault health shows "sealed": true
**Quick Fix:**
```bash
# Get unseal keys from secure location (not in git!)
KEY1=<key-from-secure-location>
KEY2=<key-from-secure-location>
KEY3=<key-from-secure-location>
sudo incus exec vault -- vault operator unseal $KEY1
sudo incus exec vault -- vault operator unseal $KEY2
sudo incus exec vault -- vault operator unseal $KEY3
# Verify
sudo incus exec vault -- vault status
```
---
### Alert: "Botserver not responding"
**Quick Diagnostics:**
```bash
# Check process
sudo incus exec system -- pgrep -f botserver || echo "NOT RUNNING"
# Check systemd status
sudo incus exec system -- systemctl status botserver --no-pager
# Check recent logs
sudo incus exec system -- tail -20 /opt/gbo/logs/err.log
# Check for GLIBC errors
sudo incus exec system -- ldd /opt/gbo/bin/botserver | grep "not found"
```
**Quick Fixes:**
1. **If systemd failed:**
```bash
sudo incus exec system -- systemctl restart botserver
sudo incus exec system -- systemctl restart ui
```
2. **If GLIBC mismatch:** Binary compiled with wrong glibc. Must rebuild inside system container (Debian 12, glibc 2.36).
3. **If port conflict:**
```bash
sudo incus exec system -- lsof -i :8080
sudo incus exec system -- killall botserver
sudo incus exec system -- systemctl start botserver
```
---
## Services Detail
Botserver runs as user `gbuser`, binary at `/opt/gbo/bin/botserver`, logs at `/opt/gbo/logs/out.log` and `/opt/gbo/logs/err.log`, systemd unit at `/etc/systemd/system/botserver.service`, env loaded from `/opt/gbo/bin/.env`. Bot BASIC scripts are stored in MinIO Drive under `{bot}.gbai/{bot}.gbdialog/*.bas` and are downloaded/compiled by DriveMonitor to `/opt/gbo/work/{bot}.gbai/{bot}.gbdialog/*.ast`.
The directory service runs Zitadel as user `root`, binary at `/opt/gbo/bin/zitadel`, logs at `/opt/gbo/logs/zitadel.log`, systemd unit at `/etc/systemd/system/directory.service`, and loads environment from the service configuration. Zitadel provides identity management and OAuth2 services for the platform.
Internally, Zitadel listens on port 8080 within the directory container. For external access:
- Via public domain (HTTPS): `https://<login-domain>` (configured through proxy container)
- Via host IP (HTTP): `http://<host-ip>:9000` (direct container port forwarding)
- Via container IP (HTTP): `http://<directory-container-ip>:9000` (direct container access)
Access the Zitadel console at `https://<login-domain>/ui/console` with admin credentials. Zitadel implements v1 Management API (deprecated) and v2 Organization/User services. Always use the v2 endpoints under `/v2/organizations` and `/v2/users` for all operations.
The botserver bootstrap also manages: Vault (secrets), PostgreSQL (database), Valkey (cache, password auth), MinIO (object storage), Zitadel (identity provider), and llama.cpp (LLM).
To obtain a PAT for Zitadel API access, check /opt/gbo/conf/directory/admin-pat.txt in the directory container. Use it with curl by setting the Authorization header: `Authorization: Bearer $(cat /opt/gbo/conf/directory/admin-pat.txt)` and include `-H "Host: <directory-ip> "` for correct host resolution.
---
## Directory Management (Zitadel)
### Getting Admin PAT (Personal Access Token)
```bash
# Get the admin PAT from directory container
PAT=$(ssh administrator@<hostname> "sudo incus exec directory -- cat /opt/gbo/conf/directory/admin-pat.txt")
```
### User Management via API (v2)
**Create a Human User:**
```bash
curl -X POST "http://<directory-ip>:8080/v2/users/human" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PAT" \
-H "Host: <directory-ip>" \
-d '{
"username": "testuser",
"profile": {"givenName": "Test", "familyName": "User"},
"email": {"email": "test@example.com", "isVerified": true},
"password": {"password": "<password>", "changeRequired": false}
}'
```
**List Users:**
```bash
curl -X POST "http://<directory-ip>:8080/v2/users" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PAT" \
-H "Host: <directory-ip>" \
-d '{"query": {"offset": 0, "limit": 100}}'
```
**Update User Password:**
```bash
curl -X POST "http://<directory-ip>:8080/v2/users/<user-id>/password" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PAT" \
-H "Host: <directory-ip>" \
-d '{
"newPassword": {"password": "<password>", "changeRequired": false}
}'
```
**Delete User:**
```bash
curl -X DELETE "http://<directory-ip>:8080/v2/users/<user-id>" \
-H "Authorization: Bearer $PAT" \
-H "Host: <directory-ip>"
```
### Directory Quick Reference
| Task | Command |
|------|---------|
| Get PAT | `sudo incus exec directory -- cat /opt/gbo/conf/directory/admin-pat.txt` |
| Check health | `curl -sf http://<directory-ip>:8080/debug/healthz` |
| Console UI | `http://<host-ip>:9000/ui/console` |
| Create user | `POST /v2/users/human` |
| List users | `POST /v2/users` |
| Update password | `POST /v2/users/{id}/password` |
# /tmp permission denied for build.log
sudo incus exec alm-ci -- chmod 1777 /tmp
sudo incus exec alm-ci -- touch /tmp/build.log && chmod 666 /tmp/build.log
# Clean old CI runs (keep recent)
sudo incus exec tables -- bash -c 'export PGPASSWORD=<postgres-password>; psql -h localhost -U postgres -d PROD-ALM -c "DELETE FROM action_run WHERE id < <RECENT_ID>;"'
sudo incus exec tables -- bash -c 'export PGPASSWORD=<postgres-password>; psql -h localhost -U postgres -d PROD-ALM -c "DELETE FROM action_run_job WHERE run_id < <RECENT_ID>;"'
```
**Watch CI in real-time:**
```bash
# Tail runner logs
sudo incus exec alm-ci -- tail -f /opt/gbo/logs/forgejo-runner.log
# Check if new builds appear
watch -n 5 'sudo incus exec tables -- bash -c "export PGPASSWORD=<postgres-password>; psql -h localhost -U postgres -d PROD-ALM -c \\"SELECT id, status, created FROM action_run ORDER BY id DESC LIMIT 3;\\""'
# Verify botserver deployed correctly
sudo incus exec system -- /opt/gbo/bin/botserver --version 2>&1 | head -3
sudo incus exec system -- tail -5 /opt/gbo/logs/err.log
```
### Monitor CI/CD Build Status
**Check latest build status:**
```bash
# View latest 3 builds with status
sudo incus exec alm -- bash -c 'cd /opt/gbo/data/GeneralBots/BotServer/actions/runs && for dir in $(ls -t | head -3); do echo "=== Build $dir ==="; cat $dir/jobs/0.json 2>/dev/null | grep -E "\"status\"|\"commit\"|\"workflow\"" | head -5; done'
# Watch runner logs in real-time
sudo incus exec alm-ci -- tail -f /opt/gbo/logs/forgejo-runner.log | grep -E "Clone|Build|Deploy|Success|Failure"
```
**Understand build timing:**
- **Rust compilation**: 2-5 minutes (cold build), 30-60 seconds (incremental)
- **Dependencies**: First build downloads ~200 dependencies
- **Deploy step**: ~5 seconds
- **Total CI time**: 2-6 minutes depending on cache
**Verify binary was updated:**
```bash
# Check binary timestamp
ssh administrator@63.141.255.9 "sudo incus exec system -- stat -c '%y' /opt/gbo/bin/botserver"
# Check running version
ssh administrator@63.141.255.9 "sudo incus exec system -- /opt/gbo/bin/botserver --version"
# Check health endpoint
curl -sf https://chat.pragmatismo.com.br/api/health || echo "Health check failed"
```
```
---
## DriveMonitor & Bot Configuration
DriveMonitor is a background service inside botserver that watches MinIO buckets and syncs changes to the local filesystem and database every 10 seconds. It monitors three directory types per bot: the `.gbdialog/` folder for BASIC scripts (downloads and recompiles on change), the `.gbot/` folder for `config.csv` (syncs to the `bot_configuration` database table), and the `.gbkb/` folder for knowledge base documents (downloads and indexes for vector search).
Bot configuration is stored in two PostgreSQL tables inside the `botserver` database. The `bot_configuration` table holds key-value pairs with columns `bot_id`, `config_key`, `config_value`, `config_type`, `is_encrypted`, and `updated_at`. The `gbot_config_sync` table tracks sync state with columns `bot_id`, `config_file_path`, `last_sync_at`, `file_hash`, and `sync_count`.
The `config.csv` format is a plain CSV with no header: each line is `key,value`, for example `llm-provider,groq` or `theme-color1,#cc0000`. DriveMonitor syncs it when the file ETag changes in MinIO, on botserver startup, or after a restart.
**Check config status:** Query `bot_configuration` via `sudo incus exec tables -- psql -h localhost -U postgres -d botserver -c "SELECT config_key, config_value FROM bot_configuration WHERE bot_id = (SELECT id FROM bots WHERE name = '<botname>') ORDER BY config_key;"`. Check sync state via the `gbot_config_sync` table. Inspect the bucket directly with `sudo incus exec drive -- /opt/gbo/bin/mc cat local/<botname>.gbai/<botname>.gbot/config.csv`.
**Debug DriveMonitor:** Monitor live logs with `sudo incus exec system -- tail -f /opt/gbo/logs/out.log | grep -E "(DRIVE_MONITOR|check_gbot|config)"`. An empty `gbot_config_sync` table means DriveMonitor has not synced yet. If no new log entries appear after 30 seconds, the loop may be stuck — restart botserver with systemctl to clear the state.
**Common config issues:** If config.csv is missing from the bucket, create and upload it with `mc cp`. If the database shows stale values, restart botserver to force a fresh sync, or as a temporary fix update the database directly with `UPDATE bot_configuration SET config_value = 'groq', updated_at = NOW() WHERE ...`. To force a re-sync without restarting, copy config.csv over itself with `mc cp local/... local/...` to change the ETag.
---
## MinIO (Drive) Operations
All bot files live in MinIO buckets. Use the `mc` CLI at `/opt/gbo/bin/mc` from inside the `drive` container. The bucket structure per bot is: `{bot}.gbai/` as root, `{bot}.gbai/{bot}.gbdialog/` for BASIC scripts, `{bot}.gbai/{bot}.gbot/` for config.csv, and `{bot}.gbai/{bot}.gbkb/` for knowledge base folders.
Common mc commands: `mc ls local/` lists all buckets; `mc ls local/botname.gbai/` lists a bucket; `mc cat local/.../start.bas` prints a file; `mc cp local/.../file /tmp/file` downloads; `mc cp /tmp/file local/.../file` uploads (this triggers DriveMonitor recompile); `mc stat local/.../config.csv` shows ETag and metadata; `mc mb local/newbot.gbai` creates a bucket; `mc rb local/oldbot.gbai` removes an empty bucket.
If mc is not found, use the full path `/opt/gbo/bin/mc`. If alias `local` is not configured, check with `mc config host list`. If MinIO is not running, check with `sudo incus exec drive -- systemctl status minio`.
---
## Vault Security Architecture
HashiCorp Vault is the single source of truth for all secrets. Botserver reads `VAULT_ADDR` and `VAULT_TOKEN` from `/opt/gbo/bin/.env` at startup, initializes a TLS/mTLS client, then reads credentials from Vault paths. If Vault is unavailable, it falls back to defaults. The `.env` file must only contain `VAULT_*` variables plus `PORT`, `DATA_DIR`, `WORK_DIR`, and `LOAD_ONLY`.
**Global Vault paths:** `gbo/tables` holds PostgreSQL credentials; `gbo/drive` holds MinIO access key and secret; `gbo/cache` holds Valkey password; `gbo/llm` holds LLM URL and API keys; `gbo/directory` holds Zitadel config; `gbo/email` holds SMTP credentials; `gbo/vectordb` holds Qdrant config; `gbo/jwt` holds JWT signing secret; `gbo/encryption` holds the master encryption key. Organization-scoped secrets follow patterns like `gbo/orgs/{org_id}/bots/{bot_id}` and tenant infrastructure uses `gbo/tenants/{tenant_id}/infrastructure`.
**Credential resolution:** For any service, botserver checks the most specific Vault path first (org+bot level), falls back to a default bot path, then falls back to the global path, and only uses environment variables as a last resort in development.
**Verify Vault health:** `sudo incus exec vault -- curl -k -sf https://localhost:8200/v1/sys/health` should return JSON with `"sealed":false`. To read a secret: set `VAULT_ADDR`, `VAULT_TOKEN`, and `VAULT_CACERT` then run `vault kv get secret/gbo/tables`. To test from the system container, use curl with `--cacert /opt/gbo/conf/system/certificates/ca/ca.crt` and `-H "X-Vault-Token: <token>"`.
**init.json** is stored at `/opt/gbo/bin/botserver-stack/conf/vault/vault-conf/init.json` and contains the root token and 5 unseal keys (3 needed to unseal). Never commit this file to git. Store it encrypted in a secure location.
**Vault troubleshooting — cannot connect:** Check that the vault container's systemd unit is running, verify the token in `.env` is not expired with `vault token lookup`, confirm the CA cert path in `.env` matches the actual file location, and test network connectivity from system to vault container. To generate a new token: `vault token create -policy="botserver" -ttl="8760h" -format=json` then update `.env` and restart botserver.
**Get database credentials from Vault v2 API:**
```bash
ssh user@<hostname> "sudo incus exec system -- curl -s --cacert /opt/gbo/conf/system/certificates/ca/ca.crt -H 'X-Vault-Token: <vault-token>' https://<vault-ip>:8200/v1/secret/data/gbo/tables 2>/dev/null"
```
**Vault troubleshooting — secrets missing:** Run `vault kv get secret/gbo/tables` (and other paths) to check if secrets exist. If a path returns NOT FOUND, add secrets with `vault kv put secret/gbo/tables host=<ip> port=5432 database=botserver username=gbuser password=<pw>` and similar for other paths.
**Vault sealed after restart:** Run `vault operator unseal <key1>`, repeat with key2 and key3 (3 of 5 keys from init.json), then verify with `vault status`.
**TLS certificate errors:** Confirm `/opt/gbo/conf/system/certificates/ca/ca.crt` exists in the system container. If missing, copy it from the vault container using `incus file pull vault/opt/gbo/conf/vault/ca.crt /tmp/ca.crt` then place it at the expected path.
**Vault snapshots:** Stop vault, run `sudo incus snapshot create vault backup-$(date +%Y%m%d-%H%M)`, start vault. Restore with `sudo incus snapshot restore vault <name>` while vault is stopped.
---
## DNS Management
### Updating DNS Records
**CRITICAL:** When updating DNS zone files, you MUST:
1. **Update the serial number** in the SOA record (format: YYYYMMDDNN)
2. **Run sync-zones.sh** to propagate changes to secondary nameservers
3. **Anonymize IPs and credentials** in all documentation and logs
**Workflow:**
```bash
# 1. Edit zone file
sudo incus exec dns -- nano /opt/gbo/data/pragmatismo.com.br.zone
# 2. Update serial (YYYYMMDDNN format)
# Example: 2026041801 (April 18, 2026, change #1)
sudo incus exec dns -- sed -i 's/2026041801/2026041802/' /opt/gbo/data/pragmatismo.com.br.zone
# 3. Reload CoreDNS
sudo incus exec dns -- pkill -HUP coredns
# 4. Sync to secondary NS
sudo /opt/gbo/bin/sync-zones.sh
# 5. Verify on secondary
ssh -o StrictHostKeyChecking=no -i /home/administrator/.ssh/id_ed25519 admin@<secondary-ip> 'getent hosts <domain>'
```
**Zone File Location:** `/opt/gbo/data/<domain>.zone` in the `dns` container
**Sync Script:** `/opt/gbo/bin/sync-zones.sh` - copies zone files to secondary NS (3.218.224.38)
**⚠️ Security Rules:**
- NEVER include real IPs in documentation - use `<ip>` or `10.x.x.x`
- NEVER include credentials - use `<password>` or `<token>`
- NEVER commit zone files with secrets to git
---
### Adding New Subdomains (HTTPS with Caddy)
**CRITICAL:** When adding new subdomains that need HTTPS, follow this order:
1. **Add DNS record FIRST** (see above workflow)
2. **Wait for DNS propagation** (can take up to 1 hour)
3. **Add Caddy config** - Caddy will automatically obtain Let's Encrypt certificate
**Complete Workflow:**
```bash
# 1. Add DNS record (update serial, sync zones)
sudo incus exec dns -- nano /opt/gbo/data/pragmatismo.com.br.zone
# Add: news IN A <ip>
sudo incus exec dns -- sed -i 's/2026041801/2026041802/' /opt/gbo/data/pragmatismo.com.br.zone
sudo incus exec dns -- pkill -HUP coredns
sudo /opt/gbo/bin/sync-zones.sh
# 2. Verify DNS propagation (wait until this works)
dig @9.9.9.9 news.pragmatismo.com.br A +short
# 3. Add Caddy config (AFTER DNS is working)
sudo sh -c 'cat >> /opt/gbo/conf/config << EOF
news.pragmatismo.com.br {
import tls_config
reverse_proxy http://<container-ip>:<port> {
header_up Host {host}
header_up X-Real-IP {remote}
header_up X-Forwarded-Proto https
}
}
EOF'
# 4. Restart Caddy
sudo incus exec proxy -- systemctl restart proxy
# 5. Wait for certificate (Caddy will auto-obtain from Let's Encrypt)
# Check logs: sudo incus exec proxy -- tail -f /opt/gbo/logs/access.log
```
**⚠️ Important:**
- Caddy will fail to obtain certificate if DNS is not propagated
- Wait up to 1 hour for DNS propagation before adding Caddy config
- Check Caddy logs for "challenge failed" errors - indicates DNS not ready
- Certificate is automatically renewed by Caddy
---
## Troubleshooting Quick Reference
**botserver won't start:** Run `sudo incus exec system -- ldd /opt/gbo/bin/botserver | grep "not found"` to check for missing libraries. Run `sudo incus exec system -- timeout 10 /opt/gbo/bin/botserver 2>&1` to see startup errors. Confirm `/opt/gbo/work/` exists and is accessible.
**botui can't reach botserver:** Check that the `ui.service` systemd file has `BOTSERVER_URL=http://localhost:5858` — not the external HTTPS URL. Fix with `sed -i 's|BOTSERVER_URL=.*|BOTSERVER_URL=http://localhost:5858|'` on the service file, then `systemctl daemon-reload` and `systemctl restart ui`.
**Suggestions not showing:** Confirm bot `.bas` files exist in MinIO Drive under `{bot}.gbai/{bot}.gbdialog/`. Check logs for compilation errors. Clear the AST cache in `/opt/gbo/work/` and restart botserver.
**IPv6 DNS timeouts on external APIs (Groq, Cloudflare):** The container's DNS may return AAAA records without IPv6 connectivity. The container should have `IPV6=no` in its network config and `gai.conf` set appropriately. Check for `RES_OPTIONS=inet4` in `botserver.service` if issues persist.
**Logs show development paths instead of Drive:** Botserver is using hardcoded dev paths. Check `.env` has `DATA_DIR=/opt/gbo/work/` and `WORK_DIR=/opt/gbo/work/`, verify the systemd unit has `EnvironmentFile=/opt/gbo/bin/.env`, and confirm Vault is reachable so service discovery works. Expected startup log lines include `info watcher:Watching data directory /opt/gbo/work` and `info botserver:BotServer started successfully on port 5858`.
**Migrations not running after push:** If `stat /opt/gbo/bin/botserver` shows old timestamp and `__diesel_schema_migrations` table has no new entries, CI did not rebuild. Make a trivial code change (e.g., add a comment) in botserver and push again to force rebuild.
---
## Drive (MinIO) File Operations Cheatsheet
All `mc` commands run inside the `drive` container with `PATH` set: `sudo incus exec drive -- bash -c 'export PATH=/opt/gbo/bin:$PATH && mc <command>'`. If `local` alias is missing, create it with credentials from Vault path `gbo/drive`.
**List bucket contents recursively:** `mc ls local/<bot>.gbai/ --recursive`
**Read a file from Drive:** `mc cat local/<bot>.gbai/<bot>.gbdialog/start.bas`
**Download a file:** `mc cp local/<bot>.gbai/<bot>.gbdialog/start.bas /tmp/start.bas`
**Upload a file to Drive (triggers DriveMonitor recompile):** Transfer file to host via `scp`, push into drive container with `sudo incus file push /tmp/file drive/tmp/file`, then `mc put /tmp/file local/<bot>.gbai/<bot>.gbdialog/start.bas`
**Full upload workflow example — updating config.csv:**
```bash
# 1. Download current config from Drive
ssh user@host "sudo incus exec drive -- bash -c 'export PATH=/opt/gbo/bin:\$PATH && mc cat local/botname.gbai/botname.gbot/config.csv'" > /tmp/config.csv
# 2. Edit locally (change model, keys, etc.)
sed -i 's/llm-model,old-model/llm-model,new-model/' /tmp/config.csv
# 3. Push edited file back to Drive
scp /tmp/config.csv user@host:/tmp/config.csv
ssh user@host "sudo incus file push /tmp/config.csv drive/tmp/config.csv"
ssh user@host "sudo incus exec drive -- bash -c 'export PATH=/opt/gbo/bin:\$PATH && mc put /tmp/config.csv local/botname.gbai/botname.gbot/config.csv'"
# 4. Wait ~15 seconds, then verify DriveMonitor picked up the change
ssh user@host "sudo incus exec system -- bash -c 'grep -i \"Model:\" /opt/gbo/logs/err.log | tail -3'"
```
**Force re-sync of config.csv** (change ETag without content change): `mc cp local/<bot>.gbai/<bot>.gbot/config.csv local/<bot>.gbai/<bot>.gbot/config.csv`
**Create a new bot bucket:** `mc mb local/newbot.gbai`
**Check MinIO health:** `sudo incus exec drive -- bash -c '/opt/gbo/bin/mc admin info local'`
---
## Logging Quick Reference
**Application logs** (searchable, timestamped, most useful): `sudo incus exec system -- tail -f /opt/gbo/logs/err.log` (errors and debug) or `/opt/gbo/logs/out.log` (stdout). The systemd journal only captures process lifecycle events, not application output.
**Search logs for specific bot activity:** `grep -i "botname\|llm\|Model:\|KB\|USE_KB\|drive_monitor" /opt/gbo/logs/err.log | tail -30`
**Check which LLM model a bot is using:** `grep "Model:" /opt/gbo/logs/err.log | tail -5`
**Check DriveMonitor config sync:** `grep "check_gbot\|config.csv\|should_sync" /opt/gbo/logs/err.log | tail -20`
**Check KB/vector operations:** `grep -i "gbkb\|qdrant\|embedding\|index" /opt/gbo/logs/err.log | tail -20`
**Live tail with filter:** `sudo incus exec system -- bash -c 'tail -f /opt/gbo/logs/err.log | grep --line-buffered -i "botname\|error\|KB"'`
---
## Program Access Cheatsheet
| Program | Container | Path | Notes |
|---------|-----------|------|-------|
| botserver | system | `/opt/gbo/bin/botserver` | Run via systemctl only |
| botui | system | `/opt/gbo/bin/botui` | Run via systemctl only |
| mc (MinIO Client) | drive | `/opt/gbo/bin/mc` | Must set `PATH=/opt/gbo/bin:$PATH` |
| psql | tables | `/usr/bin/psql` | `psql -h localhost -U postgres -d botserver` |
| vault | vault | `/opt/gbo/bin/vault` | Needs `VAULT_ADDR`, `VAULT_TOKEN`, `VAULT_CACERT` |
| zitadel | directory | `/opt/gbo/bin/zitadel` | Runs as root on port 8080 internally |
**Quick psql query — bot config:** `sudo incus exec tables -- psql -h localhost -U postgres -d botserver -c "SELECT config_key, config_value FROM bot_configuration WHERE bot_id = (SELECT id FROM bots WHERE name = 'botname') ORDER BY config_key;"`
**Quick psql query — active KBs for session:** `sudo incus exec tables -- psql -h localhost -U postgres -d botserver -c "SELECT * FROM session_kb_associations WHERE session_id = '<uuid>' AND is_active = true;"`
---
## BASIC Compilation Architecture
Compilation and runtime are now strictly separated. **Compilation** happens only in `BasicCompiler` inside DriveMonitor when it detects `.bas` file changes. The output is a fully preprocessed `.ast` file written to `work/<bot>.gbai/<bot>.gbdialog/<tool>.ast`. **Runtime** (start.bas, TOOL_EXEC, automation, schedule) loads only `.ast` files and calls `ScriptService::run()` which does `engine.compile() + eval_ast_with_scope()` on the already-preprocessed Rhai source — no preprocessing at runtime.
The `.ast` file has all transforms applied: `USE KB "cartas"` becomes `USE_KB("cartas")`, `IF/END IF``if/{ }`, `WHILE/WEND``while/{ }`, `BEGIN TALK/END TALK` → function calls, `SAVE`, `FOR EACH/NEXT`, `SELECT CASE`, `SET SCHEDULE`, `WEBHOOK`, `USE WEBSITE`, `LLM` keyword expansion, variable predeclaration, and keyword lowercasing. Runtime never calls `compile()`, `compile_tool_script()`, or `compile_preprocessed()` — those methods no longer exist.
**Tools (TOOL_EXEC) load `.ast` only** — there is no `.bas` fallback. If an `.ast` file is missing, the tool fails with "Failed to read tool .ast file". DriveMonitor must have compiled it first.
**Suggestion deduplication** uses Redis `SADD` (set) instead of `RPUSH` (list). This prevents duplicate suggestion buttons when `start.bas` runs multiple times per session. The key format is `suggestions:{bot_id}:{session_id}` and `get_suggestions` uses `SMEMBERS` to read it.
---
## Container Quick Reference
| Container | Critical | Check Command | Restart Command |
|-----------|----------|---------------|-----------------|
| system | YES | `systemctl is-active botserver` | `systemctl restart botserver` |
| tables | YES | `pgrep -f postgres` | `systemctl restart postgresql` |
| vault | YES | `curl -ksf https://localhost:8200/v1/sys/health` | `systemctl restart vault` |
| drive | YES | `pgrep -f minio` | `systemctl restart minio` |
| cache | HIGH | `pgrep -f valkey` | `systemctl restart valkey` |
| directory | HIGH | `curl -sf http://localhost:8080/debug/healthz` | `systemctl restart directory` |
| alm-ci | MED | `pgrep -f forgejo` | manual restart |
| llm | MED | `curl -sf http://localhost:8081/health` | `systemctl restart llm` |
| vector_db | LOW | `curl -sf http://localhost:6333/healthz` | `systemctl restart qdrant` |
---
## Log Tailing Commands
```bash
# Live error monitoring
sudo incus exec system -- tail -f /opt/gbo/logs/err.log | grep -i "error\|panic\|failed"
# Bot-specific activity
sudo incus exec system -- tail -f /opt/gbo/logs/err.log | grep -i "<botname>"
# DriveMonitor activity
sudo incus exec system -- tail -f /opt/gbo/logs/err.log | grep -i "drive\|config"
# LLM calls
sudo incus exec system -- tail -f /opt/gbo/logs/err.log | grep -i "model\|llm\|groq"
# CI runner
sudo incus exec alm-ci -- tail -f /opt/gbo/logs/forgejo-runner.log
```
---
## Health Endpoint Monitoring
Set up a simple cron job to alert if health fails:
```bash
# Add to host crontab (crontab -e)
*/5 * * * * curl -sf https://<system-domain>/api/health || echo "ALERT: Health check failed at $(date)" >> /var/log/gbo-health.log
```
---
## Troubleshooting Quick Reference
### Container Won't Start (No IPv4)
**Symptom:** Container shows empty IPV4 column in `sudo incus list`
**Diagnose:**
```bash
sudo incus list <container> -c n4
sudo incus exec <container> -- ip addr show eth0
```
**Fix:**
```bash
# 1. Stop container
sudo incus stop <container>
# 2. Set static IP
sudo incus config device set <container> eth0 ipv4.address <ip-address>
# 3. Configure network inside
sudo incus exec <container> -- bash -c 'cat > /etc/network/interfaces << EOF
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet static
address <ip-address>
netmask 255.255.255.0
gateway <gateway>
dns-nameservers 8.8.8.8 8.8.4.4
EOF'
# 4. Restart
sudo incus restart <container>
# 5. Verify
sudo incus exec <container> -- ip addr show eth0
```
---
### CI/ALM Permission Errors
**Symptom:** `/tmp permission denied` during CI build
**Fix:**
```bash
# On alm-ci container
sudo incus exec alm-ci -- chmod 1777 /tmp
sudo incus exec alm-ci -- touch /tmp/build.log && chmod 666 /tmp/build.log
# Check runner user
sudo incus exec alm-ci -- ls -la /opt/gbo/
# Fix ownership
sudo incus exec alm-ci -- chown -R gbuser:gbuser /opt/gbo/bin/
sudo incus exec alm-ci -- chown -R gbuser:gbuser /opt/gbo/work/
```
**CI Runner Down:**
```bash
sudo incus exec alm-ci -- pkill -9 forgejo
sleep 2
sudo incus exec alm-ci -- bash -c 'cd /opt/gbo/bin && nohup ./forgejo-runner daemon --config config.yaml >> /opt/gbo/logs/forgejo-runner.log 2>&1 &'
```
---
### MinIO (Drive) Operations with `mc`
**Setup:**
```bash
# Access drive container
sudo incus exec drive -- bash
# Set PATH
export PATH=/opt/gbo/bin:$PATH
# Verify mc works
mc --version
```
**Common Commands:**
```bash
# List all buckets
mc ls local/
# List bot bucket
mc ls local/<botname>.gbai/
# Read start.bas
mc cat local/<botname>.gbai/<botname>.gbdialog/start.bas
# Download file
mc cp local/<botname>.gbai/<botname>.gbdialog/config.csv /tmp/config.csv
# Upload file (triggers DriveMonitor)
mc cp /tmp/config.csv local/<botname>.gbai/<botname>.gbot/config.csv
# Force re-sync (change ETag)
mc cp local/<bot>.gbai/<bot>.gbot/config.csv local/<bot>.gbai/<bot>.gbot/config.csv
# Create new bucket
mc mb local/newbot.gbai
# Check MinIO health
mc admin info local
```
**If `local` alias missing:**
```bash
# Create alias
mc alias set local http://localhost:9000 <access-key> <secret-key>
```
---
### Forgejo ALM Database Operations
**Access ALM database (PROD-ALM):**
```bash
# On tables container
sudo incus exec tables -- psql -h localhost -U postgres -d PROD-ALM
```
**Common Queries:**
```sql
-- Check CI runs
SELECT id, status, commit_sha, created FROM action_run ORDER BY id DESC LIMIT 10;
-- Status codes: 0=pending, 1=success, 2=failure, 3=cancelled, 6=running
-- Check specific run jobs
SELECT id, status, name FROM action_run_job WHERE run_id = <ID>;
-- Reset stuck run
UPDATE action_task SET status = 0 WHERE id = <ID>;
UPDATE action_run_job SET status = 0 WHERE run_id = <RUN_ID>;
UPDATE action_run SET status = 0 WHERE id = <RUN_ID>;
-- Check runner token
SELECT * FROM action_runner_token;
-- List runners
SELECT * FROM action_runner;
```
**Check CI from host:**
```bash
export PGPASSWORD=<password>
sudo incus exec tables -- psql -h localhost -U postgres -d PROD-ALM -c "SELECT id, status, created FROM action_run ORDER BY id DESC LIMIT 5;"
```
---
### Zitadel API v2 Operations
**Important:** Always use **v2 API** - v1 is deprecated and non-functional.
**Get PAT:**
```bash
PAT=$(sudo incus exec directory -- cat /opt/gbo/conf/directory/admin-pat.txt)
```
**Common Operations:**
**Create User (v2):**
```bash
curl -X POST "http://<directory-ip>:8080/v2/users/human" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PAT" \
-H "Host: <directory-ip>" \
-d '{
"username": "newuser",
"profile": {"givenName": "New", "familyName": "User"},
"email": {"email": "user@example.com", "isVerified": true},
"password": {"password": "<password>", "changeRequired": false}
}'
```
**List Users (v2):**
```bash
curl -X POST "http://<directory-ip>:8080/v2/users" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PAT" \
-H "Host: <directory-ip>" \
-d '{"query": {"offset": 0, "limit": 100}}'
```
**Create Organization (v2):**
```bash
curl -X POST "http://<directory-ip>:8080/v2/organizations" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PAT" \
-H "Host: <directory-ip>" \
-d '{"name": "organization-name"}'
```
**Add Domain to Org (v2):**
```bash
curl -X POST "http://<directory-ip>:8080/v2/organizations/<org-id>/domains" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $PAT" \
-H "Host: <directory-ip>" \
-d '{"domainName": "example.com"}'
```
**⚠️ Critical:** Always include `-H "Host: <directory-ip>"` header or API returns 404.
---
### Common Errors & Quick Fixes
| Error | Cause | Fix |
|-------|-------|-----|
| `No IPv4 on container` | DHCP failed | Set static IP (see above) |
| `/tmp permission denied` | Wrong permissions | `chmod 1777 /tmp` |
| `Errors.Token.Invalid (AUTH-7fs1e)` | Zitadel PAT expired | Regenerate via console |
| `failed SASL auth` | Wrong DB password | Check Vault credentials |
| `GLIBC_2.39 not found` | Wrong build environment | Rebuild in system container |
| `connection refused` | Service down | `systemctl restart <service>` |
| `exec format error` | Architecture mismatch | Recompile for target arch |
| `address already in use` | Port conflict | `lsof -i :<port>` |
| `certificate verify failed` | Wrong CA cert | Copy from vault container |
| `DNS lookup failed` | No IPv4 connectivity | Check network config |
---
## Contact Escalation
If quick fixes don't work:
1. Capture logs: `sudo incus exec system -- tar czf /tmp/debug-$(date +%Y%m%d).tar.gz /opt/gbo/logs/`
2. Check AGENTS.md for development troubleshooting
3. Review recent commits for breaking changes
4. Consider snapshot rollback (last resort)

743
README.md
View file

@ -1,27 +1,8 @@
RULE 0: Never call tool_call while thinking. Ex NEVER do this: Let me check if the API call succeeded:<tool_call>terminal<arg_key>command</arg_key><arg_value>tail -50 botserver.log | grep -E "LLM streaming error|error|Error|SUCCESS|200"</arg_value><arg_key>cd</arg_key><arg_value>gb</arg_value></tool_call>. First finish Thinking, then emit a explanation and tool!
# General Bots Workspace
## ⚠️ CRITICAL SECURITY WARNING
**NEVER CREATE FILES WITH SECRETS IN THE REPOSITORY ROOT**
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)
**Files added to .gitignore:** `vault-unseal-keys`, `start-and-unseal.sh`, `vault-token-*`
**Why `/tmp/`?**
- Cleared on reboot (ephemeral)
- Not tracked by git
- Standard Unix security practice
- Prevents accidental commits
---
**Version:** 6.3.0
**Version:** 6.2.0
**Type:** Rust Workspace (Monorepo with Independent Subproject Repos)
---
@ -38,7 +19,7 @@ For comprehensive documentation, see **[docs.pragmatismo.com.br](https://docs.pr
| Crate | Purpose | Port | Tech Stack |
|-------|---------|------|------------|
| **botserver** | Main API server, business logic | 9000 | Axum, Diesel, Rhai BASIC |
| **botserver** | Main API server, business logic | 8088 | 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 |
@ -54,35 +35,6 @@ For comprehensive documentation, see **[docs.pragmatismo.com.br](https://docs.pr
- **Env file:** `botserver/.env`
- **Stack:** `botserver-stack/`
- **UI Files:** `botui/ui/suite/`
- **Local Bot Data:** `/opt/gbo/data/` (place `.gbai` packages here)
### Local Bot Data Directory
Place local bot packages in `/opt/gbo/data/` for automatic loading and monitoring:
**Directory Structure:**
```
/opt/gbo/data/
└── mybot.gbai/
├── mybot.gbdialog/
│ ├── start.bas
│ └── main.bas
└── mybot.gbot/
└── config.csv
```
**Features:**
- **Auto-loading:** Bots automatically mounted on server startup
- **Auto-compilation:** `.bas` files compiled to `.ast` on change
- **Auto-creation:** New bots automatically added to database
- **Hot-reload:** Changes trigger immediate recompilation
- **Monitored by:** LocalFileMonitor and ConfigWatcher services
**Usage:**
1. Create bot directory structure in `/opt/gbo/data/`
2. Add `.bas` files to `<bot_name>.gbai/<bot_name>.gbdialog/`
3. Server automatically detects and loads the bot
4. Optional: Add `config.csv` for bot configuration
---
@ -92,36 +44,15 @@ Place local bot packages in `/opt/gbo/data/` for automatic loading and monitorin
BotServer automatically installs, configures, and manages all infrastructure components on first run. **DO NOT manually start these services** - BotServer handles everything.
**Automatic Service Lifecycle:**
1. **Start**: When botserver starts, it automatically launches all infrastructure components (PostgreSQL, Vault, MinIO, Valkey, Qdrant, etc.)
2. **Credentials**: BotServer retrieves all service credentials (passwords, tokens, API keys) from Vault
3. **Connection**: BotServer uses these credentials to establish secure connections to each service
4. **Query**: All database queries, cache operations, and storage requests are authenticated using Vault-managed credentials
**Credential Flow:**
```
botserver starts
Launch PostgreSQL, MinIO, Valkey, Qdrant
Connect to Vault
Retrieve service credentials (from database)
Authenticate with each service using retrieved credentials
Ready to handle requests
```
| Component | Purpose | Port | Binary Location | Credentials From |
|-----------|---------|------|-----------------|------------------|
| **Vault** | Secrets management | 8200 | `botserver-stack/bin/vault/vault` | Auto-unsealed |
| **PostgreSQL** | Primary database | 5432 | `botserver-stack/bin/tables/bin/postgres` | Vault → database |
| **MinIO** | Object storage (S3-compatible) | 9000/9001 | `botserver-stack/bin/drive/minio` | Vault → database |
| **Zitadel** | Identity/Authentication | 8300 | `botserver-stack/bin/directory/zitadel` | Vault → database |
| **Qdrant** | Vector database (embeddings) | 6333 | `botserver-stack/bin/vector_db/qdrant` | Vault → database |
| **Valkey** | Cache/Queue (Redis-compatible) | 6379 | `botserver-stack/bin/cache/valkey-server` | Vault → database |
| **Llama.cpp** | Local LLM server | 8081 | `botserver-stack/bin/llm/build/bin/llama-server` | Vault → database |
| Component | Purpose | Port | Binary Location | Managed By |
|-----------|---------|------|-----------------|------------|
| **Vault** | Secrets management | 8200 | `botserver-stack/bin/vault/vault` | botserver |
| **PostgreSQL** | Primary database | 5432 | `botserver-stack/bin/tables/bin/postgres` | botserver |
| **MinIO** | Object storage (S3-compatible) | 9000/9001 | `botserver-stack/bin/drive/minio` | botserver |
| **Zitadel** | Identity/Authentication | 8300 | `botserver-stack/bin/directory/zitadel` | botserver |
| **Qdrant** | Vector database (embeddings) | 6333 | `botserver-stack/bin/vector_db/qdrant` | botserver |
| **Valkey** | Cache/Queue (Redis-compatible) | 6379 | `botserver-stack/bin/cache/valkey-server` | botserver |
| **Llama.cpp** | Local LLM server | 8081 | `botserver-stack/bin/llm/build/bin/llama-server` | botserver |
### 📦 Component Installation System
@ -340,12 +271,9 @@ cd botserver-stack/bin/cache && ./valkey-cli ping
The script handles BOTH servers properly:
1. Stop existing processes cleanly
2. Build botserver and botui sequentially (no race conditions)
3. Start botserver in background → **automatically starts all infrastructure services (PostgreSQL, Vault, MinIO, Valkey, Qdrant)**
4. BotServer retrieves credentials from Vault and authenticates with all services
5. Start botui in background → proxy to botserver
6. Show process IDs and monitoring commands
**Infrastructure services are fully automated - no manual configuration required!**
3. Start botserver in background → auto-bootstrap infrastructure
4. Start botui in background → proxy to botserver
5. Show process IDs and monitoring commands
**Monitor startup:**
```bash
@ -354,7 +282,7 @@ tail -f botserver.log botui.log
**Access:**
- Web UI: http://localhost:3000
- API: http://localhost:9000
- API: http://localhost:8088
### 📊 Monitor & Debug
@ -378,7 +306,7 @@ grep -E " E |W |CLIENT:" botserver.log | tail -20
```bash
cd botserver && cargo run -- --noconsole > ../botserver.log 2>&1 &
cd botui && BOTSERVER_URL="http://localhost:9000" cargo run > ../botui.log 2>&1 &
cd botui && BOTSERVER_URL="http://localhost:8088" cargo run > ../botui.log 2>&1 &
```
### 🛑 Stop Servers
@ -396,7 +324,7 @@ rm -rf botserver-stack/data/vault botserver-stack/conf/vault/init.json && ./rest
**Port in use?** Find and kill:
```bash
lsof -ti:9000 | xargs kill -9
lsof -ti:8088 | xargs kill -9
lsof -ti:3000 | xargs kill -9
```
@ -407,59 +335,8 @@ All infrastructure services (PostgreSQL, Vault, Redis, Qdrant, MinIO, etc.) are
- **Configurations:** `botserver-stack/conf/`
- **Data storage:** `botserver-stack/data/`
- **Service logs:** `botserver-stack/logs/` (check here for troubleshooting)
- **Credentials:** Stored in Vault, retrieved by botserver at startup
**Do NOT install or reference global PostgreSQL, Redis, or other services.** When botserver starts, it automatically:
1. Launches all required stack services
2. Connects to Vault
3. Retrieves credentials from the `bot_configuration` database table
4. Authenticates with each service using retrieved credentials
5. Begins handling requests with authenticated connections
If you encounter service errors, check the individual service logs in `./botserver-stack/logs/[service]/` directories.
### UI File Deployment - Production Options
**Option 1: Embedded UI (Recommended for Production)**
The `embed-ui` feature compiles UI files directly into the botui binary, eliminating the need for separate file deployment:
```bash
# Build with embedded UI files
cargo build --release -p botui --features embed-ui
# The binary now contains all UI files - no additional deployment needed!
# The botui binary is self-contained and production-ready
```
**Benefits of embed-ui:**
- ✅ Single binary deployment (no separate UI files)
- ✅ Faster startup (no filesystem access)
- ✅ Smaller attack surface
- ✅ Simpler deployment process
**Option 2: Filesystem Deployment (Development Only)**
For development, UI files are served from the filesystem:
```bash
# UI files must exist at botui/ui/suite/
# This is automatically available in development builds
```
**Option 3: Manual File Deployment (Legacy)**
If you need to deploy UI files separately (not recommended):
```bash
# Deploy UI files to production location
./botserver/deploy/deploy-ui.sh /opt/gbo
# Verify deployment
ls -la /opt/gbo/bin/ui/suite/index.html
```
See `botserver/deploy/README.md` for deployment scripts.
**Do NOT install or reference global PostgreSQL, Redis, or other services.** When botserver starts, it automatically launches all required stack services. If you encounter service errors, check the individual service logs in `./botserver-stack/logs/[service]/` directories.
### Start Both Servers (Automated)
```bash
@ -473,7 +350,7 @@ See `botserver/deploy/README.md` for deployment scripts.
cd botserver && cargo run -- --noconsole
# Terminal 2: botui
cd botui && BOTSERVER_URL="http://localhost:9000" cargo run
cd botui && BOTSERVER_URL="http://localhost:8088" cargo run
```
### Build Commands
@ -490,11 +367,234 @@ cargo test -p bottest
---
## 🤖 AI Agent Guidelines
## 🧭 LLM Navigation Guide
> **For LLM instructions, coding rules, security directives, testing workflows, and error handling patterns, see [AGENTS.md](./AGENTS.md).**
### Quick Context Jump
- [Primary Purpose](#overview) - Unified workspace for AI automation platform
- [Crate Structure](#-workspace-structure) - 9 independent crates with shared libraries
- [Dependencies](#-component-dependency-graph) - How crates depend on each other
- [Quick Start](#quick-start) - Get running in 2 commands
- [Error Patterns](#common-error-patterns) - Fix compilation errors efficiently
- [Security Rules](#-security-directives---mandatory) - MUST-FOLLOW security patterns
- [Code Patterns](#-mandatory-code-patterns) - Required coding conventions
- [Testing](#testing-strategy) - How to test changes
- [Debugging](#debugging-guide) - Troubleshoot common issues
---
### Reading This Workspace
**For LLMs analyzing this codebase:**
1. Start with [Component Dependency Graph](#-component-dependency-graph) to understand relationships
2. Review [Module Responsibility Matrix](#-module-responsibility-matrix) for what each module does
3. Study [Data Flow Patterns](#-data-flow-patterns) to understand execution flow
4. Reference [Common Architectural Patterns](#-common-architectural-patterns) before making changes
5. Check [Security Rules](#-security-directives---mandatory) - violations are blocking issues
6. Follow [Code Patterns](#-mandatory-code-patterns) - consistency is mandatory
**For Humans working on this codebase:**
1. Follow [Error Fixing Workflow](#-error-fixing-workflow) for compilation errors
2. Observe [File Size Limits](#-file-size-limits---mandatory) - max 450 lines per file
3. Run [Weekly Maintenance Tasks](#-weekly-maintenance-tasks) to keep codebase healthy
4. Read project-specific READMEs in [Project-Specific Guidelines](#-project-specific-guidelines)
## 🧪 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`
- **Database**: Uses test database, automatically migrates on first run
### Test Utilities Available
- **TestAppStateBuilder** (`bottest/src/harness.rs`) - Build test state with mocked components
- **TestBot** (`bottest/src/bot/mod.rs`) - Mock bot for testing
- **Test Database**: Auto-created, migrations run automatically
### Coverage Goals
- **Critical paths**: 80%+ coverage required
- **Error handling**: ALL error paths must have tests
- **Security**: All security guards must have tests
## 🚨 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**
Examples of errors that MUST be fixed immediately:
- Database connection errors
- Component initialization failures
- Service startup errors
- Configuration errors
- Any error containing "Error:", "Failed:", "Cannot", "Unable"
## 🐛 Debugging Guide
### 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 |
| **script execution** | In botserver logs with `[ScriptService]` prefix | BASIC compilation, runtime errors |
| **client errors** | In botserver logs with `CLIENT:` prefix | JavaScript errors, navigation events |
### Client-Side Logging
**Navigation Tracking:** All client-side navigation is logged to botserver.log with `CLIENT:` prefix:
```
CLIENT:NAVIGATION: click: home -> drive
CLIENT:NAVIGATION: hashchange: drive -> chat
```
**Error Reporting:** JavaScript errors automatically appear in server logs:
```
CLIENT:ERROR: Uncaught TypeError: Cannot read property 'x' of undefined at /suite/js/app.js:123
```
**For LLM Troubleshooting:** ALWAYS check both:
1. `botserver.log` - Server errors + client navigation/errors (prefixed with `CLIENT:`)
2. `botui.log` - UI server logs
### USE WEBSITE Feature - Vector DB Context Injection
**FIXED (v6.2.0+):** The `USE WEBSITE` BASIC command now properly injects vector database embeddings into chat context.
**How it works:**
1. **Preprocessing:** When a `.bas` file containing `USE WEBSITE "https://..."` is compiled, the website is registered for crawling
2. **Crawling:** Content is extracted, chunked, and embedded into Qdrant vector DB (collection name: `website_<url_hash>`)
3. **Runtime Association:** The compiled `.ast` file contains `USE_WEBSITE()` function call that creates session-website association
4. **Context Injection:** During chat, `inject_kb_context()` searches active websites' embeddings and includes relevant chunks in LLM prompt
**Example BASIC script:**
```basic
USE WEBSITE "https://docs.pragmatismo.com.br" REFRESH "1h"
TALK "Hello! I can now answer questions about the documentation."
```
**Database tables involved:**
- `session_website_associations` - Links sessions to websites
- `website_embeddings` - Stores crawled content vectors in Qdrant
**Verification:**
```sql
-- Check if website is associated with session
SELECT * FROM session_website_associations WHERE session_id = '<uuid>';
-- Check if embeddings exist in Qdrant (via HTTP API)
curl http://localhost:6333/collections/website_<hash>/points/scroll
```
**Previous Issue:** In earlier versions, `USE WEBSITE` was removed during preprocessing and never executed at runtime, preventing context injection. Now the function call is preserved in the compiled AST.
### Common Error Messages
| Error | Meaning | Fix |
|-------|---------|-----|
| `Session not found` | Invalid session_id in request | Check auth flow, verify session exists in DB |
| `Bot not found` | Invalid bot_name or bot_id | Verify bot exists in `bots` table |
| `Script compilation error` | BASIC syntax error in .bas file | Check .bas file syntax, look for typos |
| `Failed to send TALK message` | WebSocket disconnected | Check client connection, verify web_adapter running |
| `Drive sync failed` | S3 connection or permission issue | Verify S3 credentials, check bucket exists |
| `unwrap() called on None value` | Panic in production code | MUST FIX - replace with proper error handling |
| `Component not responding: <component_name>` | Infrastructure component not accessible | Check component status: `ps aux | grep <component>`. View logs: `tail -f botserver-stack/logs/<component>/`. Restart via ./restart.sh |
| `Config key not found: <key>` | Missing configuration in database | Check `bot_configuration` table. Set correct value via API or direct SQL update. |
| `403 Forbidden on POST /api/client-errors` | RBAC blocking client error reporting | FIXED in v6.2.0+ - endpoint now allows anonymous access |
### Useful Debugging Commands
```bash
# Check if botserver is running
ps aux | grep botserver
# View botserver logs in real-time
tail -f botserver/logs/botserver.log
# Check work directory structure
ls -la ./work/*.gbai/*/
# Test database connection
cd botserver && cargo run --bin botserver -- --test-db
# Run specific test with output
cargo test -p botserver test_name -- --nocapture
# Check for memory leaks during compilation
CARGO_BUILD_JOBS=1 cargo check -p botserver 2>&1 | grep -i error
```
### Troubleshooting Workflows
**Problem: Script not executing**
1. Check if .bas file exists in `./work/{bot_name}.gbai/{bot_name}.gbdialog/`
2. Verify file has correct syntax (compile with ScriptService)
3. Check logs for compilation errors
4. Verify drive_monitor is running and syncing files
**Problem: WebSocket messages not received**
1. Check browser console for WebSocket errors
2. Verify session_id is valid in database
3. Check web_adapter is registered for session
4. Look for TALK execution in botserver logs
**Problem: Component not starting or crashing**
1. Identify the component from error message (e.g., Vault, PostgreSQL, MinIO, Qdrant, Valkey)
2. Check if process is running: `ps aux | grep <component_name>`
3. Check component logs: `tail -f botserver-stack/logs/<component_name>/`
4. Common fixes:
- Config error: Check `botserver-stack/conf/<component_name>/` for valid configuration
- Port conflict: Ensure no other process using the component's port
- Permission error: Check file permissions in `botserver-stack/data/<component_name>/`
- Missing binary: Re-run `./reset.sh && ./restart.sh` to reinstall components
5. Restart: `./restart.sh`
**Problem: Component configuration errors**
1. All component configs stored in database `bot_configuration` table
2. Check current value: `SELECT * FROM bot_configuration WHERE config_key = '<key_name>';`
3. Update incorrect config: `UPDATE bot_configuration SET config_value = '<correct_value>' WHERE config_key = '<key_name>';`
4. For path configs: Ensure paths are relative to component binary or absolute
5. Restart botserver after config changes
**Problem: File not found errors**
1. Check if file exists in expected location
2. Verify config paths use correct format (relative/absolute)
3. Check file permissions: `ls -la <file_path>`
4. For model/data files: Ensure downloaded to `botserver-stack/data/<component>/`
**Problem: LLM not responding**
1. Check LLM API credentials in config
2. Verify API key has available quota
3. Check network connectivity to LLM provider
4. Review request/response logs for API errors
### Performance Profiling
```bash
# Profile compilation time
cargo build --release --timings
# Profile runtime performance
cargo flamegraph --bin botserver
# Check binary size
ls -lh target/release/botserver
# Memory usage
valgrind --leak-check=full target/release/botserver
```
## 📖 Glossary
@ -514,7 +614,284 @@ cargo test -p bottest
---
## 🔥 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
```
### Common Error Patterns
| Error | Fix |
|-------|-----|
| `expected i64, found u64` | `value as i64` |
| `expected Option<T>, found T` | `Some(value)` |
| `expected T, found Option<T>` | `value.unwrap_or(default)` |
| `cannot multiply f32 by f64` | `f64::from(f32_val) * f64_val` |
| `no field X on type Y` | Check struct definition |
| `no variant X found` | Check enum definition |
| `function takes N arguments` | Match function signature |
| `cannot find function` | Add missing function or fix import |
| `unused variable` | Delete or use with `..` in patterns |
| `unused import` | Delete the import line |
| `cannot move out of X because borrowed` | Use scoping `{ }` to limit borrow |
---
## 🧠 Memory Management
When compilation fails due to memory issues (process "Killed"):
```bash
pkill -9 cargo; pkill -9 rustc; pkill -9 botserver
CARGO_BUILD_JOBS=1 cargo check -p botserver 2>&1 | tail -200
```
---
## 📏 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**
### Current Files Requiring Immediate Refactoring
| File | Lines | Target Split |
|------|-------|--------------|
| `botserver/src/drive/mod.rs` | 1522 | → 4 files |
| `botserver/src/auto_task/app_generator.rs` | 2981 | → 7 files |
| `botui/ui/suite/sheet/sheet.js` | 3220 | → 8 files |
| `botserver/src/tasks/mod.rs` | 2651 | → 6 files |
| `botserver/src/learn/mod.rs` | 2306 | → 5 files |
See `TODO-refactor1.md` for detailed refactoring plans.
---
## 🔍 Continuous Monitoring
**YOLO Forever Monitoring Pattern:**
The system includes automated log monitoring to catch errors in real-time:
```bash
# Continuous monitoring (check every 5 seconds)
while true; do
sleep 5
echo "=== Check at $(date +%H:%M:%S) ==="
tail -50 botserver.log | grep -E "ERROR|WARN|CLIENT:" | tail -5 || echo "✓ Clean"
done
```
**Quick Status Check:**
```bash
# Check last 200 lines for any issues
tail -200 botserver.log | grep -E "ERROR|WARN|CLIENT:" | tail -10
# Show recent server activity
tail -30 botserver.log
# Check if server is running
ps aux | grep botserver | grep -v grep
```
**Monitoring Dashboard:**
- **Server Status**: https://localhost:8088 (health endpoint)
- **Logs**: `tail -f botserver.log`
- **Client Errors**: Look for `CLIENT:` prefix
- **Server Errors**: Look for `ERROR` or `WARN` prefixes
**Status Indicators:**
- ✅ **Clean**: No ERROR/WARN/CLIENT: entries in logs
- ⚠️ **Warnings**: Non-critical issues that should be reviewed
- ❌ **Errors**: Critical issues requiring immediate attention
**When Errors Appear:**
1. Capture the full error context (50 lines before/after)
2. Identify the component (server, client, database, etc.)
3. Check troubleshooting section for specific fixes
4. Update this README with discovered issues and resolutions
---
## 🚀 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 to find and resolve duplicate versions.
- Run `cargo machete` to remove unused dependencies.
- Use `default-features = false` and explicitly opt-in to needed features.
### Memory Optimization
- **Strings**: Prefer `&str` over `String` where possible. Use `Cow<str>` for conditional ownership.
- **Collections**: Use `Vec::with_capacity` when size is known. Consider `SmallVec` for hot paths.
- **Allocations**: Minimize heap allocations in hot paths.
- **Cloning**: Avoid unnecessary `.clone()` calls. Use references or `Cow` types.
### Code Quality Issues Found
- **955 instances** of `unwrap()`/`expect()` in codebase - ALL must be replaced with proper error handling
- **12,973 instances** of excessive `clone()`/`to_string()` calls - optimize for performance
- **Test code exceptions**: `unwrap()` allowed in test files only
### Linting & Code Quality
- **Clippy**: Code MUST pass `cargo clippy --all-targets --all-features` with **0 warnings**.
- **No Allow**: Do not use `#[allow(clippy::...)]` unless absolutely necessary and documented. Fix the underlying issue.
---
## 🔐 Security Directives - MANDATORY
### Error Handling - NO PANICS IN PRODUCTION
```rust
// ❌ 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()) }
```
### Command Execution - USE SafeCommand
```rust
// ❌ FORBIDDEN
Command::new("some_command").arg(user_input).output()
// ✅ REQUIRED
use crate::security::command_guard::SafeCommand;
SafeCommand::new("allowed_command")?
.arg("safe_arg")?
.execute()
```
### Error Responses - USE ErrorSanitizer
```rust
// ❌ 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)
```
### SQL - USE sql_guard
```rust
// ❌ 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)?;
```
---
## ❌ Absolute Prohibitions
```
❌ NEVER use .unwrap() or .expect() in production code (tests OK)
❌ 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 add comments - code must be self-documenting
❌ NEVER modify Cargo.toml lints section!
❌ NEVER use CDN links - all assets must be local
❌ NEVER use cargo clean - causes 30min rebuilds, use ./reset.sh for database issues
❌ NEVER create .md documentation files without checking botbook/ first - documentation belongs there
```
---
## ✅ Mandatory Code Patterns
### Use Self in Impl Blocks
```rust
impl MyStruct {
fn new() -> Self { Self { } } // ✅ Not MyStruct
}
```
### Derive Eq with PartialEq
```rust
#[derive(PartialEq, Eq)] // ✅ Always both
struct MyStruct { }
```
### Inline Format Args
```rust
format!("Hello {name}") // ✅ Not format!("{}", name)
```
### Combine Match Arms
```rust
match x {
A | B => do_thing(), // ✅ Combine identical arms
C => other(),
}
```
---
## 🖥️ UI Architecture (botui + botserver)
@ -523,13 +900,13 @@ cargo test -p bottest
| Server | Port | Purpose |
|--------|------|---------|
| **botui** | 3000 | Serves UI files + proxies API to botserver |
| **botserver** | 9000 | Backend API + embedded UI fallback |
| **botserver** | 8088 | Backend API + embedded UI fallback |
### How It Works
```
Browser → localhost:3000 → botui (serves HTML/CSS/JS)
→ /api/* proxied to botserver:9000
→ /api/* proxied to botserver:8088
→ /suite/* served from botui/ui/suite/
```
@ -652,50 +1029,50 @@ cargo audit
- `ADDITIONAL-SUGGESTIONS.md` - Enhancement ideas
- `TODO-*.md` - Task tracking files
Subprojects (botapp, botserver, botui, etc.) are **independent repositories referenced as git submodules**.
### ⚠️ CRITICAL: Submodule Push Workflow
When making changes to any submodule (botserver, botui, botlib, etc.):
1. **Commit and push changes within the submodule directory:**
```bash
cd botserver
git add .
git commit -m "Your changes"
git push pragmatismo main
git push github main
```
2. **Update the global gb repository submodule reference:**
```bash
cd .. # Back to gb root
git add botserver
git commit -m "Update botserver submodule to latest commit"
git push pragmatismo main
git push github main
```
**Failure to push the global gb repository will cause submodule changes to not trigger CI/CD pipelines.**
Both repositories must be pushed for changes to take effect in production.
Subprojects (botapp, botserver, etc.) are **not** git submodules - they are independent repositories.
---
## Development Workflow
1. Read this README.md (workspace structure)
2. Read **[AGENTS.md](./AGENTS.md)** (coding rules & workflows)
3. **BEFORE creating any .md file, search botbook/ for existing documentation**
4. Read `<project>/README.md` (project-specific rules)
5. Use diagnostics tool to check warnings
6. Fix all warnings with full file rewrites
7. Verify with diagnostics after each file
8. Never suppress warnings with `#[allow()]`
1. Read this README.md (workspace-level rules)
2. **BEFORE creating any .md file, search botbook/ for existing documentation**
3. Read `<project>/README.md` (project-specific rules)
4. Use diagnostics tool to check warnings
5. Fix all warnings with full file rewrites
6. Verify with diagnostics after each file
7. Never suppress warnings with `#[allow()]`
---
## Main Directive
**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
- **ZERO WARNINGS, ZERO ERRORS** - The only acceptable state
- **FIX, DON'T SUPPRESS** - No #[allow()], no Cargo.toml lint exceptions
- **SECURITY FIRST** - No unwrap, no raw errors, no direct commands
- **READ BEFORE FIX** - Always understand context first
- **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
- **Version 6.2.0** - Do not change without approval
- **GIT WORKFLOW** - ALWAYS push to ALL repositories (github, pragmatismo)
---
## License

2
botapp

@ -1 +1 @@
Subproject commit 0b556948f970832e8606f886853793e2bc8dc35c
Subproject commit b5ee6e061acf1388aef777ddcd9a2bf84bd6ed57

@ -1 +1 @@
Subproject commit 82a236f369e58fe0eda4df704b9ee74a725874e8
Subproject commit 85696bb9070738f6bb865202f8c7de733f7c731a

@ -1 +1 @@
Subproject commit 35411f4f9e64e54b1039360ab654d537cd2958c9
Subproject commit 97778e06dd804be55ff761c7fe2788af0ef50626

2
botlib

@ -1 +1 @@
Subproject commit e7caed45a44ab319c64d90f84281dbdbcba905b7
Subproject commit 2765fa2ebadc91435e8d90f068b4c96dbb77329b

@ -1 +1 @@
Subproject commit 4518bf7bd1c4ae6fafd5c42fa5f4515a383b480a
Subproject commit 22a1954fac2f87a0a13b5e599771273172afc73a

@ -1 +1 @@
Subproject commit 1727e48307fdb7b54c726af8cd6b12669764e908
Subproject commit 17a3caebabddbe843c2b7fd93f624b0ccd9c44fb

@ -1 +1 @@
Subproject commit 2ff68e5a2a24b7b63386efd2e2325d478efbb925
Subproject commit 30345c66e2738ebe73d896841e54f655999e3630

@ -1 +1 @@
Subproject commit 3110dd587290047f283300d674ad325f4f9b3046
Subproject commit dd3d8c74dd58a1cc6d6b18d22108819519aaf9c3

@ -1 +1 @@
Subproject commit 346120cb0b916f72abd2fdad577ae1c606aba1a2
Subproject commit 74e761de0dd5105885acf00183223a702a8436df

2
botui

@ -1 +1 @@
Subproject commit ed4d492e6e7863923491a846a3614de6a6c37946
Subproject commit 414d277ae1757834d2ddbd6225063b451e919788

View file

@ -0,0 +1,20 @@
{
"base_url": "http://localhost:8300",
"default_org": {
"id": "358572039839154190",
"name": "default",
"domain": "default.localhost"
},
"default_user": {
"id": "admin",
"username": "admin",
"email": "admin@localhost",
"password": "",
"first_name": "Admin",
"last_name": "User"
},
"admin_token": "eW0mGnOlKjpYHsrZZNAh1o3_8qeyF1iKKgEj-Y63GBdjQbQmxKxEjsNmVLZ_DWRDK6I3_yI",
"project_id": "",
"client_id": "358572040510308366",
"client_secret": "WyZRbj5iMkOkbvvtJWivXVaaydhWX1TodavhnAhsivl8IDZ44v2QoqT5upfgmOfz"
}

View file

@ -1,771 +0,0 @@
# BotServer Microservices Partition Plan
## 🎯 Objective
Partition the monolithic `botserver` into multiple microservices as separate Rust crates that share state, making the system easier to compile, maintain, and scale.
## 📊 Current State Analysis
### Current Structure
- **Single monolithic crate**: `botserver` (242 dependencies in Cargo.toml)
- **Feature flags**: 50+ features (chat, mail, tasks, drive, llm, etc.)
- **Main entry point**: `botserver/src/main.rs` (436 lines)
- **Shared state**: `AppState` in `core::shared::state`
- **Modules**: 40+ top-level modules
### Problems with Current Approach
1. **Long compile times**: Full rebuilds take 30+ minutes
2. **Memory intensive**: Requires significant RAM during compilation
3. **Tight coupling**: All modules compiled together
4. **Hard to scale**: Can't deploy individual services independently
5. **Feature flag complexity**: Many features, hard to test all combinations
## 🏗️ Proposed Architecture
### Shared State Architecture
```
┌─────────────────────────────────────────────────────────────┐
│ SHARED STATE LAYER │
│ ┌─────────────────────────────────────────────────────────┐│
│ │ botstate (NEW) - Shared state types & interfaces ││
│ │ - AppState definition ││
│ │ - Database connection pool ││
│ │ - Redis/Valkey client ││
│ │ - Configuration ││
│ │ - LLM provider interface ││
│ │ - Secret manager interface ││
│ └─────────────────────────────────────────────────────────┘│
└─────────────────────────────────────────────────────────────┘
```
### Microservice Crates
```
Workspace Members:
├── botlib/ # Existing - Core types, errors, utilities
├── botstate/ # NEW - Shared state layer
├── botserver-core/ # NEW - Core HTTP server, routing, middleware
├── botserver-auth/ # NEW - Authentication & authorization
├── botserver-chat/ # NEW - WebSocket chat, LLM integration
├── botserver-mail/ # NEW - Email integration (IMAP/SMTP)
├── botserver-tasks/ # NEW - Task scheduler, cron jobs
├── botserver-drive/ # NEW - File storage (S3/MinIO)
├── botserver-llm/ # NEW - LLM providers, embeddings
├── botserver-vectordb/ # NEW - Qdrant vector database
├── botserver-compliance/# NEW - Legal, audit, compliance
├── botserver-video/ # NEW - Video processing, LiveKit
├── botserver-contacts/ # NEW - CRM, contacts, calendar
├── botserver-auto/ # NEW - Automation, Rhai scripting
├── botserver-ui/ # NEW - Embedded UI, TUI (optional)
└── botserver/ # EXISTING - Becomes meta-crate that re-exports all
```
## 📦 Detailed Crate Responsibilities
### 1. **botstate** (NEW - Foundation)
**Purpose**: Define shared state types that all services will use
**Dependencies**:
- `botlib` (workspace)
- `tokio`, `redis`, `diesel`, `chrono`, `uuid`, `serde`
**Exports**:
```rust
pub struct AppState {
pub db_pool: PgPool,
pub redis_client: Arc<redis::Client>,
pub config: Arc<Config>,
pub llm_provider: Arc<dyn LLMProvider>,
pub secret_manager: Arc<dyn SecretManager>,
// ... other shared resources
}
pub trait LLMProvider {
async fn generate(&self, prompt: &str) -> Result<String>;
}
pub trait SecretManager {
fn get_secret(&self, key: &str) -> Result<String>;
}
```
**Location**: `/home/rodriguez/src/gb/botstate/`
---
### 2. **botserver-core** (NEW - HTTP Foundation)
**Purpose**: Core HTTP server, routing, middleware, health checks
**Dependencies**:
- `botlib`, `botstate`
- `axum`, `tower`, `tower-http`, `tokio`
**Responsibilities**:
- HTTP server setup (Axum)
- Middleware (CORS, logging, rate limiting)
- Health check endpoints
- Graceful shutdown
- Static file serving
**Location**: `/home/rodriguez/src/gb/botserver-core/`
---
### 3. **botserver-auth** (NEW - Security)
**Purpose**: Authentication, authorization, RBAC
**Dependencies**:
- `botlib`, `botstate`, `botserver-core`
- `jsonwebtoken`, `argon2`, `axum`
**Responsibilities**:
- User authentication (login/logout)
- JWT token management
- RBAC (role-based access control)
- Session management
- OAuth integration
**Location**: `/home/rodriguez/src/gb/botserver-auth/`
---
### 4. **botserver-chat** (NEW - Real-time Communication)
**Purpose**: WebSocket chat, message handling, LLM integration
**Dependencies**:
- `botlib`, `botstate`, `botserver-core`
- `tokio-tungstenite`, `axum`, `serde_json`
**Responsibilities**:
- WebSocket connections
- Message routing
- Chat history
- Suggestion buttons
- Tool execution framework
**Location**: `/home/rodriguez/src/gb/botserver-chat/`
---
### 5. **botserver-mail** (NEW - Email)
**Purpose**: Email integration (IMAP/SMTP)
**Dependencies**:
- `botlib`, `botstate`, `botserver-core`
- `lettre`, `imap`, `mailparse`
**Responsibilities**:
- IMAP email retrieval
- SMTP email sending
- Email parsing
- Attachments
- Email templates
**Location**: `/home/rodriguez/src/gb/botserver-mail/`
---
### 6. **botserver-tasks** (NEW - Scheduling)
**Purpose**: Task scheduler, cron jobs
**Dependencies**:
- `botlib`, `botstate`, `botserver-core`
- `cron`, `tokio`
**Responsibilities**:
- Cron job scheduler
- Task queue management
- Recurring tasks
- Task execution tracking
**Location**: `/home/rodriguez/src/gb/botserver-tasks/`
---
### 7. **botserver-drive** (NEW - Storage)
**Purpose**: File storage (S3/MinIO)
**Dependencies**:
- `botlib`, `botstate`, `botserver-core`
- `aws-sdk-s3`, `aws-config`
**Responsibilities**:
- S3/MinIO client
- File upload/download
- File synchronization
- PDF extraction
- Document processing
**Location**: `/home/rodriguez/src/gb/botserver-drive/`
---
### 8. **botserver-llm** (NEW - Language Models)
**Purpose**: LLM providers, embeddings, caching
**Dependencies**:
- `botlib`, `botstate`
- `reqwest`, `serde_json`
**Responsibilities**:
- LLM provider abstraction (Groq, OpenAI, etc.)
- Embedding generation
- Response caching
- Local LLM management (llama.cpp)
**Location**: `/home/rodriguez/src/gb/botserver-llm/`
---
### 9. **botserver-vectordb** (NEW - Vector Database)
**Purpose**: Qdrant vector database operations
**Dependencies**:
- `botlib`, `botstate`, `botserver-llm`
- `qdrant-client`
**Responsibilities**:
- Vector embeddings storage
- Similarity search
- Knowledge base chunks
- RAG (retrieval-augmented generation)
**Location**: `/home/rodriguez/src/gb/botserver-vectordb/`
---
### 10. **botserver-auto** (NEW - Automation)
**Purpose**: Automation, Rhai scripting engine
**Dependencies**:
- `botlib`, `botstate`, `botserver-core`
- `rhai`, `tokio`
**Responsibilities**:
- Rhai script engine
- BASIC keyword execution
- Workflow automation
- Event handlers
- Script compilation
**Location**: `/home/rodriguez/src/gb/botserver-auto/`
---
### 11. **botserver-contacts** (NEW - CRM)
**Purpose**: Contacts, CRM, calendar integration
**Dependencies**:
- `botlib`, `botstate`, `botserver-core`
- `icalendar`, `chrono`
**Responsibilities**:
- Contact management
- CRM operations
- Calendar integration
- Google/Microsoft sync
**Location**: `/home/rodriguez/src/gb/botserver-contacts/`
---
### 12. **botserver-compliance** (NEW - Legal)
**Purpose**: Legal, audit, compliance
**Dependencies**:
- `botlib`, `botstate`, `botserver-core`
- `csv`
**Responsibilities**:
- Audit logging
- Compliance reports
- Legal document management
- Data retention policies
**Location**: `/home/rodriguez/src/gb/botserver-compliance/`
---
### 13. **botserver-video** (NEW - Video)
**Purpose**: Video processing, LiveKit integration
**Dependencies**:
- `botlib`, `botstate`, `botserver-core`
- `livekit`
**Responsibilities**:
- LiveKit integration
- Video analytics
- Meeting management
- Media processing
**Location**: `/home/rodriguez/src/gb/botserver-video/`
---
### 14. **botserver-ui** (OPTIONAL - TUI)
**Purpose**: Console/TUI interface
**Dependencies**:
- `botlib`, `botstate`
- `ratatui`, `crossterm`
**Responsibilities**:
- Terminal UI
- Progress monitoring
- Service status dashboard
**Location**: `/home/rodriguez/src/gb/botserver-ui/`
---
### 15. **botserver** (EXISTING - Meta-crate)
**Purpose**: Re-exports all services, maintains backward compatibility
**Dependencies**:
- All botserver-* crates
- Feature flags to enable/disable services
**Responsibilities**:
- Re-export all services
- Main binary entry point
- Feature flag coordination
**Location**: `/home/rodriguez/src/gb/botserver/` (existing, modified)
---
## 🔄 State Sharing Strategy
### Approach: Shared AppState via botstate Crate
```rust
// botstate/src/lib.rs
use redis::Client as RedisClient;
use diesel::r2d2::Pool as DbPool;
use std::sync::Arc;
pub struct AppState {
pub db_pool: Arc<DbPool>,
pub redis_client: Arc<RedisClient>,
pub config: Arc<Config>,
pub llm_provider: Arc<dyn LLMProvider + Send + Sync>,
pub secret_manager: Arc<dyn SecretManager + Send + Sync>,
}
impl AppState {
pub fn new(
db_pool: DbPool,
redis_client: RedisClient,
config: Config,
llm_provider: Box<dyn LLMProvider + Send + Sync>,
secret_manager: Box<dyn SecretManager + Send + Sync>,
) -> Self {
Self {
db_pool: Arc::new(db_pool),
redis_client: Arc::new(redis_client),
config: Arc::new(config),
llm_provider: Arc::from(llm_provider),
secret_manager: Arc::from(secret_manager),
}
}
}
```
### Each Service Gets AppState as Parameter
```rust
// botserver-chat/src/lib.rs
use botstate::AppState;
use axum::Router;
pub fn create_router(state: Arc<AppState>) -> Router {
Router::new()
.route("/ws", websocket_handler)
.with_state(state)
}
```
---
## 📋 Migration Steps
### Phase 1: Foundation (Week 1-2)
1. ✅ Create `botstate` crate
- Extract `AppState` from `botserver`
- Define trait interfaces (LLMProvider, SecretManager)
- Export shared types
2. ✅ Create `botserver-core` crate
- Extract HTTP server setup
- Extract middleware
- Extract health checks
3. ✅ Update workspace `Cargo.toml`
- Add new members
- Configure dependencies
### Phase 2: Extract Services (Week 3-6)
4. ✅ Extract `botserver-auth`
- Move authentication code
- Update imports
5. ✅ Extract `botserver-chat`
- Move WebSocket code
- Move message handling
6. ✅ Extract `botserver-mail`
- Move email integration
- Move IMAP/SMTP code
7. ✅ Extract `botserver-drive`
- Move S3/MinIO code
- Move file sync
8. ✅ Extract `botserver-auto`
- Move Rhai engine
- Move BASIC keywords
### Phase 3: Advanced Services (Week 7-10)
9. ✅ Extract `botserver-llm`
- Move LLM providers
- Move caching
10. ✅ Extract `botserver-vectordb`
- Move Qdrant client
- Move embeddings
11. ✅ Extract `botserver-tasks`
- Move scheduler
- Move cron jobs
12. ✅ Extract `botserver-contacts`
- Move CRM code
- Move calendar sync
13. ✅ Extract `botserver-compliance`
- Move audit logging
- Move compliance reports
14. ✅ Extract `botserver-video`
- Move LiveKit integration
- Move video analytics
### Phase 4: Integration & Testing (Week 11-12)
15. ✅ Update `botserver` as meta-crate
- Re-export all services
- Maintain backward compatibility
- Feature flags coordination
16. ✅ Test all services independently
- Unit tests
- Integration tests
- End-to-end tests
17. ✅ Documentation
- Update README
- Document APIs
- Update AGENTS.md
---
## 🎯 Benefits
### Compile Time Improvements
- **Before**: Full rebuild 30+ minutes
- **After**:
- botstate: ~30 seconds
- Individual service: 1-3 minutes
- Full workspace: Still 30+ minutes (but cached builds work better)
### Memory Usage
- **Before**: 16GB+ RAM required
- **After**: 2-4GB per service
### Development Workflow
- Work on single service without rebuilding everything
- Better separation of concerns
- Easier to test individual components
- Can deploy services independently
### Scalability
- Deploy only needed services
- Scale services independently
- Different services can use different versions of dependencies
---
## ⚠️ Challenges & Solutions
### Challenge 1: Circular Dependencies
**Problem**: Services reference each other
**Solution**:
- Use traits in `botstate` for cross-service communication
- Dependency injection via `AppState`
- Event-driven architecture for loose coupling
### Challenge 2: Database Migrations
**Problem**: Multiple crates need to coordinate migrations
**Solution**:
- Keep all migrations in `botserver` (meta-crate)
- Use `diesel_migrations` with centralized migration directory
- Each service documents its required migrations
### Challenge 3: Shared Configuration
**Problem**: All services need config values
**Solution**:
- `Config` struct in `botstate`
- Loaded once, shared via `Arc<Config>`
- Each service reads only what it needs
### Challenge 4: Feature Flags
**Problem**: Complex feature flag interactions
**Solution**:
- Each service has its own feature flags
- `botserver` meta-crate coordinates via feature aliases
- Default: all services enabled
- Can build minimal subset for testing
---
## 📊 Dependency Graph
```mermaid
graph TD
botlib[botlib - Core types]
botstate[botstate - Shared state]
botcore[botserver-core - HTTP server]
auth[botserver-auth]
chat[botserver-chat]
mail[botserver-mail]
tasks[botserver-tasks]
drive[botserver-drive]
llm[botserver-llm]
vectordb[botserver-vectordb]
auto[botserver-auto]
contacts[botserver-contacts]
compliance[botserver-compliance]
video[botserver-video]
ui[botserver-ui]
botserver[botserver - Meta-crate]
botlib --> botstate
botstate --> botcore
botcore --> auth
botcore --> chat
botcore --> mail
botcore --> tasks
botcore --> drive
botcore --> auto
botcore --> contacts
botcore --> compliance
botcore --> video
llm --> botstate
vectordb --> botstate
vectordb --> llm
auth --> botstate
chat --> botstate
chat --> llm
mail --> botstate
tasks --> botstate
drive --> botstate
auto --> botstate
contacts --> botstate
compliance --> botstate
video --> botstate
botserver --> botcore
botserver --> auth
botserver --> chat
botserver --> mail
botserver --> tasks
botserver --> drive
botserver --> llm
botserver --> vectordb
botserver --> auto
botserver --> contacts
botserver --> compliance
botserver --> video
botserver --> ui
```
---
## 📁 File Structure After Migration
```
gb/
├── Cargo.toml (workspace)
├── botlib/
│ ├── Cargo.toml
│ └── src/
├── botstate/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ ├── state.rs
│ ├── traits.rs
│ └── config.rs
├── botserver-core/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ ├── server.rs
│ ├── middleware.rs
│ └── health.rs
├── botserver-auth/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ ├── jwt.rs
│ ├── rbac.rs
│ └── session.rs
├── botserver-chat/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ ├── websocket.rs
│ └── messages.rs
├── botserver-mail/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ ├── imap.rs
│ └── smtp.rs
├── botserver-tasks/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ ├── scheduler.rs
│ └── cron.rs
├── botserver-drive/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ ├── s3.rs
│ └── sync.rs
├── botserver-llm/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ ├── provider.rs
│ └── cache.rs
├── botserver-vectordb/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ └── qdrant.rs
├── botserver-auto/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ ├── rhai_engine.rs
│ └── keywords.rs
├── botserver-contacts/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ └── crm.rs
├── botserver-compliance/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ └── audit.rs
├── botserver-video/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ └── livekit.rs
├── botserver-ui/
│ ├── Cargo.toml
│ └── src/
│ ├── lib.rs
│ └── tui.rs
└── botserver/ (meta-crate)
├── Cargo.toml
└── src/
├── main.rs
└── lib.rs (re-exports)
```
---
## 🚀 Getting Started
### Step 1: Create botstate Crate
```bash
cd /home/rodriguez/src/gb
mkdir -p botstate/src
cat > botstate/Cargo.toml << 'EOF'
[package]
name = "botstate"
version = "0.1.0"
edition = "2021"
[dependencies]
botlib = { workspace = true }
tokio = { workspace = true, features = ["full"] }
redis = { workspace = true, features = ["tokio-comp"] }
diesel = { workspace = true, features = ["postgres", "r2d2"] }
chrono = { workspace = true }
uuid = { workspace = true, features = ["v4"] }
serde = { workspace = true, features = ["derive"] }
async-trait = { workspace = true }
EOF
```
### Step 2: Update Workspace
Edit `/home/rodriguez/src/gb/Cargo.toml`:
```toml
members = [
"botapp",
"botdevice",
"botlib",
"botserver",
"bottest",
"botui",
"botstate", # NEW
"botserver-core", # NEW
"botserver-auth", # NEW
"botserver-chat", # NEW
# ... etc
]
```
---
## ✅ Success Criteria
1. ✅ **botstate** compiles independently
2. ✅ **botserver-core** starts HTTP server without other services
3. ✅ Each service can be built independently
4. ✅ No circular dependencies between crates
5. ✅ All tests pass
6. ✅ Compile time reduced by 70% for individual services
7. ✅ Memory usage during compilation reduced by 60%
8. ✅ Backward compatibility maintained (existing code still works)
---
## 📝 Notes
- This plan prioritizes **incremental migration** - each step is reversible
- Start with `botstate` and `botserver-core` as foundation
- Test after each service extraction
- Keep the meta-crate `botserver` for backward compatibility
- Use feature flags to enable/disable services during transition
---
**Created**: 2026-04-18
**Status**: Planning Phase
**Next Step**: Create `botstate` crate with shared state types

View file

@ -1,16 +0,0 @@
#!/bin/bash
# Manual deploy script - run this on alm-ci container
echo "1. Stop..."
sudo incus exec system -- systemctl stop botserver || true
sudo incus exec system -- pkill -x botserver || true
sleep 1
echo "2. Copy..."
sudo incus file push /opt/gbo/work/botserver/target/debug/botserver system:/opt/gbo/bin/botserver --mode=0755
echo "3. Start..."
sudo incus exec system -- systemctl start botserver
sleep 2
echo "4. Verify..."
sudo incus exec system -- pgrep -x botserver && echo "✅ SUCCESS" || echo "❌ FAILED"

12
package.json Normal file
View file

@ -0,0 +1,12 @@
{
"name": "gb",
"version": "1.0.0",
"main": "index.js",
"author": "Rodrigo Rodriguez (Pragmatismo) <me@rodrigorodriguez.com>",
"license": "MIT",
"devDependencies": {
"@playwright/test": "^1.58.1",
"@types/node": "^25.2.0"
},
"scripts": {}
}

79
playwright.config.ts Normal file
View file

@ -0,0 +1,79 @@
import { defineConfig, devices } from '@playwright/test';
/**
* Read environment variables from file.
* https://github.com/motdotla/dotenv
*/
// import dotenv from 'dotenv';
// import path from 'path';
// dotenv.config({ path: path.resolve(__dirname, '.env') });
/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: './tests',
/* Run tests in files in parallel */
fullyParallel: true,
/* Fail the build on CI if you accidentally left test.only in the source code. */
forbidOnly: !!process.env.CI,
/* Retry on CI only */
retries: process.env.CI ? 2 : 0,
/* Opt out of parallel tests on CI. */
workers: process.env.CI ? 1 : undefined,
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
reporter: 'html',
/* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */
use: {
/* Base URL to use in actions like `await page.goto('')`. */
// baseURL: 'http://localhost:3000',
/* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */
trace: 'on-first-retry',
},
/* Configure projects for major browsers */
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
/* Test against mobile viewports. */
// {
// name: 'Mobile Chrome',
// use: { ...devices['Pixel 5'] },
// },
// {
// name: 'Mobile Safari',
// use: { ...devices['iPhone 12'] },
// },
/* Test against branded browsers. */
// {
// name: 'Microsoft Edge',
// use: { ...devices['Desktop Edge'], channel: 'msedge' },
// },
// {
// name: 'Google Chrome',
// use: { ...devices['Desktop Chrome'], channel: 'chrome' },
// },
],
/* Run your local dev server before starting the tests */
// webServer: {
// command: 'npm run start',
// url: 'http://localhost:3000',
// reuseExistingServer: !process.env.CI,
// },
});

View file

@ -1,296 +0,0 @@
# Integrated Suite — Conversational Interface Plan
> **Pattern:** Every suite app exposes its own `PROMPT.md` + internal tools.
> The shared chat bar activates app-specific context when the user is inside that app.
> WhatsApp campaigns is the first full example.
a common chat window stay fixed right like pane colapsable, except for chat... all other ui must be controled by chat, via api/ pompt common mechanismo.
---
## Architecture
```
User (WhatsApp / Suite chat bar)
BotOrchestrator (core/bot/mod.rs)
detect active app context
load app PROMPT.md + app InternalTools
LLM with tools → tool_executor.rs
app data / actions
```
### Key existing pieces
| File | Role |
|---|---|
| `core/bot/mod.rs` | `get_session_tools()` + `ToolExecutor::execute_tool_call()` |
| `tasks/PROMPT.md` | Pattern for app-level LLM prompt |
| `marketing/whatsapp.rs` | WhatsApp campaign send/metrics |
| `marketing/campaigns.rs` | Campaign CRUD |
| `marketing/lists.rs` | Recipient lists |
| `botui/ui/suite/campaigns/` | Campaigns UI |
---
## Standard: Every Suite App
### 1. `PROMPT.md` per app folder
Location: `botserver/src/<app>/PROMPT.md`
```markdown
# <App> — Internal Tools Guide
You are the <App> assistant. When the user is in <App>, you have access to:
- tool: list_<entities>
- tool: create_<entity>
- tool: search_<entity>
- tool: <app_specific_action>
Rules:
- Always confirm destructive actions before executing
- Show results as structured summaries, not raw JSON
- If user uploads a file, parse it and confirm before acting
```
### 2. `tools.rs` per app
Location: `botserver/src/<app>/tools.rs`
Registers `Vec<Tool>` (LLM function-calling schema) + handler mapping.
Loaded by `get_session_tools()` when session's active app = this app.
### 3. App context detection
`core/bot/mod.rs` reads `session.active_app` (set by UI via `POST /api/session/context`).
Loads `<app>/PROMPT.md` as system prompt prefix + `<app>/tools.rs` tools.
---
## WhatsApp Campaigns — Full Conversational Flow
### Meta Rules (enforced in tools)
- Only approved Message Templates for marketing (non-session-initiated)
- 24h session window for free-form after user replies
- Media: image/video/document via Media Upload API before send
- Opt-out: always honor STOP, add to suppression list immediately
- Rate: respect per-phone-number rate limits (1000 msg/s business tier)
- Template category: MARKETING requires explicit opt-in from recipient
### Conversation Flow (WhatsApp → campaign creation)
```
User sends to bot number:
"I want to send a campaign"
Bot: "Great! Send me:
1. Your contact list (.xlsx or .csv)
2. The message text
3. An image (optional)
4. When to send (or 'now')"
User uploads contacts.xlsx
[tool: parse_contact_file]
→ extract phone numbers, names
→ validate E.164 format
→ show preview: "Found 342 contacts. First 3: +55..."
User sends message text
[tool: check_template_compliance]
→ check if free-form or needs approved template
→ if template needed: list available approved templates
→ suggest closest match
User sends image (optional)
[tool: upload_media]
→ upload to Meta Media API
→ return media_id
Bot: "Ready to send to 342 contacts at 14:00 today.
Preview: [image] Hello {name}, ...
Estimated cost: $X
Confirm? (yes/no)"
User: "yes"
[tool: create_and_schedule_campaign]
→ create campaign record
→ apply warmup limit if IP warming
→ schedule via TaskScheduler
```
### WhatsApp Campaign Tools (`marketing/whatsapp_tools.rs`)
```rust
// Tool definitions for LLM function calling
pub fn whatsapp_campaign_tools() -> Vec<Tool> {
vec![
Tool::new("parse_contact_file", "Parse uploaded xlsx/csv into contact list"),
Tool::new("list_templates", "List approved WhatsApp message templates"),
Tool::new("check_template_compliance", "Check if message needs approved template"),
Tool::new("upload_media", "Upload image/video to Meta Media API"),
Tool::new("preview_campaign", "Show campaign preview with cost estimate"),
Tool::new("create_and_schedule_campaign", "Create campaign and schedule send"),
Tool::new("get_campaign_status", "Get delivery/read metrics for a campaign"),
Tool::new("pause_campaign", "Pause an in-progress campaign"),
Tool::new("list_campaigns", "List recent campaigns with metrics"),
Tool::new("add_to_suppression", "Add number to opt-out list"),
]
}
```
### WhatsApp PROMPT.md (`marketing/WHATSAPP_PROMPT.md`)
```markdown
# WhatsApp Campaign Assistant
You help users create and manage WhatsApp marketing campaigns.
## Meta Platform Rules (MANDATORY)
- Marketing messages MUST use pre-approved templates outside 24h session window
- Always check opt-in status before adding to campaign
- Honor STOP/unsubscribe immediately via add_to_suppression tool
- Never send more than warmup daily limit if IP is warming up
- Image must be uploaded via upload_media before referencing in campaign
## Conversation Style
- Guide step by step: contacts → message → media → schedule → confirm
- Show cost estimate before confirming
- After send: proactively share open/read rates when available
## File Handling
- .xlsx/.csv → use parse_contact_file tool
- Images → use upload_media tool
- Always confirm parsed data before proceeding
```
---
## Integrated Suite Chat Bar — Standard
### How it works
1. User opens any suite app (CRM, Campaigns, Drive, etc.)
2. Chat bar at bottom activates with app context
3. `POST /api/session/context { app: "campaigns" }` sets `session.active_app`
4. BotOrchestrator loads `campaigns/PROMPT.md` + `campaigns/tools.rs`
5. User can ask natural language questions or trigger actions
### Examples per app
| App | Example query | Tool activated |
|---|---|---|
| **Campaigns** | "How did last week's campaign perform?" | `get_campaign_metrics` |
| **CRM** | "Show deals closing this month" | `list_deals` with filter |
| **Drive** | "Find the Q1 report" | `search_files` |
| **Tasks** | "Create a task to follow up with Acme" | `create_task` |
| **People** | "Who hasn't been contacted in 30 days?" | `list_contacts` with filter |
| **Mail** | "Summarize unread emails from clients" | `list_emails` + LLM summary |
| **Sheet** | "What's the total revenue in column D?" | `query_sheet` |
| **Learn** | "What does our refund policy say?" | `search_kb` |
---
## Implementation Plan
### Phase 1 — Infrastructure (1 sprint)
- [ ] `core/bot/mod.rs` — read `session.active_app`, load app PROMPT + tools
- [ ] `core/tool_context.rs` — app tool registry: `register_app_tools(app_name) -> Vec<Tool>`
- [ ] `POST /api/session/context` — set active app from UI
- [ ] Suite chat bar UI component (`botui/ui/suite/partials/chatbar.html`)
### Phase 2 — WhatsApp Campaigns (1 sprint)
- [ ] `marketing/whatsapp_tools.rs` — 10 tools above
- [ ] `marketing/WHATSAPP_PROMPT.md`
- [ ] `marketing/file_parser.rs` — xlsx/csv → contact list
- [ ] Meta warmup enforcement in send path
- [ ] Conversational campaign creation flow (state machine in session)
### Phase 3 — App-by-app rollout (1 app/sprint)
Priority order based on value:
1. CRM (deals, contacts, pipeline queries)
2. Campaigns (email + WhatsApp)
3. Tasks (create, assign, status)
4. Drive (search, summarize docs)
5. Mail (summarize, draft reply)
6. People (segment, find contacts)
7. Sheet (query, calculate)
8. Learn (KB search)
### Phase 4 — Cross-app intelligence
- [ ] Global search across all apps via single query
- [ ] "What happened today?" — aggregates activity across CRM + Mail + Tasks
- [ ] Proactive suggestions: "You have 3 deals closing this week and no follow-up tasks"
---
## File Structure to Create
```
botserver/src/
├── marketing/
│ ├── whatsapp_tools.rs ← NEW: LLM tool definitions + handlers
│ ├── WHATSAPP_PROMPT.md ← NEW: WhatsApp assistant system prompt
│ ├── file_parser.rs ← NEW: xlsx/csv → contacts
│ └── warmup.rs ← NEW: (from campaigns.md plan)
├── core/
│ ├── tool_registry.rs ← NEW: app → tools mapping
│ └── bot/
│ └── app_context.rs ← NEW: load app prompt + tools per session
├── crm/
│ ├── tools.rs ← NEW
│ └── PROMPT.md ← NEW
├── tasks/
│ └── tools.rs ← NEW (PROMPT.md exists)
└── <each app>/
├── tools.rs ← NEW per app
└── PROMPT.md ← NEW per app
botui/ui/suite/
└── partials/
└── chatbar.html ← NEW: shared chat bar component
```
---
## Chat Bar UI (`partials/chatbar.html`)
```html
<div id="suite-chatbar" class="chatbar">
<div id="chatbar-messages" hx-ext="ws" ws-connect="/ws/suite-chat"></div>
<form ws-send>
<input type="hidden" name="app_context" value="{{ active_app }}">
<input type="file" id="chatbar-file" name="file" accept=".xlsx,.csv,.png,.jpg,.pdf" style="display:none">
<button type="button" onclick="document.getElementById('chatbar-file').click()">📎</button>
<input type="text" name="message" placeholder="Ask anything about {{ active_app }}...">
<button type="submit"></button>
</form>
</div>
```
File uploads go to `POST /api/suite/upload` → stored in Drive → media_id passed to tool.
---
## Compilation & Validation Log
**Last Validated:** 2026-04-17
**Status:** ✅ Design Document - No Code to Compile
**Validation Checklist:**
- [x] Architecture diagram is clear
- [x] App pattern is defined
- [x] WhatsApp campaign flow is documented
- [x] Tool definitions are complete
- [x] HTML structure is valid
- [x] Rust code examples are syntactically correct
- [x] Implementation phases are planned
- [x] File structure is organized
**Notes:**
- This is a design document for integrated suite conversational interface
- No compilation required - it's planning documentation
- Ready for implementation when developer is available
- References existing code in botserver/src/marketing/ and botserver/src/core/bot/

View file

@ -1,10 +1 @@
#!/bin/bash
set -e
echo "Cleaning up..."
rm -rf botserver-stack/ ./work/ .env
echo "Starting services..."
./restart.sh
echo "Reset complete!"
rm -rf botserver-stack/ ./work/ .env

View file

@ -1,32 +0,0 @@
$ErrorActionPreference = "Continue"
Write-Host "Stopping..."
Stop-Process -Name "botserver" -Force -ErrorAction SilentlyContinue
Stop-Process -Name "botui" -Force -ErrorAction SilentlyContinue
Stop-Process -Name "rustc" -Force -ErrorAction SilentlyContinue
Write-Host "Cleaning..."
Remove-Item -Path "botserver.log", "botui.log" -Force -ErrorAction SilentlyContinue
Write-Host "Building..."
cargo build -p botserver
if ($LASTEXITCODE -ne 0) { Write-Host "Failed to build botserver"; exit 1 }
cargo build -p botui
if ($LASTEXITCODE -ne 0) { Write-Host "Failed to build botui"; exit 1 }
Write-Host "Starting botserver..."
$env:PORT = "8080"
$env:RUST_LOG = "debug"
$env:PATH += ";C:\pgsql\pgsql\bin;C:\pgsql\pgsql\lib"
$botserverProcess = Start-Process -PassThru -NoNewWindow -FilePath ".\target\debug\botserver.exe" -ArgumentList "--noconsole" -RedirectStandardOutput "botserver.log" -RedirectStandardError "botserver.log"
Write-Host " PID: $($botserverProcess.Id)"
Write-Host "Starting botui..."
$env:BOTSERVER_URL = "http://localhost:8080"
$env:PORT = "3000"
$botuiProcess = Start-Process -PassThru -NoNewWindow -FilePath ".\target\debug\botui.exe" -RedirectStandardOutput "botui.log" -RedirectStandardError "botui.log"
Write-Host " PID: $($botuiProcess.Id)"
Write-Host "Done. Logs are being written to botserver.log and botui.log"
Write-Host "To view logs, you can use: Get-Content botserver.log -Wait"

View file

@ -1,49 +1,28 @@
#!/bin/bash
set -e
echo "=== Fast Restart: botserver + botmodels only ==="
echo "🛑 Stopping existing processes..."
pkill -f botserver || true
pkill -f botui || true
pkill -f rustc || true
# Kill only the app services, keep infra running
pkill -f "botserver --noconsole" || true
pkill -f "botmodels" || true
echo "🧹 Cleaning logs..."
rm -f botserver.log botui.log
# Clean logs
rm -f botserver.log botmodels.log
# Build only botserver (botui likely already built)
echo "🔨 Building botserver..."
cargo build -p botserver
# Start botmodels
cd botmodels
source venv/bin/activate
uvicorn src.main:app --host 0.0.0.0 --port 8085 > ../botmodels.log 2>&1 &
echo " botmodels PID: $!"
cd ..
echo "🔨 Building botui..."
cargo build -p botui
# Wait for botmodels
for i in $(seq 1 20); do
if curl -s http://localhost:8085/api/health > /dev/null 2>&1; then
echo " botmodels ready"
break
fi
sleep 1
done
echo "🚀 Starting botserver..."
RUST_LOG=info ./target/debug/botserver --noconsole > botserver.log 2>&1 &
BOTSERVER_PID=$!
# Start botserver (keep botui running if already up)
if ! pgrep -f "botui" > /dev/null; then
echo "Starting botui..."
cargo build -p botui
cd botui
BOTSERVER_URL="http://localhost:8080" ./target/debug/botui > ../botui.log 2>&1 &
echo " botui PID: $!"
cd ..
fi
echo "🚀 Starting botui..."
BOTSERVER_URL="https://localhost:8088" ./target/debug/botui > botui.log 2>&1 &
BOTUI_PID=$!
# Start botserver
BOTMODELS_HOST="http://localhost:8085" BOTMODELS_API_KEY="starter" RUST_LOG=info ./target/debug/botserver --noconsole > botserver.log 2>&1 &
echo " botserver PID: $!"
# Quick health check
sleep 2
curl -s http://localhost:8080/health > /dev/null 2>&1 && echo "✅ botserver ready" || echo "❌ botserver failed"
echo "Done. botserver $(pgrep -f 'botserver --noconsole') botui $(pgrep -f botui) botmodels $(pgrep -f botmodels)"
echo "✅ Started botserver (PID: $BOTSERVER_PID) and botui (PID: $BOTUI_PID)"
echo "📊 Monitor with: tail -f botserver.log botui.log"
echo "🌐 Access at: http://localhost:3000"

28
start.bas Normal file
View file

@ -0,0 +1,28 @@
REM Knowledge Base Website Crawler Bot - Start Template
REM Sets up bot context and crawled websites, then exits
REM Load bot introduction
intro = GET BOT MEMORY "introduction"
IF intro = "" THEN
intro = "I'm your documentation assistant with access to crawled websites."
END IF
REM Register websites for crawling (preprocessing mode)
USE WEBSITE "https://docs.python.org"
USE WEBSITE "https://developer.mozilla.org"
USE WEBSITE "https://stackoverflow.com"
REM Set context for LLM
SET CONTEXT "role" AS intro
SET CONTEXT "capabilities" AS "I can search Python docs, MDN web docs, and Stack Overflow."
REM Configure suggestion buttons
CLEAR SUGGESTIONS
ADD SUGGESTION "python" AS "How do I use Python dictionaries?"
ADD SUGGESTION "javascript" AS "Explain JavaScript async/await"
ADD SUGGESTION "web" AS "What is the DOM in web development?"
REM Initial greeting
TALK intro
TALK "I have access to Python documentation, MDN web docs, and Stack Overflow."
TALK "Ask me any programming question!"

4
stop.sh Executable file
View file

@ -0,0 +1,4 @@
pkill botui
pkill botserver -9

18
tests/example.spec.ts Normal file
View file

@ -0,0 +1,18 @@
import { test, expect } from '@playwright/test';
test('has title', async ({ page }) => {
await page.goto('https://playwright.dev/');
// Expect a title "to contain" a substring.
await expect(page).toHaveTitle(/Playwright/);
});
test('get started link', async ({ page }) => {
await page.goto('https://playwright.dev/');
// Click the get started link.
await page.getByRole('link', { name: 'Get started' }).click();
// Expects page to have a heading with the name of Installation.
await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible();
});

41
yarn.lock Normal file
View file

@ -0,0 +1,41 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@playwright/test@^1.58.1":
version "1.58.1"
resolved "https://registry.yarnpkg.com/@playwright/test/-/test-1.58.1.tgz#891dcd1da815cb1042490531f6d8778988509d22"
integrity sha512-6LdVIUERWxQMmUSSQi0I53GgCBYgM2RpGngCPY7hSeju+VrKjq3lvs7HpJoPbDiY5QM5EYRtRX5fvrinnMAz3w==
dependencies:
playwright "1.58.1"
"@types/node@^25.2.0":
version "25.2.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-25.2.0.tgz#015b7d228470c1dcbfc17fe9c63039d216b4d782"
integrity sha512-DZ8VwRFUNzuqJ5khrvwMXHmvPe+zGayJhr2CDNiKB1WBE1ST8Djl00D0IC4vvNmHMdj6DlbYRIaFE7WHjlDl5w==
dependencies:
undici-types "~7.16.0"
fsevents@2.3.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
playwright-core@1.58.1:
version "1.58.1"
resolved "https://registry.yarnpkg.com/playwright-core/-/playwright-core-1.58.1.tgz#d63be2c9b7dcbdb035beddd4b42437bd3ca89107"
integrity sha512-bcWzOaTxcW+VOOGBCQgnaKToLJ65d6AqfLVKEWvexyS3AS6rbXl+xdpYRMGSRBClPvyj44njOWoxjNdL/H9UNg==
playwright@1.58.1:
version "1.58.1"
resolved "https://registry.yarnpkg.com/playwright/-/playwright-1.58.1.tgz#63300e77a604c77264e1b499c0d94b54ed96d6ba"
integrity sha512-+2uTZHxSCcxjvGc5C891LrS1/NlxglGxzrC4seZiVjcYVQfUa87wBL6rTDqzGjuoWNjnBzRqKmF6zRYGMvQUaQ==
dependencies:
playwright-core "1.58.1"
optionalDependencies:
fsevents "2.3.2"
undici-types@~7.16.0:
version "7.16.0"
resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-7.16.0.tgz#ffccdff36aea4884cbfce9a750a0580224f58a46"
integrity sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==