Security & LGPD
DELPHOS is built with privacy and security at its foundation. Every layer of the platform — from API authentication to database queries — enforces strict tenant isolation, data minimization, and audit logging. The architecture is designed to comply with LGPD (Lei Geral de Proteção de Dados) and international healthcare data protection standards.
Key Security Features
| Feature | Description |
|---|---|
| Tenant isolation | Row-level security at the database layer |
| No patient names | SHA-256 hashes only — names never stored in plaintext |
| Audit logging | Every data access logged with actor and timestamp |
| API key scoping | Keys bound to tenants with granular permissions |
| Soft deletes | Records deactivated, never physically deleted |
| Encrypted transit | TLS for all API communication |
| On-premise option | Full deployment within your infrastructure |
| Rate limiting | Two-layer protection against abuse |
| Audio privacy | In-memory only — no audio written to disk |
Authentication
DELPHOS uses API key-based authentication. Every request must include a
valid key in the x-api-key header.
Key format
Keys follow a structured format that encodes the tenant and environment:
delphos_{tenant-slug}_{environment}_{random}| Segment | Example | Purpose |
|---|---|---|
delphos_ | Fixed prefix | Identifies DELPHOS keys in logs and secrets managers |
{tenant-slug} | clinica_aurora | Ties key to a specific tenant |
{environment} | prod, stg, dev | Separates production from test traffic |
{random} | 128-bit random | Cryptographic entropy ensuring uniqueness |
Key storage
Plaintext keys are never stored. On creation, keys are hashed with an industry-standard one-way hashing algorithm (cost factor 12). The only time a client sees the full key is at the moment of creation.
Key validation uses constant-time comparison to prevent timing attacks — an attacker cannot infer partial key correctness from response latency.
Key capabilities
Each API key supports:
| Capability | Description |
|---|---|
| Environment binding | Key works only in its designated environment (production, staging, development) |
| Granular permissions | can_read, can_write, can_delete, can_admin — independently toggleable |
| Expiration date | Optional TTL; expired keys are rejected automatically |
| Revocation | Keys can be revoked with a reason string for audit purposes |
| Status tracking | active, expired, revoked — only active keys authenticate |
Key caching
To avoid hitting the database on every request, validated keys are cached in an in-memory store with a 5-minute TTL. If the cache is unavailable, authentication falls back gracefully to a direct database lookup — the system never rejects requests due to a cache failure.
Authentication Flow
The complete authentication and tenant-resolution flow for every API request:
+------------------+ | Incoming Request | +--------+---------+ | +--------v---------+ | Extract x-api-key | +--------+---------+ | +--------v---------+ | Rate limit check | | 300 req/min / IP | +--------+---------+ | +------------v-------------+ | Validate key | | 1. Check cache | | 2. Fallback to DB lookup | +------------+-------------+ | +------------v-------------+ | Resolve tenant context | | slug, plan, rate_limits | +------------+-------------+ | +------------v-------------+ | SET LOCAL app.current_ | | app_id = tenant UUID | +------------+-------------+ | +------------v-------------+ | RLS filters ALL queries | +------------+-------------+ | +------------v-------------+ | Process request | +--------------------------+Row-Level Security (RLS)
Row-Level Security is the cornerstone of DELPHOS tenant isolation. Every tenant-scoped table has RLS policies enforced at the PostgreSQL level — not in application code.
How it works
- Middleware extracts the tenant from the authenticated API key
- A
SET LOCALstatement setsapp.current_app_idto the tenant’s UUID - PostgreSQL RLS policies on every tenant-scoped table filter rows to
match only
app_id = current_setting('app.current_app_id') - The session variable is transaction-scoped and auto-cleared
Tables with RLS policies
| Table | Data protected |
|---|---|
apps | Tenant configuration |
api_keys | Key metadata and permissions |
documents | Uploaded documents and attachments |
knowledge_collections | Knowledge base collections |
usage_tracking | API usage and quota counters |
| Patient data tables | All clinical and personal health records |
RLS in practice
Every database connection sets the tenant context before executing any query. This is enforced by middleware — individual route handlers never set or clear the context manually.
Request arrives -> Middleware authenticates key -> Middleware calls SET LOCAL app.current_app_id = '<tenant-uuid>' -> Route handler executes queries (all filtered by RLS) -> Transaction commits -> Session variable auto-clearedRate Limiting
DELPHOS applies rate limiting at two independent layers to protect against abuse and ensure fair resource allocation.
Layer 1 — Authentication rate limit
A fixed-window counter limits all incoming requests to 300 per minute per IP address, enforced before any key validation occurs.
| Property | Value |
|---|---|
| Window | 1 minute, fixed |
| Limit | 300 requests per IP |
| Counter | Atomic increment in cache |
| IP resolution | X-Forwarded-For aware (respects reverse proxies) |
| Rejection | HTTP 429 Too Many Requests with Retry-After header |
Layer 2 — Tenant quota limits
After authentication, resource-intensive endpoints enforce per-tenant quotas. These are configured per plan and can differ by endpoint.
Endpoints with quota enforcement:
| Endpoint | Resource type |
|---|---|
/v1/chat/completions | AI inference |
/v1/ocr | Document processing |
/v1/rag/* | Knowledge base operations |
/v1/consultation/* | Clinical consultation AI |
Quotas support multi-window limits — the same tenant can have separate caps for minute, hour, day, and month windows. Enterprise plans can be configured to bypass quota limits entirely.
Rate limit headers
All responses include standard rate limit headers:
| Header | Description |
|---|---|
X-RateLimit-Limit | Maximum requests allowed in current window |
X-RateLimit-Remaining | Requests remaining in current window |
X-RateLimit-Reset | UTC epoch seconds when the window resets |
Retry-After | Seconds to wait (included only on 429 responses) |
HTTP/1.1 200 OKX-RateLimit-Limit: 300X-RateLimit-Remaining: 247X-RateLimit-Reset: 1713100860HTTP/1.1 429 Too Many RequestsRetry-After: 23X-RateLimit-Limit: 300X-RateLimit-Remaining: 0X-RateLimit-Reset: 1713100860
{ "detail": "Rate limit exceeded. Try again in 23 seconds."}CORS Configuration
Cross-Origin Resource Sharing is configurable per deployment via the
CORS_ALLOWED_ORIGINS environment variable.
| Setting | Value |
|---|---|
| Default origins | http://localhost:* (development only) |
| Configuration | Comma-separated list of allowed origins |
| Credentials | Allowed (Access-Control-Allow-Credentials: true) |
| Error responses | Include CORS headers (global exception handlers ensure browsers receive CORS headers even on 4xx/5xx) |
# Example: allow your production frontend and stagingCORS_ALLOWED_ORIGINS=https://app.clinica-aurora.com.br,https://staging.clinica-aurora.com.brAudit Logging
Every security-relevant event is recorded in the security_audit_log
table. Audit logging is asynchronous — events are dispatched via
background tasks so they never block the request that triggered them.
Event types
| Event | Severity | Trigger |
|---|---|---|
CROSS_TENANT_ATTEMPT | CRITICAL | A request attempted to access a resource belonging to another tenant |
ACCESS_DENIED | WARNING | Authorization check failed (insufficient permissions) |
AUTHENTICATION_FAILED | WARNING | Invalid, expired, or revoked API key presented |
RATE_LIMIT_EXCEEDED | WARNING | Request rejected by rate limiter |
KNOWLEDGE_QUERY | INFO | Knowledge base search executed |
COLLECTION_CREATED | INFO | New knowledge base collection provisioned |
DOCUMENT_INGESTED | INFO | Document added to a collection |
Audit record fields
Every audit entry includes:
| Field | Type | Description |
|---|---|---|
event_type | string | One of the event types listed above |
app_id | uuid | Tenant that generated the event |
attempted_resource | string | Resource path or identifier |
ip_address | string | Client IP (proxy-aware) |
user_agent | string | Client user-agent string |
details | jsonb | Structured metadata specific to the event type |
severity | string | CRITICAL, WARNING, or INFO |
timestamp | timestamptz | UTC timestamp of the event |
LGPD Compliance
DELPHOS is designed to comply with the Lei Geral de Proteção de Dados (LGPD, Lei 13.709/2018), Brazil’s comprehensive data protection law. The following measures address specific LGPD requirements for the processing of sensitive health data.
Data minimization (Art. 6, III)
Patient names are stored as cryptographic hashes only — plaintext names are never persisted in the database. This ensures that even in the event of a database breach, patient identities cannot be directly recovered from DELPHOS data alone.
Sensitive health data protection (Art. 11)
Health data is classified as dados pessoais sensíveis under LGPD. DELPHOS applies additional protections:
- Tenant isolation via RLS — clinical data cannot be accessed across organizational boundaries
- Patient identifiers in POST bodies only — never in URL paths, to prevent leakage in server access logs, browser history, or proxy logs
- Soft deletes — records are deactivated rather than physically deleted, preserving audit trail integrity while removing data from active queries
- Audio processing in memory only — voice data is processed entirely in RAM; no audio files are written to disk at any point; memory is cleared immediately after transcription completes
Audit trail (Art. 37)
LGPD requires data controllers to maintain records of processing activities and demonstrate compliance upon request. DELPHOS satisfies this through:
- Security audit log — every data access, modification, and security event is recorded with actor, timestamp, and details
- Right to information — audit logs track exactly who accessed what data and when, enabling responses to data subject requests
- Non-blocking logging — audit records are written asynchronously so they never impact clinical workflow performance
Tenant isolation as data sovereignty
Each tenant (clinic, hospital, or health organization) has its data completely isolated via Row-Level Security. This architectural guarantee means:
- One organization cannot access another’s patient data
- Data controllers maintain sovereignty over their data
- Multi-tenant deployments meet the same isolation standard as dedicated databases
On-premise deployment
For organizations that require data to never leave their infrastructure, DELPHOS supports full on-premise deployment. In this model:
- All services run within the organization’s own network
- No data is transmitted to external servers
- The organization maintains full physical and logical control over data storage
- AI inference runs locally on the organization’s hardware
Log privacy
Clinical log aggregation is bound to localhost only — log data is never exposed on external network interfaces, preventing accidental exposure of operational metadata.
Network Security
DELPHOS follows a minimal-exposure network architecture:
| Layer | Policy |
|---|---|
| Public surface | Only the API gateway port (8000) is exposed to the network |
| Internal services | All AI engines, databases, and support services listen on localhost only |
| Secrets management | PostgreSQL superuser password injected via Docker secrets; application credentials migrating from environment variables to secrets |
| TLS termination | Handled by a reverse proxy (nginx, Caddy, or cloud load balancer) in front of the API gateway |
Internet | +-----v------+ | TLS Proxy | (port 443 -> 8000) +-----+------+ | +-----v------+ | API Gateway | (port 8000, only public service) +-----+------+ | +------------+------------+ | | | localhost localhost localhost :8001 :8002 :5432 AI Engine AI Engine PostgreSQLSecurity Headers
All API responses include security-relevant headers:
| Header | When included | Purpose |
|---|---|---|
X-RateLimit-Limit | Every response | Current window limit |
X-RateLimit-Remaining | Every response | Remaining requests |
X-RateLimit-Reset | Every response | Window reset time |
Retry-After | 429 responses | Seconds until retry is allowed |
WWW-Authenticate: ApiKey | 401 responses | Indicates required auth scheme |
Access-Control-Allow-Origin | CORS requests | Permitted origin |
Access-Control-Allow-Credentials | CORS requests | Credentials policy |
Security Checklist for Integrators
If you are integrating with the DELPHOS API, follow these practices:
- Store API keys securely — use a secrets manager or encrypted environment variables; never commit keys to source control
- Use environment-specific keys — production keys should never be used in development or staging
- Set key expiration — rotate keys periodically and set expiration dates as a safety net
- Send patient IDs in request bodies — never include patient identifiers in URL paths or query parameters
- Monitor rate limit headers — implement exponential backoff when approaching limits
- Enable TLS — always connect over HTTPS in production
- Restrict CORS origins — configure
CORS_ALLOWED_ORIGINSto your specific frontend domains - Monitor audit logs — set up alerts for
CROSS_TENANT_ATTEMPTandAUTHENTICATION_FAILEDevents - Prefer on-premise for sensitive deployments — if regulatory requirements demand it, deploy DELPHOS within your own infrastructure