The Admin API provides endpoints for health checks, status monitoring, and configuration management.
Enable the Admin API by setting metrics_addr in the global configuration:
global:
metrics_addr: "127.0.0.1:9090"
admin_api:
token: "your-secret-token" # Optional: Bearer token for authentication
allowed_ips: # Optional: IP allowlist
- "127.0.0.1"
- "10.0.0.0/8"When authentication is configured, all endpoints except /health require authentication.
If admin_api.token is configured, requests must include the Authorization header:
curl -H "Authorization: Bearer your-secret-token" http://127.0.0.1:9090/statusResponse without token (401 Unauthorized):
Unauthorized
If admin_api.allowed_ips is configured, requests must originate from an allowed IP/CIDR:
# From allowed IP - succeeds
curl http://127.0.0.1:9090/status
# From disallowed IP - fails with 403
curl http://192.168.1.100:9090/statusResponse from disallowed IP (403 Forbidden):
Forbidden
When both token and IP allowlist are configured:
- IP check is performed first
- If IP is allowed, token is validated
- Both must pass for access
Security Note: Always configure at least one authentication method in production. The /health endpoint remains unauthenticated for load balancer health checks.
Simple health check endpoint for load balancers and monitoring systems.
Response
{
"status": "ok"
}Status Codes
200 OK- Service is healthy
Example
curl http://127.0.0.1:9090/healthDetailed status information including version, uptime, and resource usage.
Response
{
"status": "running",
"version": "1.0.0",
"uptime": "2h30m15s",
"go_version": "go1.21.0",
"num_cpu": 8,
"goroutines": 42,
"memory": {
"alloc_bytes": 12582912,
"total_alloc_bytes": 45678901,
"sys_bytes": 25165824,
"num_gc": 15
}
}Fields
| Field | Type | Description |
|---|---|---|
status |
string | Service status (running) |
version |
string | ShadowGate version |
uptime |
string | Time since start |
go_version |
string | Go runtime version |
num_cpu |
int | Available CPU cores |
goroutines |
int | Active goroutines |
memory.alloc_bytes |
uint64 | Current heap allocation |
memory.total_alloc_bytes |
uint64 | Total bytes allocated |
memory.sys_bytes |
uint64 | Memory from OS |
memory.num_gc |
uint32 | GC cycles completed |
Example
curl http://127.0.0.1:9090/statusRequest metrics and statistics.
Response
{
"uptime": "2h30m15s",
"total_requests": 150000,
"allowed_requests": 125000,
"denied_requests": 25000,
"dropped_requests": 500,
"unique_ips": 5000,
"avg_response_ms": 12.5,
"requests_per_sec": 15.2,
"profile_requests": {
"c2-front": 100000,
"phishing": 50000
},
"decisions": {
"allow_forward": 125000,
"deny_decoy": 24000,
"drop": 500,
"redirect": 300,
"tarpit": 200
},
"rule_hits": {
"ip_allow": 125000,
"ua_blacklist": 15000,
"geo_deny": 5000,
"rate_limit": 5000
},
"backend_stats": {
"backend1": {
"requests": 75000,
"errors": 150,
"error_rate": 0.2,
"avg_latency_ms": 8.5,
"min_latency_ms": 1.2,
"max_latency_ms": 245.8
},
"backend2": {
"requests": 50000,
"errors": 100,
"error_rate": 0.2,
"avg_latency_ms": 12.3,
"min_latency_ms": 2.1,
"max_latency_ms": 189.4
}
}
}Fields
| Field | Type | Description |
|---|---|---|
uptime |
string | Time since start |
total_requests |
int64 | Total requests processed |
allowed_requests |
int64 | Requests forwarded to backends |
denied_requests |
int64 | Requests served decoys |
dropped_requests |
int64 | Requests dropped |
unique_ips |
int | Unique client IPs seen |
avg_response_ms |
float64 | Average response time |
requests_per_sec |
float64 | Current request rate |
profile_requests |
map | Requests per profile |
decisions |
map | Count by decision type |
rule_hits |
map | Count by rule type |
backend_stats |
map | Per-backend statistics |
Backend Stats Fields
| Field | Type | Description |
|---|---|---|
requests |
int64 | Total requests to backend |
errors |
int64 | Failed requests (5xx responses) |
error_rate |
float64 | Error percentage |
avg_latency_ms |
float64 | Average response latency |
min_latency_ms |
float64 | Minimum observed latency |
max_latency_ms |
float64 | Maximum observed latency |
Example
curl http://127.0.0.1:9090/metricsRequest metrics in Prometheus exposition format. This endpoint is compatible with Prometheus scrapers.
Response
# HELP shadowgate_requests_total Total number of requests processed
# TYPE shadowgate_requests_total counter
shadowgate_requests_total 150000
# HELP shadowgate_requests_allowed_total Total number of allowed requests
# TYPE shadowgate_requests_allowed_total counter
shadowgate_requests_allowed_total 125000
# HELP shadowgate_requests_denied_total Total number of denied requests
# TYPE shadowgate_requests_denied_total counter
shadowgate_requests_denied_total 25000
# HELP shadowgate_requests_dropped_total Total number of dropped requests
# TYPE shadowgate_requests_dropped_total counter
shadowgate_requests_dropped_total 500
# HELP shadowgate_unique_ips Number of unique client IPs seen
# TYPE shadowgate_unique_ips gauge
shadowgate_unique_ips 5000
# HELP shadowgate_response_time_ms_avg Average response time in milliseconds
# TYPE shadowgate_response_time_ms_avg gauge
shadowgate_response_time_ms_avg 12.500
# HELP shadowgate_requests_per_second Current request rate
# TYPE shadowgate_requests_per_second gauge
shadowgate_requests_per_second 15.200
# HELP shadowgate_profile_requests_total Requests per profile
# TYPE shadowgate_profile_requests_total counter
shadowgate_profile_requests_total{profile="c2-front"} 100000
shadowgate_profile_requests_total{profile="phishing"} 50000
# HELP shadowgate_decisions_total Counts by decision type
# TYPE shadowgate_decisions_total counter
shadowgate_decisions_total{decision="allow_forward"} 125000
shadowgate_decisions_total{decision="deny_decoy"} 24000
# HELP shadowgate_rule_hits_total Counts by rule type
# TYPE shadowgate_rule_hits_total counter
shadowgate_rule_hits_total{rule="ip_allow"} 125000
shadowgate_rule_hits_total{rule="ua_blacklist"} 15000
# HELP shadowgate_backend_requests_total Total requests per backend
# TYPE shadowgate_backend_requests_total counter
shadowgate_backend_requests_total{backend="backend1"} 75000
shadowgate_backend_requests_total{backend="backend2"} 50000
# HELP shadowgate_backend_errors_total Total errors per backend
# TYPE shadowgate_backend_errors_total counter
shadowgate_backend_errors_total{backend="backend1"} 150
shadowgate_backend_errors_total{backend="backend2"} 100
# HELP shadowgate_backend_latency_ms_avg Average latency per backend in milliseconds
# TYPE shadowgate_backend_latency_ms_avg gauge
shadowgate_backend_latency_ms_avg{backend="backend1"} 8.500
shadowgate_backend_latency_ms_avg{backend="backend2"} 12.300
# HELP shadowgate_backend_latency_ms_min Minimum latency per backend in milliseconds
# TYPE shadowgate_backend_latency_ms_min gauge
shadowgate_backend_latency_ms_min{backend="backend1"} 1.200
shadowgate_backend_latency_ms_min{backend="backend2"} 2.100
# HELP shadowgate_backend_latency_ms_max Maximum latency per backend in milliseconds
# TYPE shadowgate_backend_latency_ms_max gauge
shadowgate_backend_latency_ms_max{backend="backend1"} 245.800
shadowgate_backend_latency_ms_max{backend="backend2"} 189.400
# HELP shadowgate_backend_error_rate Error rate per backend (percentage)
# TYPE shadowgate_backend_error_rate gauge
shadowgate_backend_error_rate{backend="backend1"} 0.20
shadowgate_backend_error_rate{backend="backend2"} 0.20
# HELP shadowgate_circuit_breaker_state Circuit breaker state (0=closed, 1=open, 2=half-open)
# TYPE shadowgate_circuit_breaker_state gauge
shadowgate_circuit_breaker_state{profile="c2-front",backend="backend1"} 0
shadowgate_circuit_breaker_state{profile="c2-front",backend="backend2"} 0
# HELP shadowgate_circuit_breaker_failures Current consecutive failure count
# TYPE shadowgate_circuit_breaker_failures gauge
shadowgate_circuit_breaker_failures{profile="c2-front",backend="backend1"} 0
shadowgate_circuit_breaker_failures{profile="c2-front",backend="backend2"} 2
# HELP shadowgate_circuit_breaker_successes Current consecutive success count in half-open state
# TYPE shadowgate_circuit_breaker_successes gauge
shadowgate_circuit_breaker_successes{profile="c2-front",backend="backend1"} 0
shadowgate_circuit_breaker_successes{profile="c2-front",backend="backend2"} 0
# HELP shadowgate_backend_healthy Backend health status (1=healthy, 0=unhealthy)
# TYPE shadowgate_backend_healthy gauge
shadowgate_backend_healthy{profile="c2-front",backend="backend1"} 1
shadowgate_backend_healthy{profile="c2-front",backend="backend2"} 1
Prometheus Configuration
scrape_configs:
- job_name: 'shadowgate'
static_configs:
- targets: ['127.0.0.1:9090']
metrics_path: '/metrics/prometheus'
# If authentication is configured:
# authorization:
# type: Bearer
# credentials: your-secret-tokenExample
curl http://127.0.0.1:9090/metrics/prometheus
# With authentication
curl -H "Authorization: Bearer your-secret-token" http://127.0.0.1:9090/metrics/prometheusBackend pool status, health information, and circuit breaker state.
Response
{
"profiles": {
"c2-front": {
"total": 2,
"healthy": 2,
"backends": [
{
"name": "c2-primary",
"url": "http://10.0.1.10:8080",
"weight": 10,
"healthy": true,
"last_check": "2024-01-15T10:30:00Z",
"last_healthy": "2024-01-15T10:30:00Z",
"check_count": 1500,
"fail_count": 0,
"circuit_breaker": {
"state": "closed",
"failures": 0,
"successes": 0,
"last_state_change": "2024-01-15T08:00:00Z"
}
},
{
"name": "c2-secondary",
"url": "http://10.0.1.11:8080",
"weight": 5,
"healthy": true,
"last_check": "2024-01-15T10:30:00Z",
"last_healthy": "2024-01-15T10:30:00Z",
"check_count": 1500,
"fail_count": 2,
"circuit_breaker": {
"state": "closed",
"failures": 2,
"successes": 0,
"last_state_change": "2024-01-15T10:25:00Z"
}
}
]
}
}
}Backend Fields
| Field | Type | Description |
|---|---|---|
name |
string | Backend identifier |
url |
string | Backend URL |
weight |
int | Load balancing weight |
healthy |
bool | Current health status |
last_check |
string | Last health check time (RFC3339) |
last_healthy |
string | Last successful check time |
check_count |
int64 | Total health checks performed |
fail_count |
int64 | Failed health checks |
circuit_breaker |
object | Circuit breaker state |
Circuit Breaker Fields
| Field | Type | Description |
|---|---|---|
state |
string | Current state: closed, open, or half-open |
failures |
int | Consecutive failure count |
successes |
int | Consecutive success count (in half-open state) |
last_state_change |
string | Last state transition time (RFC3339) |
Circuit Breaker States
| State | Description |
|---|---|
closed |
Normal operation, requests flow to backend |
open |
Backend failing, requests are rejected (503) |
half-open |
Testing recovery, limited requests allowed |
Example
curl http://127.0.0.1:9090/backendsValidate configuration file. This endpoint validates the configuration without applying changes.
Note: Currently validates configuration only. A service restart is required for changes to take effect. True hot reload may be added in a future version.
Response (Success)
{
"success": true,
"message": "Configuration validated successfully"
}Response (Failure)
{
"success": false,
"message": "failed to parse configuration: invalid YAML at line 42"
}Status Codes
200 OK- Reload completed (checksuccessfield)405 Method Not Allowed- Must use POST method
Example
curl -X POST http://127.0.0.1:9090/reloadAll endpoints return errors in a consistent format:
{
"error": "error message"
}Common Status Codes
| Code | Description |
|---|---|
200 |
Success |
405 |
Method not allowed |
500 |
Internal server error |
503 |
Service unavailable |
The /metrics endpoint returns JSON format metrics. To integrate with monitoring systems:
# Fetch metrics as JSON
curl -s http://127.0.0.1:9090/metrics | jq .
# Example: Extract total requests with jq
curl -s http://127.0.0.1:9090/metrics | jq '.total_requests'#!/bin/bash
# health_check.sh
response=$(curl -s -o /dev/null -w "%{http_code}" http://127.0.0.1:9090/health)
if [ "$response" = "200" ]; then
exit 0
else
exit 1
fi#!/bin/bash
# Check for unhealthy backends
unhealthy=$(curl -s http://127.0.0.1:9090/backends | jq '.profiles[].healthy < .profiles[].total' | grep true)
if [ -n "$unhealthy" ]; then
echo "Alert: Some backends are unhealthy"
exit 1
fi