Ir al contenido
↑↓Navigate↵SelectescClose

Security monitoring

Investigate sign-ins and platform activity using security views, dashboards, and alerts

Esta página aún no está disponible en tu idioma.

How Nexus records sign-ins and platform activity for a security operations team, the read-only views those records are exposed through, and the dashboards and alerts built on them.

Three stores carry security-relevant data. Each keeps its own privacy boundary:

Store Contents Identity Read through
Audit database (nexus_audit) Sealed audit events: sign-ins, changes, data access, completed gateway requests Full: user, email, source address, user agent, org and project, login soc_* views
Identity and tenancy databases (nexus_auth, nexus_control) Current users, logins, keys, memberships, orgs, projects Names and owners; no hashes or secrets soc_* views
Request analytics (ClickHouse) Request rows, sessions, nexus-property values, agent traces Tenant scoped Query API, console, operator SQL
Operator logs (Loki) Service log events, HTTP access records, gateway completions None: tenant identifiers are rejected LogQL
Event When Attributes
user.signed_up Password registration completes email, source_ip, user_agent, auth_session_id
user.signed_in Password sign-in succeeds email, source_ip, user_agent, auth_session_id
user.sign_in_failed Password sign-in fails email (as typed, lowercased), reason, method, source_ip, user_agent
auth.session.refreshed A console session is refreshed email, source_ip, user_agent, auth_session_id
user.signed_out A console session is signed out email, source_ip, user_agent, auth_session_id
auth.oidc.login_succeeded Federated sign-in succeeds provider, email, source_ip, user_agent, auth_session_id
auth.oidc.login_denied Federated sign-in is refused provider, email when known, reason, source_ip, user_agent
admin.session.signed_in, admin.session.refreshed, admin.session.signed_out Administrator console session lifecycle email, source_ip, user_agent, auth_session_id
admin.sign_in_failed Administrator password sign-in fails as user.sign_in_failed
admin.session.denied A valid credential without an administrator assignment signs in to the administrator console email, source_ip, user_agent
auth.identity.linked A federated identity is linked to an existing account provider, email

reason on a failed password sign-in is one of bad_password, unknown_user, invalid_email (the typed value has no @), disabled, no_password (the account signs in only through an identity provider), account_unavailable, password_login_disabled, or error. The caller always receives the same error; only the audit record distinguishes the cases. Passwords are never recorded.

source_ip is the first address of the X-Forwarded-For header the console received, falling back to X-Real-IP; values that are not IP addresses are dropped and user agents are truncated to 512 characters, on both the audit event and the session row. The console sets source_ip and user_agent from the request it received and overwrites any values in the browser-supplied body. The first forwarded hop is whatever the client or an intermediate proxy put there, so the recorded address is reliable only when the edge in front of the console replaces inbound X-Forwarded-For with the connecting address.

auth_session_id identifies one interactive login. It is created at sign-in and kept when the session is refreshed, so every refresh and the sign-out carry the same value. It is not a token and cannot authenticate.

Audit events emitted by the auth, control plane, administrator control plane, query, and audit services while handling a console request carry the auth_session_id of the login that made the request. That includes credential creation and revocation, membership and role changes, classification and limit changes, tenancy lifecycle events, and data_access.* reads. Gateway traffic authenticates with API keys rather than logins; the api_key.created event records the login that created the key.

All other audit events (see ../architecture/traceability-auditability.md) are available through the same views. For a completed gateway request, gateway.request.completed records the API key, org and project, session_id, session_path, session_name, model, provider, status, latency, tokens, cost, and the nexus-property values in reporting_dimensions. Request and response content is never in the audit log.

Rejected gateway credentials are counted, not audited, in the Prometheus counter nexus_gateway_auth_failures_total{reason} with reason of missing, malformed, invalid, revoked, expired, or other.

When an OTLP endpoint is configured, every Rust service exports tracing events at info and above as OTLP log records (filter: OTLP_LOG_FILTER, default info), in addition to stderr. Each HTTP request other than /healthz, /readyz, and /metrics produces one http request completed record with the method, route template, status, status class, and latency to the response head (for a streamed response, the gateway request completed record carries the full duration). The gateway also writes one gateway request completed record per model request with the request id, route, provider, model, status, latency, fallback count, and token counts. The telemetry collector drops any log record carrying a tenant identifier; see ../security/telemetry-tiering.md.

Service migrations create the views below. A monitoring role granted SELECT on them can read nothing else: no tables, audit signing keys, password hashes, refresh-token digests, or credential hashes.

Every audit view has event_id, tenant_id, occurred_at, and sealed (true once the event is in a signed batch).

