Complete snapshot of /opt/microdao-daarion/ from NODE1 (144.76.224.179).
This represents the actual running production code that has diverged
significantly from the previous main branch.
Key changes from old main:
- Gateway (http_api.py): expanded from ~40KB to 164KB with full agent support
- Router: new /v1/agents/{id}/infer endpoint with vision + DeepSeek routing
- Behavior Policy: SOWA v2.2 (3-level: FULL/ACK/SILENT)
- Agent Registry: config/agent_registry.yml as single source of truth
- 13 agents configured (was 3)
- Memory service integration
- CrewAI teams and roles
Excluded from snapshot: venv/, .env, data/, backups, .tgz archives
Co-authored-by: Cursor <cursoragent@cursor.com>
280 lines
7.2 KiB
Bash
Executable File
280 lines
7.2 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# NODE1 API Key Management
|
|
# Version: 1.0
|
|
# Last Updated: 2026-01-26
|
|
#
|
|
# Usage:
|
|
# ./api-keys.sh create <name> [--admin]
|
|
# ./api-keys.sh revoke <key_id>
|
|
# ./api-keys.sh list
|
|
# ./api-keys.sh rotate-health
|
|
# ./api-keys.sh verify <key>
|
|
#
|
|
|
|
set -e
|
|
|
|
# Paths
|
|
KEYS_DIR="/opt/microdao-daarion/secrets"
|
|
KEYS_FILE="$KEYS_DIR/api-keys.conf"
|
|
HEALTH_TOKEN_FILE="$KEYS_DIR/health-token.conf"
|
|
NGINX_KEYS_MAP="$KEYS_DIR/nginx-api-keys.conf"
|
|
LOG_FILE="/var/log/microdao/api-keys.log"
|
|
|
|
# Colors
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
NC='\033[0m'
|
|
|
|
# Initialize directories
|
|
init_dirs() {
|
|
mkdir -p "$KEYS_DIR"
|
|
mkdir -p "$(dirname $LOG_FILE)"
|
|
chmod 700 "$KEYS_DIR"
|
|
touch "$KEYS_FILE" "$HEALTH_TOKEN_FILE" "$NGINX_KEYS_MAP"
|
|
chmod 600 "$KEYS_FILE" "$HEALTH_TOKEN_FILE" "$NGINX_KEYS_MAP"
|
|
}
|
|
|
|
# Log action (without exposing secrets)
|
|
log_action() {
|
|
local action="$1"
|
|
local key_id="$2"
|
|
local details="$3"
|
|
echo "$(date '+%Y-%m-%d %H:%M:%S') | $action | key_id=$key_id | $details" >> "$LOG_FILE"
|
|
}
|
|
|
|
# Generate random key
|
|
generate_key() {
|
|
openssl rand -hex 32
|
|
}
|
|
|
|
# Generate key ID
|
|
generate_key_id() {
|
|
local name="$1"
|
|
local timestamp=$(date +%Y%m%d%H%M%S)
|
|
local random=$(openssl rand -hex 4)
|
|
echo "kid_${name}_${timestamp}_${random}"
|
|
}
|
|
|
|
# Create new API key
|
|
create_key() {
|
|
local name="$1"
|
|
local is_admin="${2:-false}"
|
|
|
|
if [ -z "$name" ]; then
|
|
echo -e "${RED}Error: Key name required${NC}"
|
|
echo "Usage: $0 create <name> [--admin]"
|
|
exit 1
|
|
fi
|
|
|
|
local key_id=$(generate_key_id "$name")
|
|
local secret=$(generate_key)
|
|
local api_key="sk-${secret}"
|
|
local created_at=$(date -Iseconds)
|
|
local scope="standard"
|
|
[ "$is_admin" = "--admin" ] && scope="admin"
|
|
|
|
# Save to keys file (key_id|name|scope|created_at|hash)
|
|
local key_hash=$(echo -n "$api_key" | sha256sum | cut -d' ' -f1)
|
|
echo "${key_id}|${name}|${scope}|${created_at}|${key_hash}" >> "$KEYS_FILE"
|
|
|
|
# Update nginx map
|
|
rebuild_nginx_map
|
|
|
|
log_action "CREATE" "$key_id" "name=$name scope=$scope"
|
|
|
|
echo -e "${GREEN}API Key Created${NC}"
|
|
echo "================================"
|
|
echo "Key ID: $key_id"
|
|
echo "Name: $name"
|
|
echo "Scope: $scope"
|
|
echo "API Key: $api_key"
|
|
echo "================================"
|
|
echo -e "${YELLOW}WARNING: Save this key now. It cannot be retrieved later.${NC}"
|
|
}
|
|
|
|
# Revoke API key
|
|
revoke_key() {
|
|
local key_id="$1"
|
|
|
|
if [ -z "$key_id" ]; then
|
|
echo -e "${RED}Error: Key ID required${NC}"
|
|
echo "Usage: $0 revoke <key_id>"
|
|
exit 1
|
|
fi
|
|
|
|
if ! grep -q "^${key_id}|" "$KEYS_FILE" 2>/dev/null; then
|
|
echo -e "${RED}Error: Key ID not found${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
# Remove from keys file
|
|
sed -i "/^${key_id}|/d" "$KEYS_FILE"
|
|
|
|
# Rebuild nginx map
|
|
rebuild_nginx_map
|
|
|
|
log_action "REVOKE" "$key_id" "revoked"
|
|
|
|
echo -e "${GREEN}Key revoked: $key_id${NC}"
|
|
echo "Run 'nginx -s reload' to apply changes"
|
|
}
|
|
|
|
# List all keys (without secrets)
|
|
list_keys() {
|
|
echo "API Keys"
|
|
echo "========"
|
|
echo ""
|
|
printf "%-40s %-15s %-10s %s\n" "KEY_ID" "NAME" "SCOPE" "CREATED"
|
|
printf "%-40s %-15s %-10s %s\n" "------" "----" "-----" "-------"
|
|
|
|
if [ -f "$KEYS_FILE" ] && [ -s "$KEYS_FILE" ]; then
|
|
while IFS='|' read -r key_id name scope created_at hash; do
|
|
printf "%-40s %-15s %-10s %s\n" "$key_id" "$name" "$scope" "${created_at:0:19}"
|
|
done < "$KEYS_FILE"
|
|
else
|
|
echo "(no keys)"
|
|
fi
|
|
|
|
echo ""
|
|
echo "Total: $(wc -l < "$KEYS_FILE" 2>/dev/null || echo 0) keys"
|
|
}
|
|
|
|
# Verify a key (returns key_id if valid)
|
|
verify_key() {
|
|
local api_key="$1"
|
|
|
|
if [ -z "$api_key" ]; then
|
|
echo -e "${RED}Error: API key required${NC}"
|
|
exit 1
|
|
fi
|
|
|
|
local key_hash=$(echo -n "$api_key" | sha256sum | cut -d' ' -f1)
|
|
|
|
while IFS='|' read -r key_id name scope created_at stored_hash; do
|
|
if [ "$key_hash" = "$stored_hash" ]; then
|
|
echo -e "${GREEN}Valid${NC}"
|
|
echo "Key ID: $key_id"
|
|
echo "Name: $name"
|
|
echo "Scope: $scope"
|
|
log_action "VERIFY" "$key_id" "valid"
|
|
exit 0
|
|
fi
|
|
done < "$KEYS_FILE"
|
|
|
|
echo -e "${RED}Invalid key${NC}"
|
|
log_action "VERIFY" "unknown" "invalid"
|
|
exit 1
|
|
}
|
|
|
|
# Rotate health token
|
|
rotate_health_token() {
|
|
local new_token=$(generate_key)
|
|
local old_token=""
|
|
|
|
# Read old token if exists
|
|
if [ -f "$HEALTH_TOKEN_FILE" ] && [ -s "$HEALTH_TOKEN_FILE" ]; then
|
|
old_token=$(grep "^current=" "$HEALTH_TOKEN_FILE" | cut -d'=' -f2)
|
|
fi
|
|
|
|
# Write new config (current + previous for grace period)
|
|
cat > "$HEALTH_TOKEN_FILE" << EOF
|
|
# Health Token Configuration
|
|
# Generated: $(date -Iseconds)
|
|
# Previous token valid for 24h after rotation
|
|
current=$new_token
|
|
previous=$old_token
|
|
rotated_at=$(date -Iseconds)
|
|
EOF
|
|
|
|
chmod 600 "$HEALTH_TOKEN_FILE"
|
|
|
|
# Update nginx include
|
|
cat > "$KEYS_DIR/nginx-health-token.conf" << EOF
|
|
# Auto-generated health token map
|
|
# Do not edit manually
|
|
map \$http_x_health_token \$health_token_valid {
|
|
default 0;
|
|
"$new_token" 1;
|
|
EOF
|
|
|
|
# Add previous token if exists (grace period)
|
|
if [ -n "$old_token" ]; then
|
|
echo " \"$old_token\" 1; # previous (grace period)" >> "$KEYS_DIR/nginx-health-token.conf"
|
|
fi
|
|
|
|
echo "}" >> "$KEYS_DIR/nginx-health-token.conf"
|
|
chmod 600 "$KEYS_DIR/nginx-health-token.conf"
|
|
|
|
log_action "ROTATE_HEALTH" "-" "token rotated"
|
|
|
|
echo -e "${GREEN}Health Token Rotated${NC}"
|
|
echo "================================"
|
|
echo "New Token: $new_token"
|
|
echo "================================"
|
|
echo -e "${YELLOW}Update your monitoring systems with the new token.${NC}"
|
|
echo "Previous token remains valid for grace period."
|
|
echo "Run 'nginx -s reload' to apply changes"
|
|
}
|
|
|
|
# Rebuild nginx API keys map
|
|
rebuild_nginx_map() {
|
|
cat > "$NGINX_KEYS_MAP" << 'HEADER'
|
|
# Auto-generated API keys map
|
|
# Do not edit manually - use api-keys.sh
|
|
# Format: key hash -> "key_id:scope"
|
|
|
|
map $api_key_hash $api_key_info {
|
|
default "";
|
|
HEADER
|
|
|
|
while IFS='|' read -r key_id name scope created_at hash; do
|
|
echo " \"$hash\" \"$key_id:$scope\";" >> "$NGINX_KEYS_MAP"
|
|
done < "$KEYS_FILE"
|
|
|
|
echo "}" >> "$NGINX_KEYS_MAP"
|
|
chmod 600 "$NGINX_KEYS_MAP"
|
|
}
|
|
|
|
# Show usage
|
|
usage() {
|
|
echo "NODE1 API Key Management"
|
|
echo ""
|
|
echo "Usage:"
|
|
echo " $0 create <name> [--admin] Create new API key"
|
|
echo " $0 revoke <key_id> Revoke API key"
|
|
echo " $0 list List all keys"
|
|
echo " $0 verify <api_key> Verify API key"
|
|
echo " $0 rotate-health Rotate health check token"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " $0 create monitoring"
|
|
echo " $0 create admin-user --admin"
|
|
echo " $0 revoke kid_monitoring_20260126_abc123"
|
|
}
|
|
|
|
# Main
|
|
init_dirs
|
|
|
|
case "${1:-}" in
|
|
create)
|
|
create_key "$2" "$3"
|
|
;;
|
|
revoke)
|
|
revoke_key "$2"
|
|
;;
|
|
list)
|
|
list_keys
|
|
;;
|
|
verify)
|
|
verify_key "$2"
|
|
;;
|
|
rotate-health)
|
|
rotate_health_token
|
|
;;
|
|
*)
|
|
usage
|
|
;;
|
|
esac
|