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

All config fields are zero-value safe. An empty 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.

main.gogo
1package main
2
3import (
4 "time"
5
6 sentinel "github.com/MUKE-coder/sentinel/v2"
7 "github.com/gin-gonic/gin"
8)
9
10func main() {
11 r := gin.Default()
12
13 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 },
19
20 Storage: sentinel.StorageConfig{
21 Driver: sentinel.SQLite,
22 DSN: "sentinel.db",
23 RetentionDays: 90,
24 MaxOpenConns: 10,
25 MaxIdleConns: 5,
26 },
27
28 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 },
55
56 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 },
67
68 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 },
76
77 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 },
85
86 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 },
98
99 IPReputation: sentinel.IPReputationConfig{
100 Enabled: true,
101 AbuseIPDBKey: "your-abuseipdb-api-key",
102 AutoBlock: true,
103 MinAbuseScore: 80,
104 },
105
106 Geo: sentinel.GeoConfig{
107 Enabled: true,
108 Provider: sentinel.GeoIPFree,
109 },
110
111 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 },
130
131 AI: &sentinel.AIConfig{
132 Provider: sentinel.Claude,
133 APIKey: "your-anthropic-api-key",
134 Model: "claude-sonnet-4-20250514",
135 },
136
137 UserExtractor: func(c *gin.Context) *sentinel.UserContext {
138 userID := c.GetHeader("X-User-ID")
139 if userID == "" {
140 return nil
141 }
142 return &sentinel.UserContext{
143 ID: userID,
144 Email: c.GetHeader("X-User-Email"),
145 Role: c.GetHeader("X-User-Role"),
146 }
147 },
148
149 Performance: sentinel.PerformanceConfig{
150 SlowRequestThreshold: 2 * time.Second,
151 SlowQueryThreshold: 500 * time.Millisecond,
152 TrackMemory: true,
153 TrackGoroutines: true,
154 },
155 })
156
157 r.GET("/api/hello", func(c *gin.Context) {
158 c.JSON(200, gin.H{"message": "Hello, World!"})
159 })
160
161 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.

FieldTypeDefaultDescription
Enabled*booltrueWhether the dashboard is served. Set to false to disable entirely.
Prefixstring/sentinelURL prefix for the dashboard and API routes. The UI is available at {prefix}/ui.
UsernamestringadminUsername for dashboard login.
PasswordstringsentinelPassword for dashboard login.
SecretKeystringRandom, generated at each startSecret key used to sign JWT tokens for dashboard sessions.
config.gogo
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

The default username/password (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

To run Sentinel as a headless middleware without any dashboard routes, set Enabled to false. Because the field is a *bool, you need to use a pointer:
disabled := false
Dashboard: 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.

FieldTypeDefaultDescription
DriverStorageDriversentinel.SQLiteStorage backend. Options: sentinel.SQLite, sentinel.Postgres, sentinel.Memory.
DSNstringsentinel.dbData source name. For SQLite, this is a file path. For Postgres, a full connection string.
RetentionDaysint90Days to keep threat events, user activity, and performance metrics before the hourly cleanup deletes them. Audit logs are not affected.
AuditRetentionDaysint365Days 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+)
AuditKeystring""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.)
SyncIntervaltime.Duration5sHow 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+)
MaxOpenConnsint10Maximum number of open database connections.
MaxIdleConnsint5Maximum number of idle database connections.

Storage Drivers

DriverConstantNotes
SQLitesentinel.SQLiteThe 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.
Postgressentinel.PostgresRecommended for production. Required for IP blocks to reach every replica (each instance re-reads the block list every 30 seconds).
Memorysentinel.MemoryDevelopment 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.
MySQLsentinel.MySQLNot implemented. Selecting it silently falls back to in-memory storage; ValidateConfig reports it as an error.
config.gogo
1// SQLite (default, recommended)
2Storage: sentinel.StorageConfig{
3 Driver: sentinel.SQLite,
4 DSN: "sentinel.db",
5 RetentionDays: 30,
6}
7
8// In-memory (for development/testing)
9Storage: sentinel.StorageConfig{
10 Driver: sentinel.Memory,
11}
12
13// PostgreSQL
14Storage: 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

