Skip to main content

SessionArmor

Tests PyPI version Python 3.10+ Django 4.2-5.2 License: MIT

Framework-neutral session-assurance contracts with an optional Django adapter for session integrity, privacy-aware audit logging, and configurable request gates.

Part of the Tunet ecosystem — alongside SwapLayer and InfraGlyph. See the SessionArmor product page for the public capability overview.


What It Does

The dependency-free core provides immutable assurance evidence and typed gate decisions. The optional Django adapter provides three drop-in middleware classes that harden an application's session layer:

Middleware Purpose
SessionSecurityMiddleware Keyed continuity binding, validated absolute/idle timeouts, claim drift detection, and typed assurance evidence
AuditMiddleware Structured, privacy-aware security audit events for authenticated request outcomes
GateMiddleware Typed, identity-bound workflow gates with bounded, revocation-aware caching

All three are hookable — override methods to customize behavior without touching internals.

References

SessionArmor adds controls around an existing Django authentication and session setup; it does not replace HTTPS, secure cookie settings, CSRF protection, authorization, MFA, or incident response. Fingerprint binding is a replay signal rather than proof of device identity and can require tuning for mobile or privacy-network traffic. See the security model for deployment assumptions, limitations, and a release checklist.


Installation

pip install SessionArmor

The core install has no framework dependency. Install the Django adapter when middleware integration is required:

pip install "SessionArmor[django]"

Quick Start

# settings.py
MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    # ... your auth middleware ...
    'session_armor.adapters.django.SessionSecurityMiddleware',
    'session_armor.adapters.django.AuditMiddleware',
    # ... rest of your middleware ...
]

# Optional settings (shown with defaults)
SESSION_ARMOR_ABSOLUTE_TIMEOUT = 28800
SESSION_ARMOR_IDLE_TIMEOUT = 1800
SESSION_ARMOR_BIND_IP = True
SESSION_ARMOR_BIND_USER_AGENT = True
SESSION_ARMOR_DETECT_CLAIM_DRIFT = True
SESSION_ARMOR_REQUIRE_BINDING = True
SESSION_ARMOR_CLOCK_SKEW = 60
SESSION_ARMOR_LAST_ACTIVE_RESOLUTION = 0
SESSION_ARMOR_NO_STORE = True
SESSION_ARMOR_STATE_KEY = SECRET_KEY
SESSION_ARMOR_AUDIT_PEPPER = SECRET_KEY

# Forwarded headers are ignored unless both values are configured.
SESSION_ARMOR_TRUSTED_PROXY_DEPTH = 0
SESSION_ARMOR_TRUSTED_PROXY_CIDRS = ()

Note on audit IP hashing. Audit logs pseudonymize the client IP with a keyed HMAC-SHA256 so the value can't be reversed from the (low-entropy) IPv4 space. Set a dedicated SESSION_ARMOR_AUDIT_PEPPER to rotate it independently of SECRET_KEY, or to share it across services for cross-log correlation. If unset, it falls back to SECRET_KEY.

Note on proxy trust. The secure default (DEPTH = 0) ignores X-Forwarded-For. To use it, configure both the exact number of proxy hops and their CIDR ranges. Each trusted hop is checked from the socket peer back toward the client; malformed chains and catch-all trusted networks are rejected.

Building a Gate

from session_armor import GateDecision
from session_armor.adapters.django import GateMiddleware
from django.shortcuts import redirect

class ComplianceGate(GateMiddleware):
    gate_id = 'compliance'
    cache_ttl_setting = 'COMPLIANCE_CACHE_TTL'
    default_cache_ttl = 3600
    requires_trusted_session = True

    def check(self, request) -> GateDecision:
        if user_accepted_current_terms(request):
            return GateDecision.allow('current_terms_accepted')
        return GateDecision.reject('terms_acceptance_required')

    def on_reject(self, request, decision):
        return redirect('/accept-terms/')

"Check once" gates

For gates that should only kick in once (onboarding, compliance), set recheck_after_pass = False: a passing result stays cached in the session with no TTL re-checks. To push new requirements to already-passed sessions (a new document version, a new onboarding step), implement get_state_version() against a shared cache key and bump that key on publish — every active session re-checks exactly once, with no per-request database hits:

from django.core.cache import cache

class ComplianceGate(GateMiddleware):
    gate_id = 'compliance'
    recheck_after_pass = False  # Pass once, stay passed

    def get_state_version(self, request) -> str | None:
        # Bumped by admin tooling when a new document version is published
        return cache.get(f'compliance_version:{request.user.platform}', '')

    def check(self, request) -> GateDecision:
        return (
            GateDecision.allow('current_terms_accepted')
            if user_accepted_current_terms(request)
            else GateDecision.reject('terms_acceptance_required')
        )

