Real-time attack detection and IP blocking for Python — FastAPI, Django, async support
Project description
LoGuard Python SDK
Real-time security monitoring for Python applications. Detect attacks, block malicious IPs, define custom alert rules — with a single SDK that works across FastAPI, Django, and any Python backend.
Installation
pip install loguard
With framework extras:
pip install loguard[fastapi] # FastAPI / Starlette middleware
pip install loguard[django] # Django middleware
Quick Start
import os
from loguard import monitor
monitor.init(
api_key=os.environ["LOGUARD_API_KEY"],
base_url=os.environ.get("LOGUARD_BASE_URL", "https://loguard.org"),
env=os.environ.get("LOGUARD_ENV", "production"),
)
Track events
# Synchronous — waits for server response, returns IngestResult
result = monitor.event(
type="login_failed",
ip="1.2.3.4",
path="/api/login",
status_code=401,
user_id="user_123", # optional
meta={"method": "POST"}, # optional
)
print(result)
# IngestResult(ok=True, inserted=1, alerts_fired=0, plan='pro')
# Non-blocking — queues internally, never raises, ideal for production
monitor.event_fire_and_forget(
type="http_request",
ip="1.2.3.4",
path="/api/users",
status_code=200,
)
# Async
result = await monitor.aevent(
type="login_failed",
ip="1.2.3.4",
path="/api/login",
status_code=401,
)
# Batch — multiple events in one request
result = monitor.event_batch([
{"type": "http_request", "ip": "1.2.3.4", "path": "/", "status_code": 200},
{"type": "login_failed", "ip": "1.2.3.4", "path": "/login", "status_code": 401},
{"type": "http_request", "ip": "5.6.7.8", "path": "/.env", "status_code": 404},
])
print(f"accepted={result.inserted} alerts={result.alerts_fired}")
# Async batch
result = await monitor.aevent_batch([...])
Event types
type |
When to use |
|---|---|
http_request |
Any incoming HTTP request |
login_failed |
Failed authentication (401) |
login_success |
Successful login |
forbidden |
Access denied (403) |
waf_block |
Request blocked by WAF |
bot_detected |
Identified bot traffic |
Integrations
FastAPI / Starlette
import os
from fastapi import FastAPI
from loguard import monitor
from loguard.integrations.fastapi import LoGuardMiddleware
app = FastAPI()
monitor.init(
api_key=os.environ["LOGUARD_API_KEY"],
base_url=os.environ.get("LOGUARD_BASE_URL", "https://loguard.org"),
)
app.add_middleware(
LoGuardMiddleware,
track_statuses={400, 401, 403, 404, 429, 500, 502, 503},
enforce_blacklist=True, # returns 403 for blocked IPs before hitting routes
get_user_id=lambda r: r.state.user_id if hasattr(r.state, "user_id") else None,
)
Blocked IPs receive HTTP 403 {"detail": "Forbidden", "reason": "..."} before the request reaches your route handlers.
Django
# settings.py
MIDDLEWARE = [
"loguard.integrations.django.LogguardMiddleware",
# ... other middleware
]
LOGUARD_ENFORCE_BLACKLIST = True # block blacklisted IPs at middleware level
LOGUARD_TRACK_ALL = False # True = track all requests, not just errors
LOGUARD_TRACK_STATUSES = {400, 401, 403, 404, 429, 500, 502, 503}
# apps.py
import os
from django.apps import AppConfig
class MyAppConfig(AppConfig):
name = "myapp"
def ready(self):
from loguard import monitor
monitor.init(
api_key=os.environ["LOGUARD_API_KEY"],
base_url=os.environ.get("LOGUARD_BASE_URL", "https://loguard.org"),
env=os.environ.get("LOGUARD_ENV", "production"),
)
Alert Rules
Define custom rules — the server evaluates them on every ingest and fires alerts when conditions match.
from loguard import monitor
from loguard.models import AlertRule, AlertCondition
# Brute force: >10 failed logins/min from same IP
rule = monitor.alerts.create(AlertRule(
name="Brute force",
conditions=[
AlertCondition(field="type", op="eq", value="login_failed"),
AlertCondition(field="rate_per_minute", op="gt", value=10),
],
severity="high", # "low" | "medium" | "high" | "critical"
actions=["notify", "block"], # "notify" | "block" | "log"
logic="and", # "and" (all conditions) | "or" (any condition)
cooldown_sec=300, # min seconds between repeated alerts for same IP
))
print(rule.id)
# Scanner: any request to sensitive paths
rule2 = monitor.alerts.create(AlertRule(
name="Path scanner",
conditions=[
AlertCondition(field="path", op="in", value=["/.env", "/.git/config", "/admin"]),
],
severity="medium",
actions=["notify"],
))
# Manage
rules = monitor.alerts.list()
rule.enabled = False
monitor.alerts.update(rule)
monitor.alerts.delete(rule.id)
# Async variants: acreate / alist / aupdate / adelete
Condition fields and operators
| Field | Type | Description |
|---|---|---|
type |
string | Event type (login_failed, http_request, …) |
ip |
string | Source IP address |
path |
string | Request path |
status_code |
int | HTTP status code |
user_id |
string | Authenticated user ID |
rate_per_minute |
int | Request rate per minute from same IP |
rate_per_hour |
int | Request rate per hour from same IP |
| Operator | Description |
|---|---|
eq / neq |
Equal / not equal |
gt / gte / lt / lte |
Numeric comparison |
contains / startswith / endswith |
String matching |
regex |
Regular expression |
in / not_in |
Value in list |
Blacklist
Block or flag IPs, users, CIDR ranges, paths, and user agents. Checks use a 60-second TTL cache — safe to call on every request.
from datetime import datetime, timedelta, timezone
from loguard.models import BlacklistEntry
# Block an IP permanently
monitor.blacklist.block_ip("185.220.101.1", reason="Known scanner")
# Block with expiry
expires = (datetime.now(timezone.utc) + timedelta(hours=24)).isoformat()
monitor.blacklist.block_ip("1.2.3.4", reason="Brute force", expires_at=expires)
# Block a CIDR range
monitor.blacklist.block_cidr("185.220.0.0/16", reason="Tor exit nodes")
# Block a user
monitor.blacklist.block_user("user_abc123", reason="Fraud", expires_at=expires)
# Flag (mark but don't block)
monitor.blacklist.flag_ip("9.9.9.9", reason="Suspicious activity")
# Full control via BlacklistEntry
entry = monitor.blacklist.add(BlacklistEntry(
type="user_agent",
value="sqlmap",
reason="Attack tool",
action="block",
))
# Check (with 60s TTL cache)
result = monitor.blacklist.check(type="ip", value="185.220.101.1")
if result["blacklisted"]:
print(result["action"]) # "block" | "flag" | "alert"
print(result["reason"])
# List, update, remove
entries = monitor.blacklist.list(type="ip", enabled_only=True)
monitor.blacklist.remove(entry.id)
# Async variants: aadd / alist / aremove / acheck
result = await monitor.blacklist.acheck(type="ip", value="1.2.3.4")
Blacklist entry types
type |
value example |
Description |
|---|---|---|
ip |
"1.2.3.4" |
Single IPv4 address |
cidr |
"185.220.0.0/16" |
IP range |
user_id |
"user_abc123" |
Authenticated user |
path |
"/admin" |
Request path prefix |
user_agent |
"sqlmap" |
User-Agent substring |
IngestResult
Every monitor.event() call returns an IngestResult:
result = monitor.event(type="login_failed", ip="1.2.3.4", path="/login", status_code=401)
result.ok # bool — request accepted
result.inserted # int — events accepted by server
result.dropped # int — events dropped (quota or plan limit)
result.alerts_fired # int — alerts triggered by this batch
result.alerts # List[AlertOut] — alert details
result.plan # str — current plan ("free", "pro", "business", …)
result.usage_info # UsageInfo — quota usage for current month
print(result.usage_info)
# UsageInfo(used=48851/1000000, remaining=951149, month='2026-06')
if result.usage_info.is_near_limit:
print("Approaching monthly quota")
Error Handling
from loguard.exceptions import (
LogguardAuthError, # invalid or missing API key
LogguardQuotaError, # monthly event quota exceeded
LogguardConnectionError, # server unreachable or 5xx
LogguardValidationError, # invalid event data
LogguardNotFoundError, # alert rule / blacklist entry not found
LogguardConflictError, # duplicate entry
)
try:
monitor.event(type="login_failed", ip="1.2.3.4", path="/login", status_code=401)
except LogguardQuotaError:
pass # quota exceeded — handle gracefully
except LogguardConnectionError:
pass # server unreachable — fail open
except LogguardAuthError:
raise # bad API key — fail loud
event_fire_and_forget() never raises — safe to use in any middleware without try/except.
Kernel Firewall (optional)
If LoGuard Daemon is running on your host, connect it for kernel-level IP blocking:
monitor.init(api_key=os.environ["LOGUARD_API_KEY"])
monitor.init_firewall(sock_path="/var/run/loguard.sock")
# block_ip() now also triggers kernel-level NF_DROP automatically
monitor.blacklist.block_ip("1.2.3.4", reason="SQL_INJECTION")
# Direct firewall access
monitor.firewall.block_ip("1.2.3.4", duration=3600)
monitor.firewall.block_country("KP")
stats = monitor.firewall.get_stats()
init_firewall() is fail-safe — if the daemon is not running, it silently disables kernel blocking and the SDK continues working normally.
Environment Variables
| Variable | Default | Description |
|---|---|---|
LOGUARD_API_KEY |
— | Your project API key (required) |
LOGUARD_BASE_URL |
https://loguard.org |
API base URL |
LOGUARD_ENV |
production |
Environment tag (production, staging, development) |
Links
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file loguard-2.0.8.tar.gz.
File metadata
- Download URL: loguard-2.0.8.tar.gz
- Upload date:
- Size: 27.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6ba908d02b3882a933ac80aa5597679d2ea06091eedf22b773c52be86a5edfa4
|
|
| MD5 |
969abc9e04667016ab554432a83bf5b1
|
|
| BLAKE2b-256 |
22b7c08001cb22a9c2dafd3a6b724f888df18c0bf33ab433012dc41eea05af11
|
File details
Details for the file loguard-2.0.8-py3-none-any.whl.
File metadata
- Download URL: loguard-2.0.8-py3-none-any.whl
- Upload date:
- Size: 25.6 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
897e1655a66ac0719f1b1b54e65ca60fed30abec3e01497cc1e8a6f00225f3af
|
|
| MD5 |
6651c7d32fcb0936ea97e620d72ca450
|
|
| BLAKE2b-256 |
43b71b4d930126b480ba8cce2557bad39920f3de7a4f69da785e03ae1350f370
|