Blog

One SOC, 100 projects: running centralized alert triage on Elastic Security Serverless

We linked one Elastic Security project to 100 others and ran the full prebuilt detection catalog from the origin, with all the ingest landing in the linked projects. It held up, and where it deliberately does not reach is the interesting part.

Run security operations for more than one team, region, or customer, and you inherit a familiar tradeoff. One giant deployment gives you a single view but costs you tenant isolation, while separate environments preserve isolation and scatter your analysts across contexts, with detection rules drifting into 12 slightly different copies along the way. Cross-project search (CPS) on Elastic Cloud Serverless is built to end that tradeoff. One Elastic Security project runs detection and triage, along with investigation, across every linked project, and each environment keeps its own data, isolation, and lifecycle. Cross-cluster search (CCS) has played this role on Elastic Cloud Hosted for years; CPS brings a similar model to Serverless, adapted to projects instead of clusters.

To make sure that holds under real security operations center (SOC) load, we linked one Elastic Security project to 100 others and pointed all the ingest at the linked projects (that's how real central SOCs are shaped). We ran roughly 2,100 prebuilt detection rules from the origin. The search layer held up. Most of what we learned was about how to run the model well; that is, which query shapes stay fast at that scale and how to size your rule set. We also learned where the project boundary is doing its job on purpose.

This post covers how the central SOC model works with CPS, the analyst workflow we validated end to end, the Elasticsearch Query Language (ES|QL) patterns we recommend, and the limitations. 

CPS lets one Serverless project (the origin) search data in other projects in your organization (linked projects). For Security, that enables the central SOC pattern. Detection rules live and run on the origin, and their index patterns resolve across linked projects, so one rule covers every linked project with no duplication. The alerts that those rules generate land on the origin, which means that the SOC works a single queue. And the data itself never moves; each linked project keeps its own.

CPS reads across linked projects but never writes into them. Everything that a rule produces (alerts, cases, investigations) lives on the origin. That boundary is deliberate. It also shapes every section below, so keep it in mind.

The analyst workflow: detect, triage, investigate, respond

Say the cloud team's linked project shows repeated failed AWS console logins. Here's the flow we tested.

Detect: one rule set covering every linked project

A query rule on the origin targets logs-*. The pattern fans out to every linked project, matches the CloudTrail events, and generates alerts on the origin.

Triage: a single alert queue on the origin

Because every rule runs on the origin, the alert queue is already consolidated. Analysts triage in the Alerts page the way they always have: severity, tags, assignment, and status all work normally, because these are origin-local alerts. There’s no project hopping, and there are no duplicate alerts from per-project rule copies.

An origin alert whose source event lives in a linked project; kibana.alert.ancestors.index carries the provenance. 

Investigate: following the chain across linked projects

The cross-project reach that powered detection now becomes visible to the analyst. The alert flyout renders source-event context from the linked project, and Timeline search spans linked projects, so you can follow the chain (failed logins, password reset, privilege escalation, access key creation) without leaving the origin. When you need to go wider, Discover is the CPS surface, for broad sweeps across every linked project or targeted queries against one. Examples are below.

Following the chain in Timeline: repeated failures and then AttachUserPolicy and CreateAccessKey, all from linked-project data. 

Respond: triage centrally, act locally

The same boundary applies to response. Closing an alert, isolating a host, or running osquery happens in the project that owns the data, because CPS doesn't write across a project boundary. That's what keeps one tenant's response from reaching into another's. Write it into your runbook as "triage centrally, act locally."

How to hunt and investigate across linked projects with ES|QL

Three query shapes cover most of what an analyst needs from the origin. The inline comments call out the parts that matter.

Cross-tenant threat hunting: search every linked project at once

An unqualified FROM fans out to the origin plus every linked project (flat world), which makes it the right shape for hunting one entity's activity across every linked project at once. It's also what makes the scope dynamic; projects linked tomorrow are covered by the same query with no edits. 

/*
  Broad sweep: hunt one user's activity across every linked project.
*/
FROM logs-* METADATA _index
    /* METADATA _index is your project attribution. Linked-project rows arrive
       with the project alias as an index prefix; there is no ECS "project"
       field. If you need to know which tenant an event came from, this is how. */
| WHERE user.name == "svc-deploy" AND event.category == "authentication"
| KEEP _index, @timestamp, event.action, source.ip, host.name
    /* KEEP is not cosmetic. Dragging full _source at depth was many times
       slower than projecting columns in our testing. If a cross-project query
       feels slow, check this before you blame the fan-out. */
| SORT @timestamp DESC
| LIMIT 500

One query from the origin, results from every linked project; the _index prefix is your tenant attribution. 

/*
  Targeted: the same hunt, scoped to a single linked project.
  Qualifying the index pattern contacts only that project.
*/
FROM cloud-team:logs-* METADATA _index
| WHERE user.name == "svc-deploy" AND event.category == "authentication"
| KEEP @timestamp, event.action, source.ip, host.name
| SORT @timestamp DESC
| LIMIT 500

Alert counts by project and severity

For situational awareness, roll activity up per project by deriving the project from the index name:

/*
  Alert counts by project and severity, i.e. the "which tenant is on fire" view.
*/
FROM .alerts-security.alerts-* METADATA _index
| EVAL project = CASE(LOCATE(_index, ":") > 0, MV_FIRST(SPLIT(_index, ":")), "origin")
    /* Linked-project rows carry an "alias:" prefix on _index; splitting on the
    	colon yields the project. Origin rows have no prefix, so label them
    	"origin" instead of showing the raw backing index name. */
 | STATS alerts = COUNT(*) BY project, kibana.alert.severity
 | SORT alerts DESC

Alert counts by project and severity, from a single ES|QL query at the origin. 

In our test environment, rollups like this stayed interactive across all 100 linked projects.

Which surfaces honour the cross-project scope

Discover and non-managed dashboards honor the full cross-project scope. Security's managed pages show the CPS scope selector as read-only, so analysts can see which projects are in scope while admins set the defaults per space.

Analysts can see the CPS scope; managing linked projects requires an admin. 

What we learned running centralized alert triage across 100 projects

Anyone can publish a benchmark with a friendly workload, so we built ours like an actual SOC. Ingest flowed only into the linked projects, with the full prebuilt detection rule catalog (~2,100 rules) enabled at the origin. Analyst-persona queries ran on schedules that mirror real behavior: 10-minute triage sweeps, weekly reviews, 30-day incident response (IR) lookbacks.

The headline is that the central SOC model holds at that scale. A single origin ran the entire detection catalog against the data from 100 linked projects. Alerts landed in one queue, and analyst query patterns stayed interactive. Along the way, the test results turned into sizing guidance that you can use:

  • Linked project count isn’t your cost driver. Query cost scales with per-project data volume and query shape far more than with the number of linked projects. Quiet projects are close to free, which is exactly what makes 100 of them practical.

  • Project your columns. Full _source retrieval at depth is dramatically slower than KEEP-projected queries. Interactive triage and hunting queries should always project; save full documents for exports.

  • Treat rule count times schedule as a sizing input, the same way that you treat ingest. Running the full ~2,100 prebuilt detection rule catalog from one origin is an extreme case, and we did it to find the envelope; plenty of those rules had no matching data in linked projects, and the noisiest ones were the first things we disabled. The practical guidance that came out of it is simple: enable the rules that you'll actually triage, and lean on alert suppression for the noisy frontline ones.

What cross-project search doesn't do across the project boundary

We'd rather you design around these than discover them mid-incident.

Capability

Works across linked projects?

Where it runs / what to do

Alerts from origin rules

Yes

Generated on the origin from linked-project data. Fully actionable.

Alerts a linked project generates itself

No

Never enter the origin queue. Readable in Discover only.

Attack Discovery

No

Origin-generated alerts only, same as the Alerts page.

Response actions (close alert, isolate host, osquery), case attachments, workflow updates

No

Run in the project that owns the data. Triage centrally, act locally.

Timeline, Analyzer, Correlations

Yes, read-only

Linked-project alerts appear as context. Analysts can read but not act.

Session View

No

Not available for linked projects.

CSPM, KSPM, CNVM findings

No

Produced in the project where the integrations run. Review posture there.

SIEM Readiness and Value reports

No

Origin-scoped.

Entity store

Partial

Origin profiles include entities from every linked project, but each project computes its own risk score. A host in two projects is not merged.

Machine learning anomaly detection

Partial

Datafeeds read linked-project data. Jobs and results are stored on the origin. Plan ML capacity there.

Three more constraints sit outside that table.

  • Rules target all linked projects by design. That default is what keeps detection coverage automatic as projects are linked and unlinked, so there's deliberately no per-rule project picker in the rule form. When you do need a rule scoped to a subset, use project routing at the API level. One related behavior worth knowing: a rule searches only the linked projects that its last editor can access, so create and update rules with an identity that spans your projects. 

  • All projects must share one Elastic Cloud organization, and an origin can link up to 100 projects by default; higher limits are available by working with Elastic.

  • Know your alert cap. The max alerts per run limit applies across everything in the rule's scope, so at 100 linked projects, a chatty rule can hit it. Suppression and per-rule tuning matter more at this scale. 

When a centralized SOC platform fits (and when it doesn't)

Good candidates share four traits:

  1. Multiple environments (teams, regions, customers) that need independent data ownership but shared security operations.

  2. A detection strategy that you're willing to centralize on the origin: one rule set, one alert queue.

  3. Triage workflows that are read-heavy at the center, with response actions executable per tenant.

  4. Analysts comfortable in Discover and ES|QL for cross-tenant sweeps.

Poor candidates look like the opposite. If your workflow depends on centrally acting on remote alerts and endpoints today, if you have hard requirements for unified cross-project risk scoring, or if your tenants are spread across multiple Elastic Cloud organizations, wait or plan a hybrid.

How to set up centralized alert triage across linked projects

  1. Run all detection rules on the origin. Splitting rule execution across projects buys you duplicate alerts and confusing side effects.

  2. Tag your projects at link time (region, tier, customer). Tags are how you'll scope and group later.

  3. Build hunting queries with METADATA _index and KEEP from day one.

  4. Keep Kibana space names distinct across projects.

  5. Size your rule set to your link count, and write the "act locally" half of your runbook before you need it.

CPS for Security is generally available on Elastic Cloud Serverless. Try it against your own multi-project topology, and if you hit something we didn't cover here, tell us.

A consolidated queue is also a foundation to build on. For a look at where triage goes from here, see how an agentic SOC handles alert triage with Alert Zero

Don't have a Serverless project yet? Start an Elastic Cloud free trial. It takes about a minute to get a fully configured environment.

How helpful was this content?

Related Content

Data access: the hidden cost of security vendor lock-in

Data access: the hidden cost of security vendor lock-in

Mike Nichols
SOC case management and detection rule history in Elastic Security

SOC case management and detection rule history in Elastic Security

Kseniia Ignatovych
Your UEBA is lying to you: Why entity record quality decides everything

Your UEBA is lying to you: Why entity record quality decides everything

Erik Huang
Know who to watch before the incident finds you

Know who to watch before the incident finds you

Erik Huang