The SQLite driver uses a pure-Go implementation (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).

FieldTypeDefaultDescription
EnabledboolfalseEnables the WAF middleware. Must be set to true to activate.
ModeWAFModesentinel.ModeLogHow detected threats are handled. Options: sentinel.ModeLog, sentinel.ModeBlock, sentinel.ModeChallenge.
RulesRuleSetRuleStrict; RuleMedium for SSRF and open redirectPer-category sensitivity levels for built-in detection rules. Enforced since v2.3.0 — see below.
CustomRules[]WAFRulenilCustom regex-based rules for application-specific patterns.
ExcludeRoutes[]stringnilRoutes to exclude from WAF inspection (e.g., health check endpoints).
ExcludeIPs[]stringnilIP addresses or CIDR ranges to exclude from WAF inspection.
AllowedRedirectHosts[]stringnilHosts 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[]stringnilIPs 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.
MaxBodyBytesint6465536How much of each request body the WAF reads and inspects.
RejectOversizedBodyboolfalseReject bodies larger than MaxBodyBytes with 413 instead of inspecting only the first part.

WAF Modes

ModeConstantBehavior
Logsentinel.ModeLogDetects and logs threats but allows the request to proceed. Good for initial rollout.
Blocksentinel.ModeBlockDetects threats and rejects the request with HTTP 403. Recommended for production.
Challengesentinel.ModeChallengeRejects 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:

LevelPatterns kept
RuleOffNone — the category is disabled.
RuleLowOnly the most precise patterns (confidence ≥ 80).
RuleMediumEverything 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

Before v2.3.0 these levels were never read — every pattern ran whatever the config said. The defaults keep every pattern, so a default config detects exactly what it did; a category you had set to 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.
FieldDefaultDescription
SQLInjectionRuleStrictSQL injection detection sensitivity.
XSSRuleStrictCross-site scripting detection sensitivity.
PathTraversalRuleStrictPath traversal (../../etc/passwd) detection sensitivity.
CommandInjectionRuleStrictOS command injection detection sensitivity.
SSRFRuleMediumServer-side request forgery detection sensitivity.
XXERuleStrictXML external entity injection detection sensitivity.
LFIRuleStrictLocal file inclusion detection sensitivity.
OpenRedirectRuleMediumOpen 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.

FieldTypeDescription
IDstringUnique identifier for the rule.
NamestringHuman-readable name displayed in the dashboard.
PatternstringGo-compatible regular expression to match against request data.
AppliesTo[]stringWhich 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.
SeveritySeveritySeverity level: sentinel.SeverityLow, SeverityMedium, SeverityHigh, SeverityCritical.
Actionstring"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.)
EnabledboolWhether the rule is active.
config.gogo
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

Start with 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.

FieldTypeDefaultDescription
EnabledboolfalseEnables the rate limiting middleware.
StrategyRateLimitStrategysentinel.SlidingWindowAlgorithm used for counting. Options: sentinel.SlidingWindow, sentinel.FixedWindow, sentinel.TokenBucket.
ByIP*LimitnilPer-IP rate limit. Each unique client IP gets its own counter.
ByUser*LimitnilPer-user rate limit. Requires a UserIDExtractor or UserExtractor.
ByRoutemap[string]LimitnilPer-route rate limits. Keys are route patterns (e.g., /api/login).
Global*LimitnilGlobal rate limit applied across all requests regardless of source.
UserIDExtractorfunc(*gin.Context) stringnilFunction 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.

FieldTypeDescription
RequestsintMaximum number of requests allowed within the window.
Windowtime.DurationTime window for the limit (e.g., time.Minute, 15 * time.Minute).

Rate Limit Strategies

