fix(api): add security sentinel ingest compatibility endpoint#2
fix(api): add security sentinel ingest compatibility endpoint#2
Conversation
There was a problem hiding this comment.
Pull request overview
Adds an OmniLore Security Sentinel compatibility surface to FastCode’s FastAPI service so security events can be POSTed to a dedicated endpoint and basic ingest observability is surfaced via /health.
Changes:
- Add
POST /ingestendpoint that accepts a security event payload and records it in a bounded in-memory buffer. - Extend
/healthwith ingest capability metadata and current buffer size. - Add env-configurable buffer sizing and UTC receive timestamps for stored records.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| SECURITY_EVENT_BUFFER_LIMIT = max( | ||
| 10, | ||
| int(os.getenv("FASTCODE_SECURITY_EVENT_BUFFER_LIMIT", "500")), | ||
| ) |
There was a problem hiding this comment.
SECURITY_EVENT_BUFFER_LIMIT is computed with int(os.getenv(...)). If FASTCODE_SECURITY_EVENT_BUFFER_LIMIT is set to a non-integer value, this will raise ValueError at import time and prevent the API from starting. Consider parsing defensively (try/except with a fallback) and optionally clamping to a reasonable maximum to avoid accidental huge memory usage.
| SECURITY_EVENT_BUFFER_LIMIT = max( | |
| 10, | |
| int(os.getenv("FASTCODE_SECURITY_EVENT_BUFFER_LIMIT", "500")), | |
| ) | |
| _raw_security_event_buffer_limit = os.getenv("FASTCODE_SECURITY_EVENT_BUFFER_LIMIT", "500") | |
| try: | |
| _parsed_security_event_buffer_limit = int(_raw_security_event_buffer_limit) | |
| except (TypeError, ValueError): | |
| _parsed_security_event_buffer_limit = 500 | |
| # Clamp to avoid accidental huge memory usage | |
| SECURITY_EVENT_BUFFER_LIMIT = max(10, min(_parsed_security_event_buffer_limit, 10_000)) |
| return { | ||
| "status": "healthy", | ||
| "repo_loaded": fastcode_instance.repo_loaded, | ||
| "repo_indexed": fastcode_instance.repo_indexed, | ||
| "multi_repo_mode": fastcode_instance.multi_repo_mode, | ||
| "security_ingest_enabled": True, | ||
| "security_event_buffer_size": len(security_event_buffer), | ||
| } |
There was a problem hiding this comment.
In /health, the "healthy" response now includes security_ingest_enabled and security_event_buffer_size, but the earlier "initializing" return path does not. If clients rely on /health to detect ingest capability (per PR description), consider adding these fields to the initializing response as well for a consistent schema.
| security_event_buffer.append(record) | ||
| if len(security_event_buffer) > SECURITY_EVENT_BUFFER_LIMIT: | ||
| security_event_buffer.pop(0) |
There was a problem hiding this comment.
security_event_buffer uses a list and trims with pop(0), which is O(n) due to shifting elements. Since this is a bounded FIFO buffer, consider using collections.deque with maxlen (or popleft) to keep trimming O(1) and simplify the logic.
| logger.warning( | ||
| "Security ingest accepted (compat): event_type=%s tenant_id=%s", | ||
| event_type, | ||
| tenant_id, | ||
| ) |
There was a problem hiding this comment.
Every /ingest call logs at WARNING level. If security events are high-volume, this can quickly flood logs and trigger alerting; consider logging at INFO/DEBUG, adding sampling/rate-limiting, or only warning on malformed/unexpected payloads.
| logger.warning( | |
| "Security ingest accepted (compat): event_type=%s tenant_id=%s", | |
| event_type, | |
| tenant_id, | |
| ) | |
| log_message = "Security ingest accepted (compat): event_type=%s tenant_id=%s" | |
| if event_type == "unknown" or tenant_id == "unknown": | |
| logger.warning(log_message, event_type, tenant_id) | |
| else: | |
| logger.info(log_message, event_type, tenant_id) |
Summary
POST /ingestcompatibility endpoint for OmniLore Security Sentinel calls/healthWhy
OmniLore white-label gateway points security sentinel URL at
127.0.0.1:8001(FastCode). Without/ingest, events fell back to vector-store mode. This PR restores native ingest behavior.Validation
POST /ingestreturnsstatus=receivedsecurity_sentinel_ingestreturns HTTP 200 (no fallback)