secubox-openwrt/package/secubox/secubox-ai-gateway/files/usr/lib/ai-gateway/providers.sh
CyberMind-FR f3cea01792 feat(ai-gateway): Add Data Classifier (Sovereignty Engine) for ANSSI CSPN
Implement secubox-ai-gateway package with intelligent AI request routing
based on data sensitivity classification for GDPR/ANSSI compliance.

Features:
- 3-tier data classification: LOCAL_ONLY, SANITIZED, CLOUD_DIRECT
- Provider hierarchy: LocalAI > Mistral (EU) > Claude > GPT > Gemini > xAI
- PII sanitizer: IPv4/IPv6, MAC, credentials, private keys scrubbing
- OpenAI-compatible API proxy on port 4050
- aigatewayctl CLI: status, classify, sanitize, provider, audit commands
- RPCD backend with 11 ubus methods for LuCI integration
- ANSSI CSPN audit logging in JSONL format

Classification patterns detect:
- IP addresses, MAC addresses, private keys
- Credentials (password, secret, token, api_key)
- System paths, security tool references
- WireGuard configuration data

All cloud providers are opt-in. Default LOCAL_ONLY ensures data
sovereignty - sensitive data never leaves the device.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-28 17:55:22 +01:00

137 lines
3.8 KiB
Bash

#!/bin/sh
# SecuBox AI Gateway - Provider Routing Logic
# Selects appropriate AI provider based on data classification
CONFIG="ai-gateway"
PROVIDERS_DIR="/usr/lib/ai-gateway/providers"
# Load all provider configurations
load_providers() {
. /lib/functions.sh
config_load "$CONFIG"
}
# Get ordered list of enabled providers for a classification level
# Args: $1 = classification (local_only, sanitized, cloud_direct)
# Output: space-separated list of provider names sorted by priority
get_enabled_providers() {
local classification="$1"
local providers=""
# Iterate through all provider sections
for section in localai mistral claude openai gemini xai; do
local enabled=$(uci -q get ${CONFIG}.${section}.enabled)
[ "$enabled" != "1" ] && continue
local prov_class=$(uci -q get ${CONFIG}.${section}.classification)
local priority=$(uci -q get ${CONFIG}.${section}.priority || echo 99)
# Filter by classification level
case "$classification" in
local_only)
# Only LocalAI for local_only
[ "$prov_class" = "local_only" ] || continue
;;
sanitized)
# LocalAI or EU providers (Mistral)
[ "$prov_class" = "local_only" ] || [ "$prov_class" = "sanitized" ] || continue
;;
cloud_direct)
# Any provider allowed
;;
esac
providers="${providers}${priority}:${section} "
done
# Sort by priority and return provider names
echo "$providers" | tr ' ' '\n' | sort -t: -k1 -n | cut -d: -f2 | tr '\n' ' '
}
# Check if a provider is available (has API key or is local)
provider_available() {
local provider="$1"
case "$provider" in
localai)
# Check if LocalAI is running
local endpoint=$(uci -q get ${CONFIG}.localai.endpoint || echo "http://127.0.0.1:8081")
wget -q -O /dev/null --timeout=2 "${endpoint}/readyz" 2>/dev/null
return $?
;;
*)
# Cloud providers need API key
local api_key=$(uci -q get ${CONFIG}.${provider}.api_key)
[ -n "$api_key" ] && return 0
return 1
;;
esac
}
# Select best available provider for a classification
# Args: $1 = classification
# Output: provider name or empty if none available
select_provider() {
local classification="$1"
for provider in $(get_enabled_providers "$classification"); do
[ -z "$provider" ] && continue
if provider_available "$provider"; then
echo "$provider"
return 0
fi
done
# No provider available
return 1
}
# Route request to selected provider
# Args: $1 = JSON request, $2 = classification
# Output: JSON response
route_request() {
local request_json="$1"
local classification="$2"
local provider=$(select_provider "$classification")
if [ -z "$provider" ]; then
printf '{"error":{"code":"no_provider","message":"No provider available for classification: %s"}}' "$classification"
return 1
fi
# Source provider adapter
local adapter="${PROVIDERS_DIR}/${provider}.sh"
if [ ! -f "$adapter" ]; then
printf '{"error":{"code":"adapter_missing","message":"Provider adapter not found: %s"}}' "$provider"
return 1
fi
. "$adapter"
# Call provider-specific handler
provider_request "$request_json"
}
# Get provider info for status display
get_provider_info() {
local provider="$1"
local enabled=$(uci -q get ${CONFIG}.${provider}.enabled || echo "0")
local priority=$(uci -q get ${CONFIG}.${provider}.priority || echo "-")
local classification=$(uci -q get ${CONFIG}.${provider}.classification || echo "-")
local model=$(uci -q get ${CONFIG}.${provider}.model || echo "-")
local status="unavailable"
if [ "$enabled" = "1" ]; then
provider_available "$provider" && status="available" || status="configured"
fi
printf '{"name":"%s","enabled":%s,"priority":%s,"classification":"%s","model":"%s","status":"%s"}' \
"$provider" \
"$([ "$enabled" = "1" ] && echo "true" || echo "false")" \
"$priority" \
"$classification" \
"$model" \
"$status"
}