StrategyConstantDescription
Sliding Windowsentinel.SlidingWindowDefault. 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 Windowsentinel.FixedWindowConsecutive 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 Bucketsentinel.TokenBucketA bucket of Requests tokens refilled at Requests per Window: allows a burst up to the limit, then a steady rate.
config.gogo
1RateLimit: sentinel.RateLimitConfig{
2 Enabled: true,
3 Strategy: sentinel.SlidingWindow,
4
5 // 100 requests per minute per IP
6 ByIP: &sentinel.Limit{Requests: 100, Window: time.Minute},
7
8 // 500 requests per minute per authenticated user
9 ByUser: &sentinel.Limit{Requests: 500, Window: time.Minute},
10
11 // 5000 requests per minute globally
12 Global: &sentinel.Limit{Requests: 5000, Window: time.Minute},
13
14 // Strict limits on sensitive routes
15 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 },
20
21 // Extract user ID from your auth system
22 UserIDExtractor: func(c *gin.Context) string {
23 return c.GetHeader("X-User-ID")
24 },
25}

Rate Limit Headers

When rate limiting is active, Sentinel automatically sets standard rate limit headers on every response: 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.

FieldTypeDefaultDescription
EnabledboolfalseEnables the Auth Shield middleware.
LoginRoutestring""The route path for your login endpoint (e.g., /api/login).
MaxFailedAttemptsint5Number of failed login attempts before lockout is triggered.
LockoutDurationtime.Duration15 * time.MinuteHow long an IP is locked out after exceeding MaxFailedAttempts.
CredentialStuffingDetectionboolfalseDetects credential stuffing patterns (many different usernames from the same IP).
BruteForceDetectionboolfalseDetects brute force patterns (many password attempts for the same username).
config.gogo
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

The 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.

FieldTypeDefaultDescription
Enabled*booltrueWhether security headers are injected. Use a *bool to explicitly disable.
ContentSecurityPolicystring"" (not set)Value for the Content-Security-Policy header. Left empty by default because CSP is application-specific.
StrictTransportSecurityboolfalseWhen true, sets Strict-Transport-Security: max-age=31536000; includeSubDomains.
XFrameOptionsstringDENYValue for the X-Frame-Options header. Common values: DENY, SAMEORIGIN.
XContentTypeOptionsbooltrueWhen true, sets X-Content-Type-Options: nosniff.
ReferrerPolicystringstrict-origin-when-cross-originValue for the Referrer-Policy header.
PermissionsPolicystring"" (not set)Value for the Permissions-Policy header. Controls browser feature access.
config.gogo
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

Only enable 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.

FieldTypeDefaultDescription
EnabledboolfalseEnables the anomaly detection engine.
SensitivityAnomalySensitivitysentinel.AnomalySensitivityMediumDetection sensitivity. Options: sentinel.AnomalySensitivityLow, AnomalySensitivityMedium, AnomalySensitivityHigh.
LearningPeriodtime.Duration7 * 24 * time.Hour (7 days)Duration of the initial learning phase during which Sentinel builds baseline traffic patterns.
Checks[]AnomalyCheckTypenil (all checks)Which anomaly checks to run. When nil, all checks are enabled.

Anomaly Check Types

CheckConstantDescription
Impossible Travelsentinel.CheckImpossibleTravelDetects logins from geographically distant locations within impossibly short timeframes.
Unusual Accesssentinel.CheckUnusualAccessFlags access to endpoints not typically visited by a given user or IP.
Data Exfiltrationsentinel.CheckDataExfiltrationDetects abnormally large data transfers or rapid sequential data access.
Off-Hours Accesssentinel.CheckOffHoursAccessFlags access outside of normal business hours for a given user profile.
Velocity Anomalysentinel.CheckVelocityAnomalyDetects sudden spikes in request velocity from a single source.
Credential Stuffingsentinel.CheckCredentialStuffingIdentifies patterns consistent with credential stuffing attacks on auth endpoints.
config.gogo
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

During the learning period, Sentinel collects baseline data but does not flag anomalies. After the learning period expires, detections begin automatically. A shorter learning period may produce more false positives; a longer period provides more accurate baselines.

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.

FieldTypeDefaultDescription
EnabledboolfalseEnables IP reputation checking.
AbuseIPDBKeystring""Your AbuseIPDB API key. Required when Enabled is true.
AutoBlockboolfalseAutomatically add IPs exceeding MinAbuseScore to the blocklist.
MinAbuseScoreint80Minimum AbuseIPDB confidence score (0-100) to consider an IP malicious.
MaxChecksPerDayint900Daily cap on live checks of attacking IPs; cached results don't count. (v2.4.0+)
Feeds[]stringnilPublic blocklist URLs (Spamhaus DROP, FireHOL netsets, NDJSON with a cidr field) whose addresses are blocked. Works without an AbuseIPDB key. (v2.4.0+)
FeedRefreshtime.Duration24hHow often feeds are downloaded again. (v2.4.0+)
config.gogo
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

