Back to Kibana

Entity ID (EUID) Generation in Entity Store v2

x-pack/solutions/security/.agents/skills/entity-store/references/euid.md

9.5.013.3 KB
Original Source

Entity ID (EUID) Generation in Entity Store v2

This guide explains how Entity IDs are generated for Users, Hosts, and Services in the Entity Store v2. It is intended for anyone testing entity-related features, as the EUID algorithm's subtleties surface across risk scoring, flyout panels, identity resolution, and enrichment.


What is an EUID?

Every entity in the Entity Store has a logical identifier called an EUID (entity.id). This is a human-readable string that uniquely identifies the entity (e.g. user:[email protected]@okta).


General Structure

All EUIDs follow the pattern {entityType}:{composed_value} (e.g. host:ABC123, user:[email protected]@okta, service:my-api). The {entityType}: prefix is always prepended for Users, Hosts, and Services.


User Entities — Creation First, EUID Second

User entities are the easiest to test if you answer three questions in order:

  1. Can this document create a user entity at all?
  2. If yes, is it high-confidence or medium-confidence?
  3. What exact EUID should it produce?

User Entities at a Glance

TopicMedium-confidence user (local)High-confidence user (IdP-backed)
What it representsA user observed on one specific hostAn account owned by an IdP or account-managing platform
Typical sourcesElastic Defend, CrowdStrike, other endpoint/host telemetryOkta, Entra ID, Active Directory entity sync, AWS IAM-style activity
Minimum inputsuser.name + host.idA mapped IdP namespace plus one ranked identity composition
Creation gateMust not be a service account and must not be failed auth-only activityMust pass the IdP post-aggregation gate
EUID shapeuser:{user.name}@{host.id}@localuser:{identity_fields}@{namespace}

High-confidence creation gate: this is the rule that is easiest to miss during testing. A document in an IdP namespace does not automatically create a high-confidence user entity. It must also pass the IdP post-aggregation filter:

  • event.kind = asset, or
  • event.category = iam with qualifying event.type values: user, creation, deletion, or group
  • event.kind = enrichment is excluded from high-confidence entity creation

If that gate fails, the document may still look "IdP-like", but it does not create a high-confidence user entity by itself.

Step 1: Can This Document Create a User Entity?

Before reasoning about the EUID string, first check whether the document is eligible to create a user entity:

Document PatternOutcomeWhy
Known IdP/account namespace and passes the IdP post-aggregation gateContinue on the high-confidence pathThe source is acting like the system that owns or manages the account
Non-IdP/endpoint-style event with user.name + host.id, not a service account, not failed auth-onlyContinue on the medium-confidence pathThe source observed a user session on one host
IdP/account-looking event that fails the post-aggregation gateNo user entity createdNamespace alone is not enough
Service account on the local pathNo medium-confidence user entity createdShared/automation identities are intentionally excluded
Failed authentication-only activity on the local pathNo medium-confidence user entity createdPrevents phantom entities from brute force or password spray

Step 2: Decide Confidence Tier and Namespace

The namespace is derived from event.module or the first chunk of data_stream.dataset (split by .), mapped through explicit whenClauses:

Source valueCanonical namespace
okta, entityanalytics_oktaokta
azure, entityanalytics_entra_identra_id
o365, o365_metricsmicrosoft_365
entityanalytics_adactive_directory
(anything else)Falls through to the raw source value, or unknown if empty

After the namespace is evaluated, use this decision table:

ConditionConfidenceNamespaceOutcome
Source maps to a known IdP/account namespace and passes the IdP post-aggregation gateHighMapped namespace such as okta, entra_id, or active_directoryBuild a high-confidence EUID
Source does not qualify for the IdP path, but has user.name + host.id, is not a service account, and is not failed auth-onlyMediumForced to localBuild a medium-confidence EUID
Fields are present but neither path passes its guardrailsNoneN/ANo user entity is created

This is the key behavior to keep in mind: the system can see user fields on a document and still create no user entity.

Step 3: Build the EUID

Once the document is on a valid path, the final string is prepended with user:.

Medium-Confidence (local) EUID

EUID OutputWhen Used
user:{user.name}@{host.id}@localEndpoint-observed users on the local path

There is only one composition. The two components must be present, and the entity is structurally bonded to one host.

Testing note: the medium-confidence entity.name is {user.name}@{host.name} for display, but the EUID uses host.id. Do not confuse the display name with the entity ID.

High-Confidence (IdP-Backed) EUID

The system tries these compositions in ranked order and picks the first one where all required fields are present:

RankEUID OutputWhen Used
1user:{user.email}@{namespace}Most IdPs (Okta, Entra ID, Google Workspace, etc.)
2user:{user.id}@{namespace}IdPs where email is absent (AWS, Atlassian, Keycloak, etc.)
3user:{user.name}@{user.domain}@{namespace}Active Directory (name + domain disambiguates)
4user:{user.name}@{namespace}Fallback for IdPs with only user.name (GitHub, JumpCloud, etc.)

User Entity Examples

Example A — High-Confidence User Is Created

  • Fields present: [email protected], event.module=okta, event.category=["iam"], event.type=["user"]
  • Why it qualifies: the namespace maps to okta, and the document passes the IdP post-aggregation gate
  • EUID: user:[email protected]@okta

