[Entity Store] Entity Unique identifier (EUID) translation layer#250951
Merged
romulets merged 24 commits intoelastic:mainfrom Feb 6, 2026
Merged
[Entity Store] Entity Unique identifier (EUID) translation layer#250951romulets merged 24 commits intoelastic:mainfrom
romulets merged 24 commits intoelastic:mainfrom
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces a unified Entity Unique Identifier (EUID) abstraction across the entity store, with shared logic for computing, querying, and validating entity IDs in memory, ES|QL, Painless, and DSL, and wires it into both server-side code and Scout-based integration tests.
Changes:
- Refactors entity identity definitions to use a generic
euidFieldsdescription andrequiresOneOfFields, and adds shared helpers for EUID computation in memory (getEuidFromObject), ES|QL (getEuidEsqlFilter/getEuidEsqlEvaluation), Painless (getEuidPainlessEvaluation), and DSL (getEuidDslFilterBasedOnDocument). - Adapts logs extraction ES|QL query generation to use the new EUID helpers and updates snapshots and test data/expectations accordingly.
- Adds Scout API tests and fixtures to validate extraction, DSL/EUID round-trips, and Painless runtime-field behavior against the archived data.
Reviewed changes
Copilot reviewed 26 out of 26 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
x-pack/solutions/security/plugins/entity_store/test/scout/api/tests/painless_translation.spec.ts |
New Scout tests verifying Painless runtime-field EUID computation matches in-memory getEuidFromObject for each entity type. |
x-pack/solutions/security/plugins/entity_store/test/scout/api/tests/entity_extraction.spec.ts |
Refactors entity extraction expectations into shared fixtures and updates expected counts/entities for host/user/service/generic. |
x-pack/solutions/security/plugins/entity_store/test/scout/api/tests/dsl_translation.spec.ts |
New Scout tests validating that the DSL produced by getEuidDslFilterBasedOnDocument selects the intended documents for each entity type. |
x-pack/solutions/security/plugins/entity_store/test/scout/api/fixtures/entity_extraction_expected.ts |
Centralized expected entity documents for extraction tests across all entity types. |
x-pack/solutions/security/plugins/entity_store/test/scout/api/fixtures/constants.ts |
Adds UPDATES_INDEX mapping for each EntityType to its updates data stream. |
x-pack/solutions/security/plugins/entity_store/test/scout/api/es_archives/updates/data.json |
Adds an extra host update document with host.entity.id to exercise new EUID precedence. |
x-pack/solutions/security/plugins/entity_store/server/index.ts |
Exposes a public euid helper API and re-exports EntityType for other plugins to consume without using the plugin start contract. |
x-pack/solutions/security/plugins/entity_store/server/domain/logs_extraction/logs_extraction_query_builder.ts |
Switches ES |
x-pack/solutions/security/plugins/entity_store/server/domain/logs_extraction/__snapshots__/logs_extraction_query_builder.test.ts.snap |
Updates Jest snapshots to reflect the new ES |
x-pack/solutions/security/plugins/entity_store/server/domain/euid/painless.ts |
Implements Painless EUID script generation based on euidFields metadata. |
x-pack/solutions/security/plugins/entity_store/server/domain/euid/painless.test.ts |
Snapshot tests for getEuidPainlessEvaluation per entity type. |
x-pack/solutions/security/plugins/entity_store/server/domain/euid/memory.ts |
In-memory EUID computation (getEuidFromObject) and nested field accessor (getFieldValue) based on the new euidFields description. |
x-pack/solutions/security/plugins/entity_store/server/domain/euid/memory.test.ts |
Unit tests validating precedence and fallbacks for getEuidFromObject across all entity types. |
x-pack/solutions/security/plugins/entity_store/server/domain/euid/index.ts |
Barrel export for EUID helpers (memory, Painless, DSL, ES |
x-pack/solutions/security/plugins/entity_store/server/domain/euid/esql.ts |
ES |
x-pack/solutions/security/plugins/entity_store/server/domain/euid/esql.test.ts |
Tests for getEuidEsqlFilter and getEuidEsqlEvaluation (generic and host). |
x-pack/solutions/security/plugins/entity_store/server/domain/euid/dsl.ts |
Builds a DSL filter from a sample document using euidFields and ranking, plus negative conditions for higher-precedence fields. |
x-pack/solutions/security/plugins/entity_store/server/domain/euid/dsl.test.ts |
Tests for DSL filter generation for generic, host, user, and service, including precedence and “no EUID” cases. |
x-pack/solutions/security/plugins/entity_store/server/domain/euid/__snapshots__/painless.test.ts.snap |
Snapshots for generated Painless EUID scripts. |
x-pack/solutions/security/plugins/entity_store/server/domain/esql/strings.ts |
Loosens the type of esqlIsNotNullOrEmpty to accept optional field names for use with EuidAttribute. |
x-pack/solutions/security/plugins/entity_store/server/domain/definitions/user.ts |
Migrates user identity definition to requiresOneOfFields/euidFields and adds retention config for user.entity.id. |
x-pack/solutions/security/plugins/entity_store/server/domain/definitions/service.ts |
Migrates service identity definition to requiresOneOfFields/euidFields and adds retention config for service.entity.id. |
x-pack/solutions/security/plugins/entity_store/server/domain/definitions/registry.ts |
Adds getEntityDefinitionWithoutId as a reusable accessor for identity logic. |
x-pack/solutions/security/plugins/entity_store/server/domain/definitions/host.ts |
Migrates host identity definition to requiresOneOfFields/euidFields and adds retention config for host.entity.id. |
x-pack/solutions/security/plugins/entity_store/server/domain/definitions/generic.ts |
Simplifies generic identity to euidFields and commentary around entity.id. |
x-pack/solutions/security/plugins/entity_store/server/domain/definitions/entity_schema.ts |
Redefines the identity schema around euidFields and introduces EuidAttribute typing used across EUID helpers. |
x-pack/solutions/security/plugins/entity_store/server/domain/euid/dsl.ts
Outdated
Show resolved
Hide resolved
x-pack/solutions/security/plugins/entity_store/common/domain/euid/dsl.ts
Show resolved
Hide resolved
x-pack/solutions/security/plugins/entity_store/server/domain/euid/memory.test.ts
Outdated
Show resolved
Hide resolved
x-pack/solutions/security/plugins/entity_store/common/domain/euid/memory.test.ts
Show resolved
Hide resolved
x-pack/solutions/security/plugins/entity_store/server/domain/euid/memory.test.ts
Outdated
Show resolved
Hide resolved
x-pack/solutions/security/plugins/entity_store/server/domain/euid/memory.test.ts
Outdated
Show resolved
Hide resolved
x-pack/solutions/security/plugins/entity_store/server/domain/euid/memory.test.ts
Outdated
Show resolved
Hide resolved
x-pack/solutions/security/plugins/entity_store/common/domain/euid/memory.test.ts
Show resolved
Hide resolved
...security/plugins/entity_store/server/domain/logs_extraction/logs_extraction_query_builder.ts
Show resolved
Hide resolved
opauloh
approved these changes
Jan 31, 2026
x-pack/solutions/security/plugins/entity_store/common/domain/definitions/host.ts
Show resolved
Hide resolved
x-pack/solutions/security/plugins/entity_store/common/domain/euid/memory.ts
Show resolved
Hide resolved
chennn1990
reviewed
Feb 2, 2026
x-pack/solutions/security/plugins/entity_store/server/domain/definitions/entity_schema.ts
Outdated
Show resolved
Hide resolved
x-pack/solutions/security/plugins/entity_store/server/domain/definitions/entity_schema.ts
Outdated
Show resolved
Hide resolved
chennn1990
approved these changes
Feb 2, 2026
Contributor
⏳ Build in-progress
Failed CI StepsTest FailuresHistory
cc @romulets |
abhishekbhatia1710
added a commit
to abhishekbhatia1710/kibana
that referenced
this pull request
Mar 10, 2026
The Entity Store V2 EUID PR (elastic#250951) is merged. This migrates the lead generation pipeline from V1 per-entity-type indices to V2's unified index pattern (.entities.v2.latest.security_{namespace}). - fetchAllEntityStoreRecords now queries a single V2 index instead of looping over separate user/host V1 indices - entityRecordToLeadEntity falls back to entity.id (EUID) when entity.name is absent - temporal_state_module uses V2 history snapshot pattern and filters by entity.type/entity.name instead of V1 entity-type-specific fields - De-duplicated entityToKey by importing from shared utils - Added unit tests for fetchAllEntityStoreRecords, entityRecordToLeadEntity, and getEntityStoreLatestIndex
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements functions to support other teams usage of the Entity Unique Identifier EUID.
These are all naive implementations doing sequential checks. The current implementation allows teams to start using already. We might still tweak their logic to have less checks (e.g. have only 1
host.namecheck instead of 2 in thehostdefinition).How to use it
Note 1:
If for some reason those functions are needed in the front end, we might need to move toDone!commons, but that will also require refactoring definitions to be undercommon. Possible, but let's do only if it's necessary.Note 2: We need to know the entity type to know what euid logic to follow. I'm not sure if there are cases where we don't know the entity type and we just want to find any id (the same document can return all ids). Then this logic will need to be handled on the consumer side (imo).
Added Functions
DSL
getEuidDslFilterBasedOnDocument(entityType: EntityType, doc: any)Based the entity id and on any document you have in memory (it can't be flattened, if that's a use case, we need to implement it) it returns a query DSL to be filtered on elasticsearch. For a full list of examples, see
x-pack/solutions/security/plugins/entity_store/server/domain/euid/dsl.test.ts.Simple example
Example where it must match one field and other ones must not be present
ESQL
getEuidEsqlFilterBasedOnDocument(entityType: EntityType, doc: any)ESQL counterpart of the DSL filter. Same concept of not being available for flattened objects apply.
Simple example
Example where it must match one field and other ones must not be present
ESQL
getEuidEsqlDocumentsContainsIdFilter(entityType: entityType)Generates ESQL filter to make sure that we only have EUID Eligible documents in the context of the query. This makes for aids better performance before generating ids ESQL for every document available in the query. It's used in the main Entity Store Query.
Example of host
ESQL
getEuidEsqlEvaluation(entityType: EntityType)Generates a ESQL
EVALexpression which calculates theeuid. It doesn't assign to any field so in your code you need to do it. E.g.It's used in the main Entity Store Query.
Example of eval for host
Typescript
getEuidFromObject(entityType: EntityType, doc: any)Based the entity id and on any document you have in memory (it can't be flattened, if that's a use case, we need to implement it) it returns an EUID. E.g.
Painless
getEuidPainlessEvaluation(entityType: EntityType)Generates a painless script for the EUID generation. It's based on the existence of Map
docand it returns null. It's hard to assume what will be available in the painless context or if we must return a value oremita value, depending on where you use this script (runtime, aggregation, transform). What is nice is that a Map will be available in some shape and form.For example, to use in a runtime field (tested with scout), you can wrap the generation around a function and emit the value:
Now looking at it, maybe we could be the ones providing it these context painless specific scripts. But the beauty of the current implementation is that users could use how they want, where they want, without depending on us to implement something new.
Example of generated painless script for host