Since v2.4.0, every IP behind a threat event is checked in the background as it arrives — at most once a day per IP, private addresses skipped, capped by 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.

FieldTypeDefaultDescription
EnabledboolfalseEnables IP geolocation lookups.
ProviderGeoProvidersentinel.GeoIPFreeGeolocation provider. Options: sentinel.GeoIPFree (GeoLite2), sentinel.GeoIPPaid (GeoIP2).
config.gogo
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.

FieldTypeDefaultDescription
MinSeveritySeveritysentinel.SeverityHighMinimum severity level to trigger alerts. Options: SeverityLow, SeverityMedium, SeverityHigh, SeverityCritical.
Slack*SlackConfignilSlack webhook configuration. Set to enable Slack alerts.
Email*EmailConfignilEmail SMTP configuration. Set to enable email alerts.
Webhook*WebhookConfignilGeneric webhook configuration. Set to enable webhook alerts.

Slack Alerts

FieldTypeDescription
WebhookURLstringThe Slack incoming webhook URL.

Email Alerts

FieldTypeDescription
SMTPHoststringSMTP server hostname.
SMTPPortintSMTP server port (typically 587 for TLS, 465 for SSL).
UsernamestringSMTP authentication username.
PasswordstringSMTP authentication password.
Recipients[]stringList of email addresses to receive alerts.

Webhook Alerts

FieldTypeDescription
URLstringThe webhook endpoint URL. Sentinel sends a JSON POST request.
Headersmap[string]stringCustom HTTP headers to include in webhook requests (e.g., for authentication).
config.gogo
1Alerts: sentinel.AlertConfig{
2 MinSeverity: sentinel.SeverityHigh,
3
4 // Slack alerts
5 Slack: &sentinel.SlackConfig{
6 WebhookURL: "https://hooks.slack.com/services/T00/B00/xxx",
7 },
8
9 // Email alerts
10 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 },
20
21 // 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

You can enable any combination of Slack, email, and webhook alerts. When an event exceeds the 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.

FieldTypeDefaultDescription
ProviderAIProvider(none)AI provider. Options: sentinel.Claude, sentinel.OpenAI, sentinel.Gemini.
APIKeystring""API key for the selected provider.
Modelstring(provider default)Optional model override. If empty, Sentinel uses the provider's recommended model.
DailySummaryboolfalseDeprecated — 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.
MaxCallsPerDayint64500Caps upstream LLM calls per UTC day across all AI features. Cached responses don't count. 0 disables the cap.
RedactionAIRedactionredact everythingWhat 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

ProviderConstantDefault Model
Anthropic Claudesentinel.ClaudeClaude Sonnet
OpenAIsentinel.OpenAIGPT-4o
Google Geminisentinel.GeminiGemini Pro
config.gogo
1// Using Anthropic Claude
2AI: &sentinel.AIConfig{
3 Provider: sentinel.Claude,
4 APIKey: os.Getenv("ANTHROPIC_API_KEY"),
5}
6
7// Using OpenAI
8AI: &sentinel.AIConfig{
9 Provider: sentinel.OpenAI,
10 APIKey: os.Getenv("OPENAI_API_KEY"),
11 Model: "gpt-4o",
12}
13
14// Using Google Gemini
15AI: &sentinel.AIConfig{
16 Provider: sentinel.Gemini,
17 APIKey: os.Getenv("GEMINI_API_KEY"),
18}

AI is Optional

The 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