Sticky passes and revocation. With recheck_after_pass = False, a pass is cached until the session ends, invalidate() is called, or the state version changes — so it is not appropriate for authorization/entitlement gates where access may be revoked. For those, use a normal TTL gate, drive get_state_version(), or set sticky_pass_max_age (seconds) to cap how long a sticky pass may be reused before a forced re-check.

Customizing Session Security

from session_armor.adapters.django import SessionSecurityMiddleware

class MySessionSecurity(SessionSecurityMiddleware):
    exempt_paths = ('/health/', '/static/')
    last_active_resolution = 60  # only rewrite last-active timestamp every 60s

    def get_critical_claims(self, user):
        # Auth0 / OIDC claims that must not change mid-session
        return [user.sub, user.organization_uuid, user.platform]

    def get_login_url(self, request):
        platform = getattr(request.user, 'platform', '')
        return f'/{platform}/login/' if platform else '/login/'

# After a reviewed server-side claim update, use the public API rather than
# writing SessionArmor's private session keys.
MySessionSecurity(lambda request: request).rebaseline_claims(request)

AccessGate integration

SessionArmor and AccessGate are complementary boundaries. SessionArmor decides whether the authenticated session is still trustworthy and attaches an immutable request.session_assurance record. AccessGate's Django adapter carries that record into authorization context, where SessionAssurancePolicy denies stale, missing, malformed, or subject-mismatched evidence before business policies may grant an action:

from access_gate import AuthorizationEngine, RolePolicy, SessionAssurancePolicy

engine = AuthorizationEngine([
    SessionAssurancePolicy(actions={'records.read'}),
    RolePolicy({'records_reader'}, actions={'records.read'}),
])

The assurance policy is deny-only: a trusted session never grants a permission by itself. See docs/ACCESSGATE_INTEGRATION.md.

Customizing Audit Logging

from session_armor.adapters.django import AuditMiddleware

class MyAudit(AuditMiddleware):
    exempt_paths = ('/health/', '/static/', '/favicon.ico')

    def get_user_identity(self, request):
        return {
            'uid': request.user.sub,
            'platform': request.user.platform,
            'org': request.user.organization_uuid,
        }

Development

# Clone and install
git clone https://github.com/Tunet-xyz/session_armor.git
cd session_armor
pip install -e ".[dev]"

# Run tests
pytest

# Lint
ruff check src/session_armor tests

# Type check
mypy src/session_armor

Public Agent MCP Contract

SessionArmor includes a public agent-operability contract in mcp/. It describes how external agents can safely help with middleware ordering, security settings, audit customization, gate design, browser-visible docs, and rollout planning without requiring source-code access or production session data.

From a checkout, run the dependency-free contract server with python mcp/server.py. After installation, agents can use the packaged stdio command:

sessionarmor-mcp

License

MIT — Copyright (c) 2024-2026 Tunet Ltd. See LICENSE.

Download files

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

Source Distribution

sessionarmor-0.5.0.tar.gz (52.8 kB view details)

Uploaded Source

Built Distribution

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

sessionarmor-0.5.0-py3-none-any.whl (31.7 kB view details)

Uploaded Python 3

File details

Details for the file sessionarmor-0.5.0.tar.gz.

File metadata

  • Download URL: sessionarmor-0.5.0.tar.gz
  • Upload date:
  • Size: 52.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for sessionarmor-0.5.0.tar.gz
Algorithm Hash digest
SHA256 fd8e95be4ec35f0e47b3c50665d6786c4ea4e5aae4b2c0fa1de43d10e1f30db9
MD5 1bb1e9c5f9b854da6902cde96def1e08
BLAKE2b-256 83bef80255d7473c39989efc622bc67486697c95cb0b5dedb4da92b1ad807209

See more details on using hashes here.

Provenance

The following attestation bundles were made for sessionarmor-0.5.0.tar.gz:

Publisher: publish.yaml on Tunet-xyz/session_armor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file sessionarmor-0.5.0-py3-none-any.whl.

File metadata

  • Download URL: sessionarmor-0.5.0-py3-none-any.whl
  • Upload date:
  • Size: 31.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for sessionarmor-0.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 73f13bef8664586cdfcce6153e63483896c0799bc24a1be8136f764d7ef8bb02
MD5 6b96c7a52b6653aca8974fdc09da519f
BLAKE2b-256 c7e586dc12989cc4a306d67afc0587ebde93a53670f92d7e895fb63b71e7d23c

See more details on using hashes here.

Provenance

The following attestation bundles were made for sessionarmor-0.5.0-py3-none-any.whl:

Publisher: publish.yaml on Tunet-xyz/session_armor

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

This release

0.5.0 This release

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page