Skip to main content

Real-time attack detection and IP blocking for Python — FastAPI, Django, async support

Project description

LoGuard Python SDK

PyPI version Python License: MIT

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


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

loguard-2.0.8.tar.gz (27.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

loguard-2.0.8-py3-none-any.whl (25.6 kB view details)

Uploaded Python 3

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

Hashes for loguard-2.0.8.tar.gz
Algorithm Hash digest
SHA256 6ba908d02b3882a933ac80aa5597679d2ea06091eedf22b773c52be86a5edfa4
MD5 969abc9e04667016ab554432a83bf5b1
BLAKE2b-256 22b7c08001cb22a9c2dafd3a6b724f888df18c0bf33ab433012dc41eea05af11

See more details on using hashes here.

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

Hashes for loguard-2.0.8-py3-none-any.whl
Algorithm Hash digest
SHA256 897e1655a66ac0719f1b1b54e65ca60fed30abec3e01497cc1e8a6f00225f3af
MD5 6651c7d32fcb0936ea97e620d72ca450
BLAKE2b-256 43b71b4d930126b480ba8cce2557bad39920f3de7a4f69da785e03ae1350f370

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page