From 6c2af55e7e1d04077856df6abfdd5524f1b123dc Mon Sep 17 00:00:00 2001 From: Aaron Nelson Date: Tue, 5 May 2026 23:28:39 +0000 Subject: [PATCH] Server-side session TTL enforcement - session_exists() now rejects rows older than 30 days, matching the client cookie max-age. - Opportunistic cleanup of expired rows on session_exists() calls, preventing unbounded growth of sessions.db from orphaned tokens (PWA reinstalls, manual cookie clears). Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/api.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/scripts/api.py b/scripts/api.py index 7d700db..ae66ff5 100644 --- a/scripts/api.py +++ b/scripts/api.py @@ -4,7 +4,7 @@ import sqlite3 import subprocess import hashlib from pathlib import Path -from datetime import datetime +from datetime import datetime, timedelta from dotenv import load_dotenv from sentence_transformers import SentenceTransformer import anthropic @@ -132,6 +132,7 @@ When making factual claims about Aaron — his history, credentials, locations, # Auth configuration import os SESSION_PASSWORD = os.getenv("AARON_AI_PASSWORD", "changeme") +SESSION_MAX_AGE_SECONDS = 60 * 60 * 24 * 30 SESSIONS_DB = str(Path.home() / "aaronai" / "sessions.db") def _init_sessions(): @@ -163,7 +164,10 @@ def delete_session(token: str): def session_exists(token: str) -> bool: conn = _connect_sessions() - row = conn.execute("SELECT 1 FROM sessions WHERE token = ?", (token,)).fetchone() + cutoff = (datetime.now() - timedelta(seconds=SESSION_MAX_AGE_SECONDS)).isoformat() + conn.execute("DELETE FROM sessions WHERE created_at < ?", (cutoff,)) + conn.commit() + row = conn.execute("SELECT 1 FROM sessions WHERE token = ? AND created_at >= ?", (token, cutoff)).fetchone() conn.close() return row is not None @@ -381,7 +385,7 @@ async def login(request: Request, response: Response): httponly=True, secure=True, samesite="lax", - max_age=60 * 60 * 24 * 30 + max_age=SESSION_MAX_AGE_SECONDS ) response.body = b'{"ok": true}' response.status_code = 200