Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion app/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,15 @@ class Config:
# omnibioai-billing are then not visible.
V1_REDIS_URL = os.getenv("V1_REDIS_URL", "")
V1_RATE_LIMIT_PER_MINUTE = int(os.getenv("V1_RATE_LIMIT_PER_MINUTE", "60"))
V1_IDEMPOTENCY_TTL = int(os.getenv("V1_IDEMPOTENCY_TTL", "86400"))
# M18 (design audit gap #11): this cached replay body is the real
# answer text/citations for /v1/literature/answers, not just
# metadata -- one of only two places that text survives anywhere in
# this system at all (the other is omnibioai-rag's own query
# cache). The design doc's 30-day question/answer deletion policy
# is enforced here as a hard ceiling on this already-short-lived
# cache's TTL, not a separate deletion job -- there is no durable
# store for one to run against.
V1_IDEMPOTENCY_TTL = min(int(os.getenv("V1_IDEMPOTENCY_TTL", "86400")), 30 * 24 * 60 * 60)
# Design audit gap #7 ("concurrent-answer limits are absent"): the
# most expensive /v1 call (it invokes an LLM, up to RAG's own
# 300-second timeout) is capped on how many of a single key's or
Expand Down
24 changes: 24 additions & 0 deletions tests/test_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,30 @@ def test_config_reads_iam_url_from_env(monkeypatch):
importlib.reload(cfg_module)


def test_v1_idempotency_ttl_is_capped_at_30_days_even_if_configured_higher(monkeypatch):
"""M18 (design audit gap #11): the idempotency-replay cache holds
the real answer text/citations, so its TTL is the de facto
enforcement of the 30-day deletion policy -- a misconfigured env
var must not be able to silently violate that."""
monkeypatch.setenv("V1_IDEMPOTENCY_TTL", str(60 * 24 * 60 * 60)) # 60 days
import app.core.config as cfg_module

importlib.reload(cfg_module)
assert cfg_module.Config.V1_IDEMPOTENCY_TTL == 30 * 24 * 60 * 60
monkeypatch.delenv("V1_IDEMPOTENCY_TTL", raising=False)
importlib.reload(cfg_module)


def test_v1_idempotency_ttl_below_the_30_day_cap_is_unaffected(monkeypatch):
monkeypatch.setenv("V1_IDEMPOTENCY_TTL", "60")
import app.core.config as cfg_module

importlib.reload(cfg_module)
assert cfg_module.Config.V1_IDEMPOTENCY_TTL == 60
monkeypatch.delenv("V1_IDEMPOTENCY_TTL", raising=False)
importlib.reload(cfg_module)


def test_config_defaults_are_set():
"""IAM_URL/REDIS_URL/JWT_SECRET all have non-empty defaults, so the
gateway can start with no environment variables configured."""
Expand Down
Loading