How Hydrus Networking Sessions Handle Cookies and Authentication
Hydrus networking sessions implement a two-layer authentication system where a static access key generates a temporary session_key cookie for session persistence, with the NetworkSessionManager handling client-side cookie cleanup and the server resolving cookies to accounts via _callbackEstablishAccountFromHeader.
Hydrus is a personal media library application with a client-server architecture that relies heavily on its networking layer for both local API access and external site scraping. The Hydrus networking sessions system provides the backbone for stateful HTTP communication, managing everything from session token generation to automatic cleanup of expired cookies. Understanding this architecture is essential for developers extending the platform or troubleshooting authentication failures.
Server-Side Session Creation and Cookie Issuance
In hydrus/server/networking/ServerServerResources.py, the HydrusResourceSessionKey class handles the initial authentication handshake. When a client presents a valid access key, the server generates a 32-byte random session key and returns it as an HTTP cookie.
# ServerServerResources.py – lines 93-106
session_key, expires = SG.server_controller.server_session_manager.AddSession(self._service_key, access_key)
now = HydrusTime.GetNow()
max_age = expires - now
cookies = [('session_key', session_key.hex(),
{'max_age': str(max_age), 'path': '/'})]
response_context = HydrusServerResources.ResponseContext(200, cookies=cookies)
The HydrusSessionManagerServer class (defined in hydrus/core/HydrusSessions.py) stores the session key with an expiration timestamp, typically defaulting to 30 days. The response includes a Set-Cookie header with the calculated max_age and a path of /, ensuring the cookie applies to all subsequent requests to the domain.
Server-Side Cookie-Based Authentication
All restricted API endpoints inherit from HydrusResourceRestricted, which relies on _callbackEstablishAccountFromHeader to resolve incoming requests to specific user accounts. This method first inspects the Cookie header for a session_key value before falling back to the static access key.
# ServerServerResources.py – lines 225-260
session_key = None
if request.requestHeaders.hasHeader('Cookie'):
cookie_texts = request.requestHeaders.getRawHeaders('Cookie')
cookie_text = cookie_texts[0]
cookies = http.cookies.SimpleCookie(cookie_text)
if 'session_key' in cookies:
morsel = cookies['session_key']
session_key = bytes.fromhex(morsel.value)
if session_key is None:
# No cookie → try access_key header
access_key = self._parseHydrusNetworkAccessKey(request, key_required=False)
...
else:
account = SG.server_controller.server_session_manager.GetAccount(self._service_key, session_key)
If the session_key cookie is present and valid, HydrusSessionManagerServer.GetAccount resolves it to the corresponding account object. If the cookie is malformed, the server raises HydrusExceptions.BadRequestException; if the session has expired, it raises HydrusExceptions.SessionException, which translates to a 401 HTTP response.
Client-Side Session Management and Cookie Storage
The Hydrus client maintains persistent HTTP state using requests.Session objects encapsulated within NetworkSessionManagerSessionContainer classes, as implemented in hydrus/client/networking/ClientNetworkingSessions.py.
Session Container Architecture
Each network context (global, per-domain, or per-Hydrus-service) receives its own session container. For local Hydrus server connections, the system disables SSL verification to accommodate self-signed certificates.
# ClientNetworkingSessions.py – lines 52-60
self.session = requests.Session()
if self.network_context.context_type == CC.NETWORK_CONTEXT_HYDRUS:
self.session.verify = False # self-signed certs for local server
Automatic Cookie Cleanup
The NetworkSessionManager periodically purges transient cookies to prevent memory bloat and security leakage. During the PrepareForNewWork cycle, it clears both session-only cookies (those without explicit expiration dates) and expired cookies based on their timestamps.
# ClientNetworkingSessions.py – lines 56-63 (PrepareForNewWork)
my_session_cookies = self.session.cookies
if HydrusTime.TimeHasPassed(self.last_touched_time + self.SESSION_TIMEOUT):
my_session_cookies.clear_session_cookies()
my_session_cookies.clear_expired_cookies()
Database Persistence of Cookies
When the client persists its state to the database, the NetworkSessionManager serializes the cookie jar using Python's pickle module. To prevent stale session data from persisting across restarts, the system explicitly clears session-only cookies before serialization.
# ClientNetworkingSessions.py – lines 62-70 (_GetSerialisableInfo)
self.session.cookies.clear_session_cookies()
pickled_cookies_hex = pickle.dumps(self.session.cookies).hex()
return (serialisable_network_context, pickled_cookies_hex)
On application restart, the client restores the pickled cookie jar and immediately clears session-only cookies again, ensuring no ephemeral authentication tokens survive between sessions.
Public Cookie Management API
Hydrus exposes a local server API for external scripts to inspect and manipulate the client's cookie store for arbitrary domains. Implemented in hydrus/client/networking/api/ClientLocalServerResourcesManageCookies.py, this interface provides endpoints for reading and writing cookies without direct database access.
To retrieve cookies for a specific domain, the HydrusResourceClientAPIRestrictedManageCookiesGetCookies class iterates through the session's cookie jar:
# ClientLocalServerResourcesManageCookies.py – lines 41-58 (GET)
for cookie in session.cookies:
body_cookies_list.append([cookie.name, cookie.value,
cookie.domain, cookie.path,
cookie.expires])
To set or delete cookies, the HydrusResourceClientAPIRestrictedManageCookiesSetCookies class validates incoming cookie rows and updates the jar accordingly:
# ... lines 76-102 (POST)
for cookie_row in cookie_rows:
if len(cookie_row) != 5: raise BadRequestException(...)
name, value, domain, path, expires = cookie_row
if value is None:
session.cookies.clear(domain, path, name)
else:
session.cookies.set(name, value, domain=domain,
path=path, expires=expires)
Practical Authentication Flow Example
Below is a complete Python example demonstrating the authentication flow: exchanging an access key for a session cookie, then using that cookie for authenticated requests.
import requests
from urllib.parse import urljoin
BASE = 'http://localhost:45871/' # local Hydrus server
SESSION_ENDPOINT = urljoin(BASE, 'session_key')
DATA_ENDPOINT = urljoin(BASE, 'some/restricted/resource')
# 1️⃣ Obtain an access key (pre-generated by the server)
access_key = b'YOUR_ACCESS_KEY' # bytes, not hex-encoded
# 2️⃣ Exchange for a session cookie
resp = requests.get(
SESSION_ENDPOINT,
headers={'Hydrus-Access-Key': access_key.hex()}
)
resp.raise_for_status()
# Store the session_key cookie for subsequent requests
session = requests.Session()
session.cookies.update(resp.cookies)
# 3️⃣ Make an authenticated request (cookie sent automatically)
resp = session.get(DATA_ENDPOINT)
print(resp.json())
Alternatively, scripts that do not maintain cookie state can continue using the Hydrus-Access-Key header on every request; the server automatically falls back to this method when the session_key cookie is absent, as shown in the _callbackEstablishAccountFromHeader logic.
Summary
- Hydrus networking sessions use a two-tier authentication model: a static access key creates a temporary session_key cookie that maintains state for subsequent requests.
- The server resolves cookies to accounts through
hydrus/server/networking/ServerServerResources.py, specifically within the_callbackEstablishAccountFromHeadermethod. - Client-side cookie hygiene is enforced by
NetworkSessionManagerinhydrus/client/networking/ClientNetworkingSessions.py, which automatically clears expired and session-only cookies both in memory and during database serialization. - A public REST API at
/manage_cookies/*allows external scripts to query and modify domain-specific cookies, enabling custom login flows for third-party sites. - Login scripts in
hydrus/client/networking/ClientNetworkingLogin.pyverify successful authentication by checking for the presence of validsession_keycookies in the session jar.
Frequently Asked Questions
How long do Hydrus session cookies last?
By default, session cookies generated by HydrusResourceSessionKey expire after 30 days. The server calculates the max_age parameter dynamically based on the difference between the current time and the expiration timestamp stored in HydrusSessionManagerServer. Clients can force earlier expiration by clearing cookies manually through the API or GUI.
Can I use Hydrus networking sessions without cookies?
Yes. While the preferred method uses the session_key cookie for stateful sessions, the server accepts a Hydrus-Access-Key header on every request as a fallback. In ServerServerResources.py, the _callbackEstablishAccountFromHeader method checks for the access key header whenever the session cookie is missing or invalid, allowing stateless clients to authenticate each request independently.
How does Hydrus handle expired or invalid session cookies?
When the server encounters an expired session key, HydrusSessionManagerServer.GetAccount raises HydrusExceptions.SessionException, which translates to a 401 Unauthorized HTTP response. Malformed cookie syntax triggers HydrusExceptions.BadRequestException. On the client side, NetworkSessionManager proactively removes expired cookies via clear_expired_cookies() before each request cycle, preventing the transmission of stale authentication tokens.
Where does Hydrus store cookies between application restarts?
The client serializes the requests.Session cookie jar to the SQLite database using Python's pickle module. In ClientNetworkingSessions.py, the _GetSerialisableInfo method converts the cookie jar to a hexadecimal string after first purging session-only cookies. During application startup, the system restores these cookies and immediately clears any residual session-only entries to ensure a clean state.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →