> ## Documentation Index
> Fetch the complete documentation index at: https://cona.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Logging Context

> How CONA enriches logs with organization and user context

# 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

```
┌─────────────────────────────────────────────────────────────┐
│                    Request Flow                             │
└─────────────────────────────────────────────────────────────┘
                     │
                     │ Auth0 Session Retrieved
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  getAuth0Session()                                          │
│  • Validates session                                        │
│  • Initializes logging context with user.sub               │
└────────────────────┬────────────────────────────────────────┘
                     │
                     │ User Fetched from DB
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  getAuth0User()                                             │
│  • Fetches user from database                               │
│  • Updates logging context with userId                      │
└────────────────────┬────────────────────────────────────────┘
                     │
                     │ Organization Context Added
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  requireAuth() / getOrganizationId()                        │
│  • Fetches organization from database                       │
│  • Updates logging context with:                            │
│    - organizationId                                         │
│    - organizationSlug                                       │
└────────────────────┬────────────────────────────────────────┘
                     │
                     │ All Subsequent Logs Include Context
                     ▼
┌─────────────────────────────────────────────────────────────┐
│  Axiom Logs                                                 │
│  • organizationId                                           │
│  • organizationSlug                                         │
│  • userId                                                   │
│  • userSub                                                  │
└─────────────────────────────────────────────────────────────┘
```

***

## 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

1. **No Circular Dependencies**: Logging doesn't depend on database queries
2. **Clean Separation of Concerns**: Security (RLS) vs Observability (Logging)
3. **Independent Evolution**: Can change logging strategy without affecting RLS
4. **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).

```typescript theme={null}
import { initLoggingContext } from "@/lib/logging-context";

initLoggingContext({
  userSub: "auth0|123456",
  userId: "user-uuid",
  organizationId: "org-uuid",
  organizationSlug: "acme-corp",
});
```

### `updateLoggingContext(params)`

Update specific fields in the logging context (called as more data becomes available).

```typescript theme={null}
import { updateLoggingContext } from "@/lib/logging-context";

// After fetching user from database
updateLoggingContext({
  userId: user.id,
});

// After fetching organization
updateLoggingContext({
  organizationId: org.id,
  organizationSlug: org.slug,
});
```

### `getLoggingContext()`

Get the current logging context (read-only).

```typescript theme={null}
import { getLoggingContext } from "@/lib/logging-context";

const context = getLoggingContext();
console.log(context.organizationSlug); // "acme-corp"
```

***

## Usage Examples

### Middleware Logging

```typescript theme={null}
import { logMiddlewareRequest } from "@/lib/middleware-logger";

export async function middleware(request: NextRequest) {
  // Automatically includes logging context if available
  logMiddlewareRequest(request);
}
```

**Logged Fields:**

* `organizationSlug` (if auth'd)
* `organizationId` (if auth'd)
* `userId` (if auth'd)
* Request URL, method, IP, etc.

### Security Event Logging

```typescript theme={null}
import { logSecurityEvent } from "@/lib/middleware-logger";

logSecurityEvent(request, response, {
  action: "auth_required",
  reason: "No valid session",
  blocked: true,
});
```

**Logged Fields:**

* All fields from logging context
* Security-specific metadata
* `source: "security"` tag

### Server Action Logging

```typescript theme={null}
import { log } from "@/app/lib/logger";
import { getLoggingContext } from "@/lib/logging-context";

export async function serverAction() {
  const context = getLoggingContext();
  
  await log({
    message: "Processing invoice",
    level: "info",
    metadata: {
      ...context, // Include all logging context
      invoiceId: "inv-123",
    },
  });
}
```

***

## Where Context is Set

### 1. Authentication Flow (`auth.ts`)

**`getAuth0Session()`** - Sets `userSub`

```typescript theme={null}
initLoggingContext({
  userSub: session.user.sub,
});
```

**`getAuth0User()`** - Adds `userId`

```typescript theme={null}
updateLoggingContext({
  userId: user.id,
});
```

**`requireAuth()`** - Adds `organizationId` and `organizationSlug`

```typescript theme={null}
updateLoggingContext({
  organizationId,
  organizationSlug: organization.slug,
});
```

***

## Best Practices

1. **Initialize Early**: Call `initLoggingContext()` as soon as you have the Auth0 session
2. **Update Incrementally**: Use `updateLoggingContext()` as more data becomes available
3. **Read-Only Access**: Always use `getLoggingContext()` to access context (don't modify directly)
4. **Request Isolation**: Context is automatically isolated per-request using AsyncLocalStorage

***

## Security Considerations

### Request Isolation with AsyncLocalStorage

The logging context uses Node.js's `AsyncLocalStorage` 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):**

```typescript theme={null}
// ❌ Module-level variable - shared across all requests
let loggingContext = {};

// Request A: initLoggingContext({ userId: "user-a" })
// Request B: initLoggingContext({ userId: "user-b" }) 
// Request A logs → incorrectly tagged with user-b!
```

**With AsyncLocalStorage (secure):**

```typescript theme={null}
// ✅ Request-scoped storage - isolated per request
const asyncLocalStorage = new AsyncLocalStorage<LoggingContext>();

// Request A and B have separate, isolated contexts
// No race conditions or cross-contamination
```

### 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

AsyncLocalStorage ensures security logs are always attributed to the correct request context.

***

## 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)             |

***

## Migration Notes

Prior to this system, organization slug was stored in RLS state, causing circular dependencies between logging and database layers. The new system cleanly separates these concerns:

**Before:**

```typescript theme={null}
// ❌ Circular dependency
import { getRlsState } from "@/app/lib/db/rls";
const rlsState = getRlsState();
logger.info("Event", { orgSlug: rlsState.orgSlug });
```

**After:**

```typescript theme={null}
// ✅ Clean separation
import { getLoggingContext } from "@/lib/logging-context";
const context = getLoggingContext();
logger.info("Event", { orgSlug: context.organizationSlug });
```