The extractor is called once the rest of the chain has run, so it can read whatever your auth middleware put on the context (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.
FieldTypeDefaultDescription
UserExtractorfunc(*gin.Context) *UserContextnilFunction to extract user information from a request. Return nil if no user is authenticated.

UserContext Struct

FieldTypeDescription
IDstringUnique user identifier.
EmailstringUser email address (used in alerts and dashboard display).
RolestringUser role (e.g., "admin", "user"). Used for access pattern analysis.
config.gogo
1// Extract user from JWT claims set by your auth middleware
2UserExtractor: 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 request
7 }
8 return &sentinel.UserContext{
9 ID: userID,
10 Email: c.GetHeader("X-User-Email"),
11 Role: c.GetHeader("X-User-Role"),
12 }
13}
14
15// 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 nil
20 }
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.

FieldTypeDefaultDescription
CountersCounterStorenil (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"
2
3Counters: 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.

FieldTypeDefaultDescription
Enabled*booltrueWhether performance monitoring is active. Use a *bool to explicitly disable.
SlowRequestThresholdtime.Duration2 * time.SecondRequests exceeding this duration are flagged as slow in the dashboard.
SlowQueryThresholdtime.Duration500 * time.MillisecondDatabase queries exceeding this duration are flagged as slow (used with the GORM plugin).
TrackMemoryboolfalseTrack Go runtime memory statistics and expose them in the dashboard.
TrackGoroutinesboolfalseTrack the number of active goroutines and expose in the dashboard.
config.gogo
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.

SectionFieldDefault Value
DashboardEnabledtrue
DashboardPrefix/sentinel
DashboardUsernameadmin
DashboardPasswordsentinel
DashboardSecretKeyRandom, generated at each start
StorageDriversentinel.SQLite
StorageDSNsentinel.db
StorageRetentionDays90
StorageMaxOpenConns10
StorageMaxIdleConns5
WAFEnabledfalse
WAFModesentinel.ModeLog
WAF RulesSQLInjectionsentinel.RuleStrict
WAF RulesXSSsentinel.RuleStrict
WAF RulesPathTraversalsentinel.RuleStrict
WAF RulesCommandInjectionsentinel.RuleStrict
WAF RulesSSRFsentinel.RuleMedium
WAF RulesXXEsentinel.RuleStrict
WAF RulesLFIsentinel.RuleStrict
WAF RulesOpenRedirectsentinel.RuleMedium
Rate LimitEnabledfalse
Rate LimitStrategysentinel.SlidingWindow
Auth ShieldEnabledfalse
Auth ShieldMaxFailedAttempts5
Auth ShieldLockoutDuration15 * time.Minute
HeadersEnabledtrue
HeadersXFrameOptionsDENY
HeadersXContentTypeOptionstrue (nosniff)
HeadersReferrerPolicystrict-origin-when-cross-origin
AnomalyEnabledfalse
AnomalySensitivitysentinel.AnomalySensitivityMedium
AnomalyLearningPeriod7 * 24 * time.Hour
IP ReputationEnabledfalse
IP ReputationMinAbuseScore80
GeoEnabledfalse
GeoProvidersentinel.GeoIPFree
AlertsMinSeveritysentinel.SeverityHigh
AI(entire section)nil (disabled)
PerformanceEnabledtrue
PerformanceSlowRequestThreshold2 * time.Second
PerformanceSlowQueryThreshold500 * time.Millisecond
main.gogo
1// Zero config — all defaults applied automatically
2r := gin.Default()
3sentinel.Mount(r, nil, sentinel.Config{})
4
5// 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 start
13 },
14 Storage: sentinel.StorageConfig{
15 // Driver: sentinel.SQLite
16 // DSN: "sentinel.db"
17 // RetentionDays: 90
18 },
19 // WAF: disabled
20 // RateLimit: disabled
21 // AuthShield: disabled
22 // Headers: enabled with safe defaults
23 // Anomaly: disabled
24 // Performance: enabled
25})

Production Checklist

Before deploying to production, make sure to:
  • Change the dashboard Username, Password, and SecretKey
  • 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.SQLite or sentinel.Postgres for persistent storage
  • Set StrictTransportSecurity: true if 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.

main.gogo
1import "os"
2
3config := 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

Always use environment variables or a secrets manager for API keys, passwords, SMTP credentials, and webhook URLs. The code examples on this page use literal strings for clarity, but production deployments should never commit secrets to source control.

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

Built with by JB