Configuration Reference
Sentinel is configured by passing a sentinel.Config struct to sentinel.Mount(). Every field has a sensible default, so you can start with an empty struct and enable features incrementally. This page documents every configuration section, its fields, default values, and usage patterns.
Zero-Value Friendly
sentinel.Config{} gives you a working setup with SQLite storage, the dashboard enabled, security headers active, and performance monitoring on. Features like WAF, rate limiting, and anomaly detection are opt-in via their Enabled field.Complete Example
Below is a fully configured Sentinel instance with every major feature enabled. In practice, you only need to set the fields relevant to your application.
1package main23import (4 "time"56 sentinel "github.com/MUKE-coder/sentinel/v2"7 "github.com/gin-gonic/gin"8)910func main() {11 r := gin.Default()1213 sentinel.Mount(r, nil, sentinel.Config{14 Dashboard: sentinel.DashboardConfig{15 Username: "admin",16 Password: "s3cur3-p4ss!",17 SecretKey: "my-jwt-secret-change-in-production",18 },1920 Storage: sentinel.StorageConfig{21 Driver: sentinel.SQLite,22 DSN: "sentinel.db",23 RetentionDays: 90,24 MaxOpenConns: 10,25 MaxIdleConns: 5,26 },2728 WAF: sentinel.WAFConfig{29 Enabled: true,30 Mode: sentinel.ModeBlock,31 Rules: sentinel.RuleSet{32 SQLInjection: sentinel.RuleStrict,33 XSS: sentinel.RuleStrict,34 PathTraversal: sentinel.RuleStrict,35 CommandInjection: sentinel.RuleStrict,36 SSRF: sentinel.RuleMedium,37 XXE: sentinel.RuleStrict,38 LFI: sentinel.RuleStrict,39 OpenRedirect: sentinel.RuleMedium,40 },41 CustomRules: []sentinel.WAFRule{42 {43 ID: "block-admin-enum",44 Name: "Block admin enumeration",45 Pattern: `(?i)/(wp-admin|phpmyadmin|administrator)`,46 AppliesTo: []string{"path"},47 Severity: sentinel.SeverityMedium,48 Action: "block",49 Enabled: true,50 },51 },52 ExcludeRoutes: []string{"/health", "/metrics"},53 ExcludeIPs: []string{"10.0.0.0/8"},54 },5556 RateLimit: sentinel.RateLimitConfig{57 Enabled: true,58 Strategy: sentinel.SlidingWindow,59 ByIP: &sentinel.Limit{Requests: 100, Window: time.Minute},60 ByUser: &sentinel.Limit{Requests: 500, Window: time.Minute},61 Global: &sentinel.Limit{Requests: 5000, Window: time.Minute},62 ByRoute: map[string]sentinel.Limit{63 "/api/login": {Requests: 5, Window: 15 * time.Minute},64 "/api/register": {Requests: 3, Window: time.Hour},65 },66 },6768 AuthShield: sentinel.AuthShieldConfig{69 Enabled: true,70 LoginRoute: "/api/login",71 MaxFailedAttempts: 5,72 LockoutDuration: 15 * time.Minute,73 CredentialStuffingDetection: true,74 BruteForceDetection: true,75 },7677 Headers: sentinel.HeaderConfig{78 ContentSecurityPolicy: "default-src 'self'",79 StrictTransportSecurity: true,80 XFrameOptions: "DENY",81 XContentTypeOptions: true,82 ReferrerPolicy: "strict-origin-when-cross-origin",83 PermissionsPolicy: "camera=(), microphone=(), geolocation=()",84 },8586 Anomaly: sentinel.AnomalyConfig{87 Enabled: true,88 Sensitivity: sentinel.AnomalySensitivityMedium,89 LearningPeriod: 7 * 24 * time.Hour,90 Checks: []sentinel.AnomalyCheckType{91 sentinel.CheckImpossibleTravel,92 sentinel.CheckUnusualAccess,93 sentinel.CheckDataExfiltration,94 sentinel.CheckOffHoursAccess,95 sentinel.CheckVelocityAnomaly,96 },97 },9899 IPReputation: sentinel.IPReputationConfig{100 Enabled: true,101 AbuseIPDBKey: "your-abuseipdb-api-key",102 AutoBlock: true,103 MinAbuseScore: 80,104 },105106 Geo: sentinel.GeoConfig{107 Enabled: true,108 Provider: sentinel.GeoIPFree,109 },110111 Alerts: sentinel.AlertConfig{112 MinSeverity: sentinel.SeverityHigh,113 Slack: &sentinel.SlackConfig{114 WebhookURL: "https://hooks.slack.com/services/...",115 },116 Email: &sentinel.EmailConfig{117 SMTPHost: "smtp.example.com",118 SMTPPort: 587,119 Username: "alerts@example.com",120 Password: "smtp-password",121 Recipients: []string{"security@example.com"},122 },123 Webhook: &sentinel.WebhookConfig{124 URL: "https://your-siem.example.com/webhook",125 Headers: map[string]string{126 "Authorization": "Bearer your-token",127 },128 },129 },130131 AI: &sentinel.AIConfig{132 Provider: sentinel.Claude,133 APIKey: "your-anthropic-api-key",134 Model: "claude-sonnet-4-20250514",135 },136137 UserExtractor: func(c *gin.Context) *sentinel.UserContext {138 userID := c.GetHeader("X-User-ID")139 if userID == "" {140 return nil141 }142 return &sentinel.UserContext{143 ID: userID,144 Email: c.GetHeader("X-User-Email"),145 Role: c.GetHeader("X-User-Role"),146 }147 },148149 Performance: sentinel.PerformanceConfig{150 SlowRequestThreshold: 2 * time.Second,151 SlowQueryThreshold: 500 * time.Millisecond,152 TrackMemory: true,153 TrackGoroutines: true,154 },155 })156157 r.GET("/api/hello", func(c *gin.Context) {158 c.JSON(200, gin.H{"message": "Hello, World!"})159 })160161 r.Run(":8080")162}
Dashboard
The DashboardConfig controls the embedded React security dashboard. The dashboard is enabled by default and served under the configured prefix. It is protected by JWT-based authentication using the username and password you provide.
| Field | Type | Default | Description |
|---|---|---|---|
Enabled | *bool | true | Whether the dashboard is served. Set to false to disable entirely. |
Prefix | string | /sentinel | URL prefix for the dashboard and API routes. The UI is available at {prefix}/ui. |
Username | string | admin | Username for dashboard login. |
Password | string | sentinel | Password for dashboard login. |
SecretKey | string | Random, generated at each start | Secret key used to sign JWT tokens for dashboard sessions. |
1Dashboard: sentinel.DashboardConfig{2 Prefix: "/admin/security",3 Username: "secops",4 Password: "change-me-in-production",5 SecretKey: "a-strong-random-secret",6}
Change Default Credentials
admin/sentinel) and secret key are intended for development only. Always set strong values in production. The SecretKey is used to sign JWT tokens — if it is compromised, attackers can forge dashboard sessions.Disabling the Dashboard
Enabled to false. Because the field is a *bool, you need to use a pointer:disabled := falseDashboard: sentinel.DashboardConfig{Enabled: &disabled,}
Storage
The StorageConfig controls how Sentinel persists security events, threat actors, rate limit counters, and other data. Sentinel ships with two built-in storage drivers and supports additional drivers.
| Field | Type | Default | Description |
|---|---|---|---|
Driver | StorageDriver | sentinel.SQLite | Storage backend. Options: sentinel.SQLite, sentinel.Postgres, sentinel.Memory. |
DSN | string | sentinel.db | Data source name. For SQLite, this is a file path. For Postgres, a full connection string. |
RetentionDays | int | 90 | Days to keep threat events, user activity, and performance metrics before the hourly cleanup deletes them. Audit logs are not affected. |
AuditRetentionDays | int | 365 | Days to keep audit log entries. Separate from RetentionDays because audit history is evidence: PCI-DSS 10.5.1 requires 12 months. ValidateConfig warns below 365. (v2.3.0+) |
AuditKey | string | "" | HMAC key for the audit log's hash chain. Without it the chain is plain SHA-256; with it, rewriting entries undetectably requires the key. Keep it outside the database and stable. (v2.4.0+; see Audit Logging.) |
SyncInterval | time.Duration | 5s | How often this replica picks up changes made elsewhere: IP blocks and whitelist entries written by another replica, and dashboard setting changes. A negative value turns polling off, so this process only sees its own changes. (v2.6.0+) |
MaxOpenConns | int | 10 | Maximum number of open database connections. |
MaxIdleConns | int | 5 | Maximum number of idle database connections. |
Storage Drivers
| Driver | Constant | Notes |
|---|---|---|
| SQLite | sentinel.SQLite | The default. Pure Go (no CGo); data persists to a file. In containers, put the file on a persistent volume — an ephemeral filesystem loses it on every deploy. |
| Postgres | sentinel.Postgres | Recommended for production. Required for IP blocks to reach every replica (each instance re-reads the block list every 30 seconds). |
| Memory | sentinel.Memory | Development and tests only. Everything is lost on restart, and compliance reports are refused in release mode (HTTP 409) because they would cover only the time since the last start. |
| MySQL | sentinel.MySQL | Not implemented. Selecting it silently falls back to in-memory storage; ValidateConfig reports it as an error. |
1// SQLite (default, recommended)2Storage: sentinel.StorageConfig{3 Driver: sentinel.SQLite,4 DSN: "sentinel.db",5 RetentionDays: 30,6}78// In-memory (for development/testing)9Storage: sentinel.StorageConfig{10 Driver: sentinel.Memory,11}1213// PostgreSQL14Storage: sentinel.StorageConfig{15 Driver: sentinel.Postgres,16 DSN: "postgres://user:pass@localhost:5432/sentinel?sslmode=disable",17 MaxOpenConns: 25,18 MaxIdleConns: 10,19}
No CGo Required
github.com/glebarez/sqlite), so you do not need CGo or any C compiler installed. This makes cross-compilation and Docker builds straightforward.WAF (Web Application Firewall)
The WAFConfig controls the built-in Web Application Firewall. When enabled, it inspects incoming requests for SQL injection, XSS, path traversal, command injection, SSRF, XXE, LFI, and open redirect attacks. It can operate in log-only mode (observe without blocking) or block mode (reject malicious requests with a 403 status).
| Field | Type | Default | Description |
|---|---|---|---|
Enabled | bool | false | Enables the WAF middleware. Must be set to true to activate. |
Mode | WAFMode | sentinel.ModeLog | How detected threats are handled. Options: sentinel.ModeLog, sentinel.ModeBlock, sentinel.ModeChallenge. |
Rules | RuleSet | RuleStrict; RuleMedium for SSRF and open redirect | Per-category sensitivity levels for built-in detection rules. Enforced since v2.3.0 — see below. |
CustomRules | []WAFRule | nil | Custom regex-based rules for application-specific patterns. |
ExcludeRoutes | []string | nil | Routes to exclude from WAF inspection (e.g., health check endpoints). |
ExcludeIPs | []string | nil | IP addresses or CIDR ranges to exclude from WAF inspection. |
AllowedRedirectHosts | []string | nil | Hosts your application owns, so a redirect to one of them is treated as a callback instead of an open redirect. Entries are hosts (example.com, port ignored) or wildcards (*.example.com, any subdomain but not the bare domain). Without this, every absolute URL in a redirect parameter is reported — Sentinel can't know your hostnames. (v2.6.0+) |
TrustedProxies | []string | nil | IPs or CIDRs of your reverse proxies. X-Forwarded-For / X-Real-IP are honored only when the direct connection comes from one of them, and the chain is read right to left — the first address that isn't one of your proxies is the client. Used by every per-IP feature, not just the WAF. Empty means proxy headers are ignored. |
MaxBodyBytes | int64 | 65536 | How much of each request body the WAF reads and inspects. |
RejectOversizedBody | bool | false | Reject bodies larger than MaxBodyBytes with 413 instead of inspecting only the first part. |
WAF Modes
| Mode | Constant | Behavior |
|---|---|---|
| Log | sentinel.ModeLog | Detects and logs threats but allows the request to proceed. Good for initial rollout. |
| Block | sentinel.ModeBlock | Detects threats and rejects the request with HTTP 403. Recommended for production. |
| Challenge | sentinel.ModeChallenge | Rejects the request with HTTP 429, Retry-After: 30, and code WAF_CHALLENGE. There is no interactive challenge page. |
RuleSet — Per-Category Sensitivity
Each built-in detection category can have its sensitivity tuned independently. The available levels are sentinel.RuleOff, sentinel.RuleLow, sentinel.RuleMedium, and sentinel.RuleStrict. Each level keeps the built-in patterns at or above a confidence floor:
| Level | Patterns kept |
|---|---|
RuleOff | None — the category is disabled. |
RuleLow | Only the most precise patterns (confidence ≥ 80). |
RuleMedium | Everything except the noisiest patterns (confidence ≥ 60) — for example the bare SQL-comment pattern is dropped. |
RuleStrict (or empty) | Every pattern. |
Enforced since v2.3.0
RuleOff, RuleLow, or RuleMedium now actually changes detection. An unknown value is enforced as strict and reported by ValidateConfig. The dashboard can change levels on the running WAF via PUT /sentinel/api/waf/rules.| Field | Default | Description |
|---|---|---|
SQLInjection | RuleStrict | SQL injection detection sensitivity. |
XSS | RuleStrict | Cross-site scripting detection sensitivity. |
PathTraversal | RuleStrict | Path traversal (../../etc/passwd) detection sensitivity. |
CommandInjection | RuleStrict | OS command injection detection sensitivity. |
SSRF | RuleMedium | Server-side request forgery detection sensitivity. |
XXE | RuleStrict | XML external entity injection detection sensitivity. |
LFI | RuleStrict | Local file inclusion detection sensitivity. |
OpenRedirect | RuleMedium | Open redirect detection sensitivity. |
Custom WAF Rules
Custom rules let you add application-specific detection patterns using regular expressions. Each rule is defined as a WAFRule struct.
| Field | Type | Description |
|---|---|---|
ID | string | Unique identifier for the rule. |
Name | string | Human-readable name displayed in the dashboard. |
Pattern | string | Go-compatible regular expression to match against request data. |
AppliesTo | []string | Which parts of the request to inspect: "path", "query", "header", "body". Empty means all four. Any other value is never scanned and is reported by ValidateConfig. |
Severity | Severity | Severity level: sentinel.SeverityLow, SeverityMedium, SeverityHigh, SeverityCritical. |
Action | string | "block" (or empty) lets the WAF mode decide; "log" records matches but never blocks, even in block mode — use it to watch a new rule against real traffic before trusting it. A request that also trips an enforced rule is still blocked. (Enforced since v2.3.0.) |
Enabled | bool | Whether the rule is active. |
1WAF: sentinel.WAFConfig{2 Enabled: true,3 Mode: sentinel.ModeBlock,4 Rules: sentinel.RuleSet{5 SQLInjection: sentinel.RuleStrict,6 XSS: sentinel.RuleStrict,7 PathTraversal: sentinel.RuleStrict,8 CommandInjection: sentinel.RuleStrict,9 SSRF: sentinel.RuleMedium,10 XXE: sentinel.RuleStrict,11 LFI: sentinel.RuleStrict,12 OpenRedirect: sentinel.RuleMedium,13 },14 CustomRules: []sentinel.WAFRule{15 {16 ID: "block-admin-enum",17 Name: "Block admin enumeration",18 Pattern: `(?i)/(wp-admin|phpmyadmin|administrator)`,19 AppliesTo: []string{"path"},20 Severity: sentinel.SeverityMedium,21 Action: "block",22 Enabled: true,23 },24 {25 ID: "block-sensitive-files",26 Name: "Block sensitive file access",27 Pattern: `(?i)\.(env|git|bak|sql|log)$`,28 AppliesTo: []string{"path"},29 Severity: sentinel.SeverityHigh,30 Action: "block",31 Enabled: true,32 },33 },34 ExcludeRoutes: []string{"/health", "/readiness"},35 ExcludeIPs: []string{"127.0.0.1", "10.0.0.0/8"},36}
Recommended Rollout Strategy
sentinel.ModeLog in production to observe what the WAF detects without blocking real traffic. Review the dashboard for false positives, tune rule sensitivities, then switch to sentinel.ModeBlock when confident. Once in block mode, add new custom rules with Action: "log" first and promote them to "block" after watching them against real traffic.Rate Limiting
The RateLimitConfig enables multi-dimensional rate limiting. You can set limits per IP, per authenticated user, per route, and globally. All dimensions are enforced independently — a request must pass all applicable limits.
| Field | Type | Default | Description |
|---|---|---|---|
Enabled | bool | false | Enables the rate limiting middleware. |
Strategy | RateLimitStrategy | sentinel.SlidingWindow | Algorithm used for counting. Options: sentinel.SlidingWindow, sentinel.FixedWindow, sentinel.TokenBucket. |
ByIP | *Limit | nil | Per-IP rate limit. Each unique client IP gets its own counter. |
ByUser | *Limit | nil | Per-user rate limit. Requires a UserIDExtractor or UserExtractor. |
ByRoute | map[string]Limit | nil | Per-route rate limits. Keys are route patterns (e.g., /api/login). |
Global | *Limit | nil | Global rate limit applied across all requests regardless of source. |
UserIDExtractor | func(*gin.Context) string | nil | Function to extract a user ID from the request for per-user limiting. |
The Limit Struct
Each limit is defined by a number of allowed requests within a time window.
| Field | Type | Description |
|---|---|---|
Requests | int | Maximum number of requests allowed within the window. |
Window | time.Duration | Time window for the limit (e.g., time.Minute, 15 * time.Minute). |
Rate Limit Strategies
| Strategy | Constant | Description |
|---|---|---|
| Sliding Window | sentinel.SlidingWindow | Default. Counts requests over the window ending now (the previous window's count, weighted by how much of it still overlaps, plus the current one), so twice the limit can't pass in a moment either side of a window boundary. Rejected requests aren't counted. |
| Fixed Window | sentinel.FixedWindow | Consecutive windows starting at a client's first request; the counter resets completely at each boundary, so up to twice the limit can pass across one. Rejected requests count. This was the only behavior before v2.3.0, whatever Strategy said. |
| Token Bucket | sentinel.TokenBucket | A bucket of Requests tokens refilled at Requests per Window: allows a burst up to the limit, then a steady rate. |
1RateLimit: sentinel.RateLimitConfig{2 Enabled: true,3 Strategy: sentinel.SlidingWindow,45 // 100 requests per minute per IP6 ByIP: &sentinel.Limit{Requests: 100, Window: time.Minute},78 // 500 requests per minute per authenticated user9 ByUser: &sentinel.Limit{Requests: 500, Window: time.Minute},1011 // 5000 requests per minute globally12 Global: &sentinel.Limit{Requests: 5000, Window: time.Minute},1314 // Strict limits on sensitive routes15 ByRoute: map[string]sentinel.Limit{16 "/api/login": {Requests: 5, Window: 15 * time.Minute},17 "/api/register": {Requests: 3, Window: time.Hour},18 "/api/password-reset": {Requests: 3, Window: time.Hour},19 },2021 // Extract user ID from your auth system22 UserIDExtractor: func(c *gin.Context) string {23 return c.GetHeader("X-User-ID")24 },25}
Rate Limit Headers
X-RateLimit-Limit, X-RateLimit-Remaining, and X-RateLimit-Reset. When a limit is exceeded, the client receives a 429 Too Many Requests response.Auth Shield
The AuthShieldConfig protects authentication endpoints from brute-force attacks and credential stuffing. When enabled, it monitors the configured login route for failed authentication attempts and automatically locks out offending IPs.
| Field | Type | Default | Description |
|---|---|---|---|
Enabled | bool | false | Enables the Auth Shield middleware. |
LoginRoute | string | "" | The route path for your login endpoint (e.g., /api/login). |
MaxFailedAttempts | int | 5 | Number of failed login attempts before lockout is triggered. |
LockoutDuration | time.Duration | 15 * time.Minute | How long an IP is locked out after exceeding MaxFailedAttempts. |
CredentialStuffingDetection | bool | false | Detects credential stuffing patterns (many different usernames from the same IP). |
BruteForceDetection | bool | false | Detects brute force patterns (many password attempts for the same username). |
1AuthShield: sentinel.AuthShieldConfig{2 Enabled: true,3 LoginRoute: "/api/login",4 MaxFailedAttempts: 5,5 LockoutDuration: 15 * time.Minute,6 CredentialStuffingDetection: true,7 BruteForceDetection: true,8}
LoginRoute Must Match Exactly
LoginRoute must match the exact path of your login handler. Sentinel monitors this route for non-2xx responses to count failed login attempts. If the route does not match, Auth Shield will not detect failures.Security Headers
The HeaderConfig controls automatic injection of security headers into every response. Security headers are enabled by default. These headers protect against clickjacking, MIME sniffing, XSS, and other client-side attacks.
| Field | Type | Default | Description |
|---|---|---|---|
Enabled | *bool | true | Whether security headers are injected. Use a *bool to explicitly disable. |
ContentSecurityPolicy | string | "" (not set) | Value for the Content-Security-Policy header. Left empty by default because CSP is application-specific. |
StrictTransportSecurity | bool | false | When true, sets Strict-Transport-Security: max-age=31536000; includeSubDomains. |
XFrameOptions | string | DENY | Value for the X-Frame-Options header. Common values: DENY, SAMEORIGIN. |
XContentTypeOptions | bool | true | When true, sets X-Content-Type-Options: nosniff. |
ReferrerPolicy | string | strict-origin-when-cross-origin | Value for the Referrer-Policy header. |
PermissionsPolicy | string | "" (not set) | Value for the Permissions-Policy header. Controls browser feature access. |
1Headers: sentinel.HeaderConfig{2 ContentSecurityPolicy: "default-src 'self'; script-src 'self' 'unsafe-inline'",3 StrictTransportSecurity: true,4 XFrameOptions: "DENY",5 XContentTypeOptions: true,6 ReferrerPolicy: "strict-origin-when-cross-origin",7 PermissionsPolicy: "camera=(), microphone=(), geolocation=()",8}
HSTS Warning
StrictTransportSecurity if your application is served exclusively over HTTPS. Once a browser receives an HSTS header, it will refuse to connect over plain HTTP for the specified duration.Anomaly Detection
The AnomalyConfig enables behavioral anomaly detection. Sentinel learns normal traffic patterns during a configurable learning period and then flags deviations such as impossible travel, unusual access patterns, and data exfiltration attempts.
| Field | Type | Default | Description |
|---|---|---|---|
Enabled | bool | false | Enables the anomaly detection engine. |
Sensitivity | AnomalySensitivity | sentinel.AnomalySensitivityMedium | Detection sensitivity. Options: sentinel.AnomalySensitivityLow, AnomalySensitivityMedium, AnomalySensitivityHigh. |
LearningPeriod | time.Duration | 7 * 24 * time.Hour (7 days) | Duration of the initial learning phase during which Sentinel builds baseline traffic patterns. |
Checks | []AnomalyCheckType | nil (all checks) | Which anomaly checks to run. When nil, all checks are enabled. |
Anomaly Check Types
| Check | Constant | Description |
|---|---|---|
| Impossible Travel | sentinel.CheckImpossibleTravel | Detects logins from geographically distant locations within impossibly short timeframes. |
| Unusual Access | sentinel.CheckUnusualAccess | Flags access to endpoints not typically visited by a given user or IP. |
| Data Exfiltration | sentinel.CheckDataExfiltration | Detects abnormally large data transfers or rapid sequential data access. |
| Off-Hours Access | sentinel.CheckOffHoursAccess | Flags access outside of normal business hours for a given user profile. |
| Velocity Anomaly | sentinel.CheckVelocityAnomaly | Detects sudden spikes in request velocity from a single source. |
| Credential Stuffing | sentinel.CheckCredentialStuffing | Identifies patterns consistent with credential stuffing attacks on auth endpoints. |
1Anomaly: sentinel.AnomalyConfig{2 Enabled: true,3 Sensitivity: sentinel.AnomalySensitivityMedium,4 LearningPeriod: 7 * 24 * time.Hour,5 Checks: []sentinel.AnomalyCheckType{6 sentinel.CheckImpossibleTravel,7 sentinel.CheckUnusualAccess,8 sentinel.CheckDataExfiltration,9 sentinel.CheckVelocityAnomaly,10 },11}
Learning Period
IP Reputation
The IPReputationConfig integrates with the AbuseIPDB API to check the reputation score of incoming IP addresses. Known malicious IPs can be automatically blocked.
| Field | Type | Default | Description |
|---|---|---|---|
Enabled | bool | false | Enables IP reputation checking. |
AbuseIPDBKey | string | "" | Your AbuseIPDB API key. Required when Enabled is true. |
AutoBlock | bool | false | Automatically add IPs exceeding MinAbuseScore to the blocklist. |
MinAbuseScore | int | 80 | Minimum AbuseIPDB confidence score (0-100) to consider an IP malicious. |
MaxChecksPerDay | int | 900 | Daily cap on live checks of attacking IPs; cached results don't count. (v2.4.0+) |
Feeds | []string | nil | Public blocklist URLs (Spamhaus DROP, FireHOL netsets, NDJSON with a cidr field) whose addresses are blocked. Works without an AbuseIPDB key. (v2.4.0+) |
FeedRefresh | time.Duration | 24h | How often feeds are downloaded again. (v2.4.0+) |
1IPReputation: sentinel.IPReputationConfig{2 Enabled: true,3 AbuseIPDBKey: os.Getenv("ABUSEIPDB_API_KEY"),4 AutoBlock: true,5 MinAbuseScore: 80,6 Feeds: []string{"https://www.spamhaus.org/drop/drop_v4.json"},7}
Live checks and quota
MaxChecksPerDay — and AutoBlock acts on the result. IP blocks (dashboard, AutoBlock, feeds) are enforced whether or not the WAF is enabled. See Threat Intelligence.Geolocation
The GeoConfig enables IP geolocation for enriching security events with country, city, and coordinate data. This powers the dashboard map view and the impossible travel anomaly check.
| Field | Type | Default | Description |
|---|---|---|---|
Enabled | bool | false | Enables IP geolocation lookups. |
Provider | GeoProvider | sentinel.GeoIPFree | Geolocation provider. Options: sentinel.GeoIPFree (GeoLite2), sentinel.GeoIPPaid (GeoIP2). |
1Geo: sentinel.GeoConfig{2 Enabled: true,3 Provider: sentinel.GeoIPFree,4}
Alerts
The AlertConfig configures real-time alerting when security events exceed a severity threshold. Sentinel supports three alert channels: Slack, email, and generic webhooks. You can enable one or more channels simultaneously.
| Field | Type | Default | Description |
|---|---|---|---|
MinSeverity | Severity | sentinel.SeverityHigh | Minimum severity level to trigger alerts. Options: SeverityLow, SeverityMedium, SeverityHigh, SeverityCritical. |
Slack | *SlackConfig | nil | Slack webhook configuration. Set to enable Slack alerts. |
Email | *EmailConfig | nil | Email SMTP configuration. Set to enable email alerts. |
Webhook | *WebhookConfig | nil | Generic webhook configuration. Set to enable webhook alerts. |
Slack Alerts
| Field | Type | Description |
|---|---|---|
WebhookURL | string | The Slack incoming webhook URL. |
Email Alerts
| Field | Type | Description |
|---|---|---|
SMTPHost | string | SMTP server hostname. |
SMTPPort | int | SMTP server port (typically 587 for TLS, 465 for SSL). |
Username | string | SMTP authentication username. |
Password | string | SMTP authentication password. |
Recipients | []string | List of email addresses to receive alerts. |
Webhook Alerts
| Field | Type | Description |
|---|---|---|
URL | string | The webhook endpoint URL. Sentinel sends a JSON POST request. |
Headers | map[string]string | Custom HTTP headers to include in webhook requests (e.g., for authentication). |
1Alerts: sentinel.AlertConfig{2 MinSeverity: sentinel.SeverityHigh,34 // Slack alerts5 Slack: &sentinel.SlackConfig{6 WebhookURL: "https://hooks.slack.com/services/T00/B00/xxx",7 },89 // Email alerts10 Email: &sentinel.EmailConfig{11 SMTPHost: "smtp.example.com",12 SMTPPort: 587,13 Username: "alerts@example.com",14 Password: "smtp-password",15 Recipients: []string{16 "security-team@example.com",17 "oncall@example.com",18 },19 },2021 // Webhook alerts (e.g., to a SIEM)22 Webhook: &sentinel.WebhookConfig{23 URL: "https://siem.example.com/api/ingest",24 Headers: map[string]string{25 "Authorization": "Bearer your-api-token",26 "X-Source": "sentinel",27 },28 },29}
Multiple Alert Channels
MinSeverity threshold, it is dispatched to all configured channels concurrently.AI Analysis
The AIConfig enables AI-powered security analysis. When configured, Sentinel uses large language models to analyze threat patterns, generate daily security summaries, and provide natural-language insights in the dashboard. This is an optional feature — it requires an API key from one of the supported providers.
| Field | Type | Default | Description |
|---|---|---|---|
Provider | AIProvider | (none) | AI provider. Options: sentinel.Claude, sentinel.OpenAI, sentinel.Gemini. |
APIKey | string | "" | API key for the selected provider. |
Model | string | (provider default) | Optional model override. If empty, Sentinel uses the provider's recommended model. |
DailySummary | bool | false | Deprecated — has no effect. Nothing runs on a schedule; the daily summary is generated on demand from the dashboard's AI page. ValidateConfig warns when it is set. |
MaxCallsPerDay | int64 | 500 | Caps upstream LLM calls per UTC day across all AI features. Cached responses don't count. 0 disables the cap. |
Redaction | AIRedaction | redact everything | What is masked before data is sent to the provider: query-string values, request bodies, full IPs, and scrubbed personal data and secrets. Opt out per item with SendPayloads, SendFullIPs, DisableScrubbing. (v2.4.0+; see AI Analysis.) |
Supported Providers
| Provider | Constant | Default Model |
|---|---|---|
| Anthropic Claude | sentinel.Claude | Claude Sonnet |
| OpenAI | sentinel.OpenAI | GPT-4o |
| Google Gemini | sentinel.Gemini | Gemini Pro |
1// Using Anthropic Claude2AI: &sentinel.AIConfig{3 Provider: sentinel.Claude,4 APIKey: os.Getenv("ANTHROPIC_API_KEY"),5}67// Using OpenAI8AI: &sentinel.AIConfig{9 Provider: sentinel.OpenAI,10 APIKey: os.Getenv("OPENAI_API_KEY"),11 Model: "gpt-4o",12}1314// Using Google Gemini15AI: &sentinel.AIConfig{16 Provider: sentinel.Gemini,17 APIKey: os.Getenv("GEMINI_API_KEY"),18}
AI is Optional
AI field is a pointer (*AIConfig). When left as nil, no AI provider is initialized and no API calls are made. The dashboard AI analysis tab will show a message indicating that AI is not configured.User Extractor
The UserExtractor identifies the authenticated user behind a request. When it is set, Sentinel records one user-activity entry per authenticated request — route pattern, method, status, duration, client IP, and a link to any threat the WAF logged on it. That data drives the dashboard's Users page, the GDPR report's per-user section, and anomaly detection; without an extractor all three stay empty. Return nil for unauthenticated requests.
It runs after your handler
c.Get("user"), JWT claims, and so on). Before v2.3.0 the field was never read. Per-user rate limiting uses the separate RateLimit.UserIDExtractor.| Field | Type | Default | Description |
|---|---|---|---|
UserExtractor | func(*gin.Context) *UserContext | nil | Function to extract user information from a request. Return nil if no user is authenticated. |
UserContext Struct
| Field | Type | Description |
|---|---|---|
ID | string | Unique user identifier. |
Email | string | User email address (used in alerts and dashboard display). |
Role | string | User role (e.g., "admin", "user"). Used for access pattern analysis. |
1// Extract user from JWT claims set by your auth middleware2UserExtractor: func(c *gin.Context) *sentinel.UserContext {3 // Option 1: From headers (proxy/gateway sets these)4 userID := c.GetHeader("X-User-ID")5 if userID == "" {6 return nil // Unauthenticated request7 }8 return &sentinel.UserContext{9 ID: userID,10 Email: c.GetHeader("X-User-Email"),11 Role: c.GetHeader("X-User-Role"),12 }13}1415// Option 2: From Gin context (your auth middleware sets these)16UserExtractor: func(c *gin.Context) *sentinel.UserContext {17 userID, exists := c.Get("userID")18 if !exists {19 return nil20 }21 return &sentinel.UserContext{22 ID: userID.(string),23 Email: c.GetString("userEmail"),24 Role: c.GetString("userRole"),25 }26}
UserExtractor vs UserIDExtractor
Config.UserExtractor provides full user context (ID, email, role) for threat profiling and dashboard display. RateLimitConfig.UserIDExtractor is a simpler function that returns only a user ID string for rate limiting. If you set UserExtractor, Sentinel can derive the user ID from it automatically. You only need UserIDExtractor if you want rate limiting to use a different identifier.Counters (Multiple Replicas)
Config.Counters holds the counters behind rate limiting and AuthShield. When it is nil, they live in process memory, and each replica counts on its own. Set a shared store so limits and lockouts hold across every instance.
| Field | Type | Default | Description |
|---|---|---|---|
Counters | CounterStore | nil (in memory) | Where rate-limit counters and AuthShield failures and lockouts are kept. redisstore.New(client) shares them through Redis. |
1import "github.com/MUKE-coder/sentinel/v2/redisstore"23Counters: redisstore.New(redis.NewClient(&redis.Options{Addr: "redis:6379"})),
Performance Monitoring
The PerformanceConfig controls request performance tracking and system resource monitoring. Performance monitoring is enabled by default. Sentinel records request durations, flags slow requests, and optionally tracks memory usage and goroutine counts.
| Field | Type | Default | Description |
|---|---|---|---|
Enabled | *bool | true | Whether performance monitoring is active. Use a *bool to explicitly disable. |
SlowRequestThreshold | time.Duration | 2 * time.Second | Requests exceeding this duration are flagged as slow in the dashboard. |
SlowQueryThreshold | time.Duration | 500 * time.Millisecond | Database queries exceeding this duration are flagged as slow (used with the GORM plugin). |
TrackMemory | bool | false | Track Go runtime memory statistics and expose them in the dashboard. |
TrackGoroutines | bool | false | Track the number of active goroutines and expose in the dashboard. |
1Performance: sentinel.PerformanceConfig{2 SlowRequestThreshold: 1 * time.Second,3 SlowQueryThreshold: 200 * time.Millisecond,4 TrackMemory: true,5 TrackGoroutines: true,6}
Defaults (Zero Config)
When you pass an empty sentinel.Config{}, Sentinel applies the following defaults via the ApplyDefaults() method. This table summarizes the effective configuration with zero user-provided values.
| Section | Field | Default Value |
|---|---|---|
| Dashboard | Enabled | true |
| Dashboard | Prefix | /sentinel |
| Dashboard | Username | admin |
| Dashboard | Password | sentinel |
| Dashboard | SecretKey | Random, generated at each start |
| Storage | Driver | sentinel.SQLite |
| Storage | DSN | sentinel.db |
| Storage | RetentionDays | 90 |
| Storage | MaxOpenConns | 10 |
| Storage | MaxIdleConns | 5 |
| WAF | Enabled | false |
| WAF | Mode | sentinel.ModeLog |
| WAF Rules | SQLInjection | sentinel.RuleStrict |
| WAF Rules | XSS | sentinel.RuleStrict |
| WAF Rules | PathTraversal | sentinel.RuleStrict |
| WAF Rules | CommandInjection | sentinel.RuleStrict |
| WAF Rules | SSRF | sentinel.RuleMedium |
| WAF Rules | XXE | sentinel.RuleStrict |
| WAF Rules | LFI | sentinel.RuleStrict |
| WAF Rules | OpenRedirect | sentinel.RuleMedium |
| Rate Limit | Enabled | false |
| Rate Limit | Strategy | sentinel.SlidingWindow |
| Auth Shield | Enabled | false |
| Auth Shield | MaxFailedAttempts | 5 |
| Auth Shield | LockoutDuration | 15 * time.Minute |
| Headers | Enabled | true |
| Headers | XFrameOptions | DENY |
| Headers | XContentTypeOptions | true (nosniff) |
| Headers | ReferrerPolicy | strict-origin-when-cross-origin |
| Anomaly | Enabled | false |
| Anomaly | Sensitivity | sentinel.AnomalySensitivityMedium |
| Anomaly | LearningPeriod | 7 * 24 * time.Hour |
| IP Reputation | Enabled | false |
| IP Reputation | MinAbuseScore | 80 |
| Geo | Enabled | false |
| Geo | Provider | sentinel.GeoIPFree |
| Alerts | MinSeverity | sentinel.SeverityHigh |
| AI | (entire section) | nil (disabled) |
| Performance | Enabled | true |
| Performance | SlowRequestThreshold | 2 * time.Second |
| Performance | SlowQueryThreshold | 500 * time.Millisecond |
1// Zero config — all defaults applied automatically2r := gin.Default()3sentinel.Mount(r, nil, sentinel.Config{})45// This is equivalent to:6sentinel.Mount(r, nil, sentinel.Config{7 Dashboard: sentinel.DashboardConfig{8 // Enabled: true (default)9 // Prefix: "/sentinel"10 // Username: "admin"11 // Password: "sentinel" (accepted from localhost only)12 // SecretKey: random, generated at each start13 },14 Storage: sentinel.StorageConfig{15 // Driver: sentinel.SQLite16 // DSN: "sentinel.db"17 // RetentionDays: 9018 },19 // WAF: disabled20 // RateLimit: disabled21 // AuthShield: disabled22 // Headers: enabled with safe defaults23 // Anomaly: disabled24 // Performance: enabled25})
Production Checklist
- Change the dashboard
Username,Password, andSecretKey - Enable the WAF with
ModeBlock - Set up rate limiting with appropriate thresholds for your traffic
- Configure at least one alert channel (Slack, email, or webhook)
- Use
sentinel.SQLiteorsentinel.Postgresfor persistent storage - Set
StrictTransportSecurity: trueif serving over HTTPS
Environment Variables Pattern
While Sentinel does not read environment variables directly, a common pattern is to read them yourself and pass them into the config struct. This keeps secrets out of your source code.
1import "os"23config := sentinel.Config{4 Dashboard: sentinel.DashboardConfig{5 Username: os.Getenv("SENTINEL_USERNAME"),6 Password: os.Getenv("SENTINEL_PASSWORD"),7 SecretKey: os.Getenv("SENTINEL_SECRET_KEY"),8 },9 Storage: sentinel.StorageConfig{10 Driver: sentinel.SQLite,11 DSN: os.Getenv("SENTINEL_DB_PATH"),12 },13 AI: &sentinel.AIConfig{14 Provider: sentinel.Claude,15 APIKey: os.Getenv("ANTHROPIC_API_KEY"),16 },17 Alerts: sentinel.AlertConfig{18 Slack: &sentinel.SlackConfig{19 WebhookURL: os.Getenv("SLACK_WEBHOOK_URL"),20 },21 },22 IPReputation: sentinel.IPReputationConfig{23 Enabled: os.Getenv("ABUSEIPDB_API_KEY") != "",24 AbuseIPDBKey: os.Getenv("ABUSEIPDB_API_KEY"),25 },26}
Never Hardcode Secrets
Next Steps
- WAF Deep Dive — Advanced WAF configuration, custom rules, and tuning
- Rate Limiting — Strategies, per-route limits, and response headers
- Auth Shield — Brute-force and credential stuffing protection
- Anomaly Detection — Behavioral analysis and learning periods
- Alerting — Slack, email, and webhook integration
- AI Analysis — Claude, OpenAI, and Gemini integration
- Dashboard — Explore the 13-page security dashboard