Logging Context System
CONA uses a separate logging context system to enrich logs with organization and user metadata. This is intentionally kept separate from the RLS (Row-Level Security) system to avoid circular dependencies and maintain clean separation of concerns.Architecture
Why Separate from RLS?
RLS System (packages/database/src/rls.ts)
- Purpose: Database security and multi-tenant isolation
- Scope: PostgreSQL session configuration variables
- Data:
orgId,userId,sub(Auth0 ID) - Used by: PostgreSQL RLS policies
Logging Context (apps/webapp/lib/logging-context.ts)
- Purpose: Enrich logs with metadata for monitoring and debugging
- Scope: Request-scoped context (isolated via AsyncLocalStorage)
- Data:
organizationId,organizationSlug,userId,userSub - Used by: Axiom logging, middleware logging, security monitoring
Benefits of Separation
- No Circular Dependencies: Logging doesn’t depend on database queries
- Clean Separation of Concerns: Security (RLS) vs Observability (Logging)
- Independent Evolution: Can change logging strategy without affecting RLS
- Performance: Logging context is lightweight and doesn’t touch the database
API Reference
initLoggingContext(params)
Initialize logging context for a new request (typically called once per request).
updateLoggingContext(params)
Update specific fields in the logging context (called as more data becomes available).
getLoggingContext()
Get the current logging context (read-only).
Usage Examples
Middleware Logging
organizationSlug(if auth’d)organizationId(if auth’d)userId(if auth’d)- Request URL, method, IP, etc.
Security Event Logging
- All fields from logging context
- Security-specific metadata
source: "security"tag
Server Action Logging
Where Context is Set
1. Authentication Flow (auth.ts)
getAuth0Session() - Sets userSub
getAuth0User() - Adds userId
requireAuth() - Adds organizationId and organizationSlug
Best Practices
- Initialize Early: Call
initLoggingContext()as soon as you have the Auth0 session - Update Incrementally: Use
updateLoggingContext()as more data becomes available - Read-Only Access: Always use
getLoggingContext()to access context (don’t modify directly) - Request Isolation: Context is automatically isolated per-request using AsyncLocalStorage
Security Considerations
Request Isolation with AsyncLocalStorage
The logging context uses Node.js’sAsyncLocalStorage to ensure each request has its own isolated context. This prevents race conditions where concurrent requests could overwrite each other’s context.
Without AsyncLocalStorage (vulnerable):
Why This Matters for Security Auditing
Security events must be accurately attributed to the correct user and organization. Without proper request isolation:- Brute force detection could flag the wrong IP/user
- Unauthorized access logs could show incorrect user IDs
- Audit trails would be unreliable for compliance
- Security incidents couldn’t be accurately investigated
Comparison: RLS vs Logging Context
| Aspect | RLS System | Logging Context |
|---|---|---|
| Purpose | Database security | Log enrichment |
| Storage | PostgreSQL session vars | AsyncLocalStorage (request-scoped) |
| Data | orgId, userId, sub | orgId, orgSlug, userId, userSub |
| Updated by | initRlsWrapper() | initLoggingContext() |
| Used by | PostgreSQL RLS policies | Axiom logs, middleware |
| Scope | Database transactions | Per-request (isolated) |