Example B — Medium-Confidence User Is Created

  • Fields present: user.name=jdoe, host.id=HW-UUID-ABC, event.module=endpoint
  • Why it qualifies: this is a non-IdP/endpoint event with user.name + host.id, and it is not blocked by the local-path guardrails
  • EUID: user:jdoe@HW-UUID-ABC@local

Example C — Looks IdP-Backed, but No Entity Is Created

  • Fields present: [email protected], event.module=okta, event.category=["authentication"], event.type=["start"]
  • Why it does not qualify: the namespace maps to okta, but the document does not have event.kind = asset, and it does not have event.category = iam with a qualifying lifecycle type
  • Outcome: no high-confidence user entity is created from this document

Example D — Local User Is Excluded by Guardrails

  • Fields present: user.name=www-data, host.id=HW-UUID-ABC, event.module=endpoint
  • Why it does not qualify: it matches the local shape, but www-data is a service account and is excluded from medium-confidence creation
  • Outcome: no medium-confidence user entity is created

The same no-entity outcome applies to failed authentication-only events, even when user.name + host.id are present.

Same person, different entities: Jane may appear as user:[email protected]@okta (her Okta account), user:[email protected]@entra_id (her Entra ID account), and user:jdoe@HW-UUID-LAPTOP@local (her laptop session). These are intentionally separate entities until Entity Resolution links them.


What is NOT Done

  • No case normalization: [email protected] and [email protected] produce different EUIDs. Field values are used as-is.
  • No trimming: Leading/trailing whitespace is preserved.
  • No deduplication across namespaces: [email protected]@okta and [email protected]@entra_id are different entities by design.
  • Arrays are collapsed to first element: If a field contains ["a", "b"], only "a" is used.

Document Filters and Guardrails

These are the same creation gates summarized above, repeated here as a quick checklist:

  • IdP post-aggregation filter: High-confidence entities require event.kind = asset or event.category = iam with qualifying event types (user, creation, deletion, group).
  • Enrichment events (event.kind = enrichment) are excluded from high-confidence entity creation.
  • Failed authentication events (event.outcome = failure) are excluded from medium-confidence creation — a brute-force attempt against admin@host must not create a phantom entity.
  • Service accounts are excluded from the local namespace: root, bin, daemon, sys, nobody, jenkins, ansible, deploy, terraform, gitlab-runner, postgres, mysql, redis, elasticsearch, kafka, admin, operator, service.

Host Entities — Ranked Single-Field Fallback

Host EUIDs use a priority-ranked fallback chain with no namespace concept. The first field present wins:

PriorityEUID OutputSource Field
1host:{host.id}host.id
2host:{host.name}host.name
3host:{host.hostname}host.hostname

Example: A machine with host.id = HW-UUID-ABC123 produces EUID host:HW-UUID-ABC123. If that same machine were seen by an integration that only reports host.name = prod-web-01, and host.id is absent, the EUID would be host:prod-web-01 — a different entity. This is the primary testing pitfall for hosts: the same physical machine can appear as two entities if different integrations populate different identity fields.

Strategic motivation: Host identity is simpler than user identity — there is no namespace. Any integration reporting host.id, host.name, or host.hostname can create or enrich a host entity. The ranking prefers host.id (hardware UUID, most stable) but falls back to DNS names when that is all an integration provides (common for network sensors, firewalls, and SIEMs that lack agent-level host instrumentation).

Service Entities

Service EUIDs are the simplest: a direct mapping from one field, no ranking, no namespace.

EUID OutputSource Field
service:{service.name}service.name

Example: service:nginx or service:elastic-agent.

Key Implementation Details

  • Branch selection: entity.namespace == 'local' → MC ranking; otherwise → HC ranking
  • First-match wins: A composition only matches if ALL its fields are present and non-empty
  • Document _id: MD5 hash of the EUID string (not the EUID itself)
  • EUID is immutable: Once computed, it's the entity's permanent identity
  • Resolution uses EUID: resolved_to stores the target entity's EUID string

Common QA Scenarios

ScenarioWhy it happens
"Why did clicking this user open a different user's flyout?"Entity is an alias (resolved_to set). Flyout navigates to the golden/target entity.
"Why are there two entities for the same person?"Same person, different IDP accounts (e.g., user:emily@okta vs user:emily@entra_id). Expected — resolution links them.
"Why is this user entity showing user:jdoe@laptop-A@local?"MC entity from endpoint data. entity.namespace == 'local', uses [email protected]@local format.
"Why does this entity have no email in its EUID?"user.email was empty for that source event. Algorithm fell through to priority 2+ (user.id or user.name).
"Why did this entity not get created at all?"Failed pipeline gates: either documentsFilter (pre-agg) or postAggFilter (post-agg) excluded the source events.

Code Locations

FilePurpose
common/domain/definitions/user.tsUser entity: HC/MC branches, confidence, namespace mapping
common/domain/definitions/host.tsHost entity: 3-field linear ranking
common/domain/definitions/service.tsService entity: single-field identity
common/domain/euid/memory.tsIn-memory EUID calculation (getEuidFromObject)
common/domain/euid/commons.tsField ranking logic (getEffectiveEuidRanking)
common/domain/euid/esql.tsESQL generation for extraction pipelines
common/domain/euid/field_evaluations.tsField evaluation application

All paths relative to x-pack/solutions/security/plugins/entity_store/.