View Rows Columns
soc_auth_events Sign-in lifecycle events above kind, outcome (success, failure, session), surface (tenant, admin), method (password, oidc, session), user_id, email, reason, source_ip, user_agent, identity_provider, auth_session_id
soc_admin_changes Credential, membership, invitation, registration, tenancy, wallet, connection, and user-account events kind, category, action, actor_kind, actor_id, subject_kind, subject_id, workspace_id, org_id, project_id, credential_prefix, role, email, provider, auth_session_id, detail (all attributes)
soc_data_access data_access.* reads kind, accessed, actor_kind, actor_id, workspace_id, org_id, project_id, request_id, session_id, trace_id, viewed_event_id, view, payload_depth, auth_session_id
soc_usage_events gateway.request.completed request_id, api_key_id, auth_type, workspace_id, org_id, project_id, session_id, session_path, session_name, model, resolved_model, provider, endpoint_key, classification, status, latency_ms, fallback_count, prompt_tokens, completion_tokens, reasoning_tokens, cost_usd, policy_effect, properties (nexus-property values), property_sources
soc_policy_denials policy.decision that did not allow effect, action, principal_kind, principal_id, resource_kind, resource_id, reason_code, policy_id, classification
soc_event_timeline Every audit event kind, actor and subject, scope, email, source_ip, auth_session_id, session_id, api_key_id, classification
View Rows Columns
soc_users Accounts user_id, email, name, created_at, email_verified_at, disabled_at, active, has_password, identity_issuers, platform_admin
soc_login_sessions One row per login auth_session_id, user_id, email, kind (user, admin), started_at, last_used_at, expires_at, refresh_count (rotations after sign-in), first_source_ip, last_source_ip, last_user_agent, active (not revoked and not past its hard expiry; the inactivity limit is not applied)
soc_developer_tokens Personal developer keys token_id, user_id, email, name, prefix, scope ids, scopes, created_at, last_used_at, expires_at, revoked_at
View Columns
soc_workspaces, soc_orgs, soc_projects ids, slug, name, kind and owner where applicable, trace_logging_enabled (orgs and projects), created_at, deleted_at
soc_api_keys api_key_id, scope ids, name, prefix, created_by, scopes, trace_logging_enabled, created_at, last_used_at, expires_at, revoked_at, active
soc_trace_ingest_keys trace_ingest_key_id, scope ids, name, prefix, created_by, created_at, rotated_at, revoked_at
soc_memberships scope, user_id, workspace_id, org_id, project_id, role, created_at, revoked_at

Sign-in history for one user:

SELECT occurred_at, kind, outcome, method, source_ip, user_agent, reason, auth_session_id
FROM soc_auth_events
WHERE email = lower('someone@example.com')
ORDER BY occurred_at DESC
LIMIT 200;

Everything done during one login:

SELECT occurred_at, kind, subject_kind, subject_id, org_id, project_id
FROM soc_event_timeline
WHERE auth_session_id = 'sess_01J...'
ORDER BY occurred_at;

Requests from one external application session carrying a property:

SELECT occurred_at, request_id, api_key_id, model, status, cost_usd
FROM soc_usage_events
WHERE session_id = 'checkout-4821'
AND properties->>'customer_id' = 'acme'
ORDER BY occurred_at;

The same filters in ClickHouse request analytics:

SELECT completed_at, request_id, api_key_id, model, status_code, total_cost_usd
FROM vortex.requests
WHERE session_id = 'checkout-4821'
AND reporting_dimensions['customer_id'] = 'acme'
ORDER BY completed_at;

The Nexus Helm chart provisions, when security monitoring is enabled:

  • Read-only datasources Nexus Audit, Nexus Identity, and Nexus Tenancy that connect as a role limited to the soc_* views, with read-only transactions and a 60-second statement limit.
  • A Nexus Security folder with these dashboards:
    • Nexus Security - Authentication: successful and failed sign-ins, failure reasons, top failing emails and source addresses, administrator sign-ins and denials, per-user sign-in history, and active logins.
    • Nexus Security - Changes and Access: credential lifecycle, membership and role changes, tenancy and policy changes, policy denials, and who viewed audit records, requests, and payloads.
    • Nexus Usage - Adoption: active users and keys, sign-ups, new orgs, projects, and keys, and requests, tokens, and cost by model, provider, org, API key, and property value.
    • Nexus Investigation: filters for user, login, API key, session, request, source address, and up to three property key/value pairs, combined with AND, with drill-down links between them.
  • Alert rules for repeated failed sign-ins per account and per source address, administrator sign-in failures, platform provider credential changes, disabled accounts and manager grants, gateway credential rejection bursts, a stalled audit archive, and an audit event backlog.

Request and session rows link to the console request or session page. Prompt and response content is read there, which enforces payload permissions and records a data_access event; the dashboards never display it.

Chart settings, secrets, and access control are described in the nexus-iac chart documentation.

Correlation metadata sent by applications calling the gateway is what makes per-application investigation possible:

  • Send nexus-session-id (and optionally nexus-session-path and nexus-session-name) consistently for every request in one user conversation or job.
  • Send nexus-property-<key>: <value> for application identifiers such as a customer, tenant, or workflow. A workspace manager must first define <key> as a reporting dimension; requests that send an undefined key are rejected.
  • Never put secrets, credentials, or message content in session names or property values. They are stored in request analytics and the audit log.
  • The audit service builds the view indexes on audit_events concurrently at startup, one per migration, so replicas already serving keep ingesting. If the service is stopped during a build, Postgres keeps an invalid index under that name and the migration skips it on restart; run DROP INDEX CONCURRENTLY <name> for any audit_events_*_idx whose pg_index.indisvalid is false, then restart.
  • Reading the views directly, including through Grafana, does not create data_access.audit_viewed events. Restrict who can sign in to Grafana and use the database’s own logging if direct reads must be audited.
  • Older audit records can lack auth_session_id and sign-in client context; filters on those columns exclude them.
  • Gateway API-key rejections are counted per reason without the key or caller, because request volume makes per-attempt records impractical.