diff --git a/.forgejo/workflows/botserver.yaml b/.forgejo/workflows/botserver.yaml index 4773b752..08333fc2 100644 --- a/.forgejo/workflows/botserver.yaml +++ b/.forgejo/workflows/botserver.yaml @@ -18,12 +18,12 @@ jobs: git pull git submodule update --init --recursive botlib botserver cargo build -p botserver - - name: Deploy + - name: Deploy to Stage run: | - sudo incus exec system -- systemctl stop botserver || true - sudo incus exec system -- pkill -x botserver || true + sudo incus exec system --project STAGE-GBO -- systemctl stop botserver || true + sudo incus exec system --project STAGE-GBO -- 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 + sudo incus file push /opt/gbo/work/botserver/target/debug/botserver system/opt/gbo/bin/botserver --project STAGE-GBO --mode=0755 + sudo incus exec system --project STAGE-GBO -- systemctl start botserver sleep 2 - sudo incus exec system -- pgrep -x botserver && echo "✅ BotServer Deployed" || echo "❌ Failed" + sudo incus exec system --project STAGE-GBO -- pgrep -x botserver && echo "✅ BotServer Deployed to Stage" || echo "❌ Failed" diff --git a/STAGE.md b/STAGE.md new file mode 100644 index 00000000..52cc4d48 --- /dev/null +++ b/STAGE.md @@ -0,0 +1,67 @@ +# Staging Environment Guide (STAGE-GBO) + +## Infrastructure Overview + +The staging environment is implemented using an isolated **Incus Project** named `STAGE-GBO`. This guarantees that the stage containers, network, and storage are entirely separated from the production environment (`default` project), preventing any accidental interference with `PROD-GBO` containers. + +To work within the staging environment, you must switch to its project first: +```bash +sudo incus project switch STAGE-GBO +``` +To switch back to production: +```bash +sudo incus project switch default +``` + +### Container Architecture + +The stage environment consists of clones of the following production containers, restricted to a maximum of 10GB disk space and mapped to a dedicated 10.0.3.x subnet (`stagebr0` network): + +| Container | Internal IP | Data Status | Purpose | +|-----------|-------------|-------------|---------| +| **system** | `10.0.3.10` | Wiped (`/opt/gbo/work/`) | Main BotServer + BotUI | +| **tables** | `10.0.3.11` | Intact (schema & DB preserved) | PostgreSQL database | +| **vault** | `10.0.3.12` | Intact | Secrets management | +| **cache** | `10.0.3.13` | Wiped (RDB/AOF deleted) | Valkey cache | +| **drive** | `10.0.3.14` | Wiped (started from scratch) | MinIO object storage | +| **llm** | `10.0.3.15` | Intact | llama.cpp local inference | + +## Automation Script + +The setup process was automated using `setup-stage-gbo.sh`. The script performs the following tasks: +1. **Creates `STAGE-GBO` Project:** Configured with `features.networks=true` and `features.profiles=true` to isolate networks and profiles from PROD. +2. **Creates `stagebr0` Network:** A dedicated NAT network for 10.0.3.x. +3. **Sets Resource Limits:** Configures the `default` profile in `STAGE-GBO` with a 10GB root disk size limit. +4. **Clones Containers:** Uses `incus copy` to securely copy containers from `default` to `STAGE-GBO` using ZFS/BTRFS copy-on-write without consuming immediate space. +5. **Configures IPs:** Updates `/etc/network/interfaces` inside each stage container to assign the static 10.0.3.x IPs. +6. **Cleans Data:** Wipes `/opt/gbo/logs/` in all containers, wipes MinIO data in `drive`, wipes the AST cache in `system`, and clears Valkey data in `cache`. The BotServer database in `tables` is preserved for testing. + +## Daily Operations & Access + +### Accessing Stage Containers + +Because the project is isolated, running commands requires switching the project or specifying it explicitly: +```bash +# Explicitly access the system container in STAGE-GBO +sudo incus exec STAGE-GBO:system -- bash + +# Or switch context entirely +sudo incus project switch STAGE-GBO +sudo incus list +sudo incus exec system -- bash +``` + +### Resetting Data + +If you need to completely reset a specific component in the staging environment without affecting production, simply stop it, clear its data, and restart it: +```bash +sudo incus project switch STAGE-GBO +sudo incus stop drive +sudo incus exec drive -- rm -rf /opt/gbo/data/minio/* +sudo incus start drive +``` + +### Security Directives + +- **NO External Exposure:** The staging environment is internally isolated. Do not map public DNS or Caddy proxy rules to the `10.0.3.x` IPs unless testing is specifically required via a staging domain. +- **Data Protection:** Although it's an isolated project, `incus copy` relies on the host's underlying storage. Running aggressive I/O operations or writing massive amounts of data in stage could potentially exhaust the host's shared disk space. The 10GB hard limit per container mitigates this, but monitor `df -h` on the host to ensure `PROD` is not impacted. diff --git a/botlib/src/message_types.rs b/botlib/src/message_types.rs index 81f7373b..84416062 100644 --- a/botlib/src/message_types.rs +++ b/botlib/src/message_types.rs @@ -18,6 +18,7 @@ impl MessageType { pub const CONTEXT_CHANGE: Self = Self(5); pub const TOOL_EXEC: Self = Self(6); + pub const SYSTEM: Self = Self(7); } impl From for MessageType { @@ -48,6 +49,7 @@ impl std::fmt::Display for MessageType { 4 => "SUGGESTION", 5 => "CONTEXT_CHANGE", 6 => "TOOL_EXEC", + 7 => "SYSTEM", _ => "UNKNOWN", }; write!(f, "{name}") diff --git a/botserver/src/core/bot/mod.rs b/botserver/src/core/bot/mod.rs index f54f8d87..fb009c16 100644 --- a/botserver/src/core/bot/mod.rs +++ b/botserver/src/core/bot/mod.rs @@ -438,6 +438,16 @@ impl BotOrchestrator { } } + // Handle SYSTEM messages (type 7) - inject into history as system role + if message.message_type == MessageType::SYSTEM { + if !message_content.is_empty() { + info!("SYSTEM message injection for session {}", session_id); + let mut sm = self.state.session_manager.blocking_lock(); + sm.save_message(session_id, user_id, 3, &message_content, 1)?; // role 3 = System + } + return Ok(()); + } + // Legacy: Handle direct tool invocation via __TOOL__: prefix if message_content.starts_with("__TOOL__:") { let tool_name = message_content.trim_start_matches("__TOOL__:").trim(); diff --git a/botui/ui/suite/chat/chat.html b/botui/ui/suite/chat/chat.html index 7f257df2..bb1af9da 100644 --- a/botui/ui/suite/chat/chat.html +++ b/botui/ui/suite/chat/chat.html @@ -119,7 +119,7 @@ CONTINUE: 3, SUGGESTION: 4, CONTEXT_CHANGE: 5, - SYSTEM: 6, // For switcher/modifier injection - doesn't appear in chat history + SYSTEM: 7, // For switcher/modifier injection - doesn't appear in chat history }; var EntityTypes = { @@ -989,10 +989,10 @@ function hideThinkingIndicator() { chip.textContent = suggestion.text || "Suggestion"; - chip.onclick = (function (sugg, act, name) { + chip.onclick = (function (sugg, act, name, isSw) { return function () { - console.log("Suggestion clicked:", sugg, "as switcher:", isSwitcher); - if (isSwitcher) { + console.log("Suggestion clicked:", sugg, "as switcher:", isSw); + if (isSw) { // Toggle switcher logic if (activeSwitchers.has(name)) { activeSwitchers.delete(name); @@ -1032,7 +1032,7 @@ function hideThinkingIndicator() { // Default fallback: send suggestion text window.sendMessage(sugg.text); }; - })(suggestion, action, switcherName); + })(suggestion, action, switcherName, isSwitcher); suggestionsEl.appendChild(chip); }); diff --git a/setup-stage-gbo.sh b/setup-stage-gbo.sh new file mode 100644 index 00000000..caf688df --- /dev/null +++ b/setup-stage-gbo.sh @@ -0,0 +1,101 @@ +#!/bin/bash +# setup-stage-gbo.sh +# Run this on the Incus host (administrator@63.141.255.9) +# +# This script sets up a STAGE-GBO project that completely isolates the stage +# environment from PROD, clones the essential containers, changes their IPs +# to 10.0.3.x, restricts disk size to 10GB max, and wipes data where requested. + +set -e + +PROJECT="STAGE-GBO" +NETWORK="stagebr0" + +echo "=== 1. Creating Isolated Project: $PROJECT ===" +# features.networks and features.profiles isolate the network and profiles from default +sudo incus project create $PROJECT \ + -c features.networks=true \ + -c features.profiles=true \ + -c features.storage.volumes=true || echo "Project might already exist." + +sudo incus project switch $PROJECT + +echo "=== 2. Creating Stage Network (10.0.3.x) ===" +sudo incus network create $NETWORK ipv4.address=10.0.3.1/24 ipv4.nat=true ipv6.address=none || echo "Network might already exist." + +echo "=== 3. Configuring Stage Default Profile (10GB Limit) ===" +# Configure the default profile for the STAGE-GBO project to use the new network +sudo incus profile device add default eth0 nic network=$NETWORK name=eth0 || \ +sudo incus profile device set default eth0 network $NETWORK || true + +# Limit root disk size to 10GB +sudo incus profile device add default root disk path=/ pool=default size=10GB || \ +sudo incus profile device set default root size=10GB || true + +# Containers to clone +CONTAINERS=("system" "tables" "vault" "cache" "drive" "llm") + +# Target IPs for stage environment +declare -A IPS=( + ["system"]="10.0.3.10" + ["tables"]="10.0.3.11" + ["vault"]="10.0.3.12" + ["cache"]="10.0.3.13" + ["drive"]="10.0.3.14" + ["llm"]="10.0.3.15" +) + +echo "=== 4. Cloning Containers from PROD (default project) ===" +sudo incus project switch PROD-GBO1 + +for c in "${CONTAINERS[@]}"; do + echo "Copying $c to $PROJECT..." + sudo incus copy PROD-GBO1:$c $PROJECT:$c || echo " Warning: Failed to copy $c. It might already exist." +done + +echo "=== 5. Reconfiguring and Cleaning Data in STAGE-GBO ===" +sudo incus project switch $PROJECT + +for c in "${CONTAINERS[@]}"; do + IP="${IPS[$c]}" + echo "--> Starting $c for reconfiguration..." + sudo incus start $c || true + sleep 3 # Wait for container to initialize + + echo " Setting static IP $IP in /etc/network/interfaces..." + sudo incus exec $c -- 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 10.0.3.1 +dns-nameservers 8.8.8.8 8.8.4.4 +EOF" + + echo " Cleaning logs..." + sudo incus exec $c -- bash -c 'rm -rf /opt/gbo/logs/* || true' + + # Apply specific data wipe rules + if [ "$c" == "drive" ]; then + echo " Wiping MinIO data (starting from scratch)..." + sudo incus exec $c -- bash -c 'rm -rf /opt/gbo/data/minio/* || true' + elif [ "$c" == "tables" ]; then + echo " Keeping tables data (database botserver intact as requested)." + elif [ "$c" == "cache" ]; then + echo " Wiping Valkey cache..." + sudo incus exec $c -- bash -c 'rm -rf /opt/gbo/data/valkey/*.rdb /opt/gbo/data/valkey/*.aof || true' + elif [ "$c" == "system" ]; then + echo " Wiping work directory and compiled ASTs..." + sudo incus exec $c -- bash -c 'rm -rf /opt/gbo/work/* || true' + fi + + echo " Restarting $c to apply new IP..." + sudo incus restart $c || true +done + +echo "=== STAGE-GBO Setup Complete ===" +echo "You are currently in the default project." +sudo incus project switch PROD-GBO1