Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
/packages/airlock_digital @elastic/security-service-integrations @elastic/sit-crest-contractors
/packages/akamai @elastic/security-service-integrations @elastic/sit-crest-contractors
/packages/amazon_security_lake @elastic/security-service-integrations @elastic/sit-crest-contractors
/packages/anduril_lattice @elastic/security-service-integrations
/packages/anthropic @elastic/integration-experience
/packages/anthropic_metrics @elastic/obs-infraobs-integrations
/packages/apache @elastic/obs-infraobs-integrations
Expand Down
93 changes: 93 additions & 0 deletions packages/anduril_lattice/LICENSE.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
Elastic License 2.0

URL: https://www.elastic.co/licensing/elastic-license

## Acceptance

By using the software, you agree to all of the terms and conditions below.

## Copyright License

The licensor grants you a non-exclusive, royalty-free, worldwide,
non-sublicensable, non-transferable license to use, copy, distribute, make
available, and prepare derivative works of the software, in each case subject to
the limitations and conditions below.

## Limitations

You may not provide the software to third parties as a hosted or managed
service, where the service provides users with access to any substantial set of
the features or functionality of the software.

You may not move, change, disable, or circumvent the license key functionality
in the software, and you may not remove or obscure any functionality in the
software that is protected by the license key.

You may not alter, remove, or obscure any licensing, copyright, or other notices
of the licensor in the software. Any use of the licensor’s trademarks is subject
to applicable law.

## Patents

The licensor grants you a license, under any patent claims the licensor can
license, or becomes able to license, to make, have made, use, sell, offer for
sale, import and have imported the software, in each case subject to the
limitations and conditions in this license. This license does not cover any
patent claims that you cause to be infringed by modifications or additions to
the software. If you or your company make any written claim that the software
infringes or contributes to infringement of any patent, your patent license for
the software granted under these terms ends immediately. If your company makes
such a claim, your patent license ends immediately for work on behalf of your
company.

## Notices

You must ensure that anyone who gets a copy of any part of the software from you
also gets a copy of these terms.

If you modify the software, you must include in any modified copies of the
software prominent notices stating that you have modified the software.

## No Other Rights

These terms do not imply any licenses other than those expressly granted in
these terms.

## Termination

If you use the software in violation of these terms, such use is not licensed,
and your licenses will automatically terminate. If the licensor provides you
with a notice of your violation, and you cease all violation of this license no
later than 30 days after you receive that notice, your licenses will be
reinstated retroactively. However, if you violate these terms after such
reinstatement, any additional violation of these terms will cause your licenses
to terminate automatically and permanently.

## No Liability

*As far as the law allows, the software comes as is, without any warranty or
condition, and the licensor will not be liable to you for any damages arising
out of these terms or the use or nature of the software, under any kind of
legal claim.*

## Definitions

The **licensor** is the entity offering these terms, and the **software** is the
software the licensor makes available under these terms, including any portion
of it.

**you** refers to the individual or entity agreeing to these terms.

**your company** is any legal entity, sole proprietorship, or other kind of
organization that you work for, plus all organizations that have control over,
are under the control of, or are under common control with that
organization. **control** means ownership of substantially all the assets of an
entity, or the power to direct its management and policies by vote, contract, or
otherwise. Control can be direct or indirect.

**your licenses** are all the licenses granted to you for the software under
these terms.

**use** means anything you do with the software requiring one of your licenses.

**trademark** means trademarks, service marks, and similar rights.
3 changes: 3 additions & 0 deletions packages/anduril_lattice/_dev/build/build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
dependencies:
ecs:
reference: "git@v9.3.0"
142 changes: 142 additions & 0 deletions packages/anduril_lattice/_dev/build/docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
{{- generatedHeader }}
# Anduril Lattice Integration for Elastic

## Overview

The Anduril Lattice integration for Elastic collects entity lifecycle events from the [Anduril Lattice](https://www.anduril.com/lattice/) command and control (C2) platform. Lattice fuses data from heterogeneous sensor networks — radar, ADS-B, AIS, RF/EW, optical, and Anduril hardware — into a Common Operational Picture (COP). Every tracked or managed item is an **Entity**: aircraft, surface vessels, ground vehicles, UAS, fixed sensors, geospatial zones, and signals of interest.

This integration collects all entity lifecycle events (created, updated, deleted, preexisting snapshot) and ships them to Elasticsearch for real-time situational awareness, threat monitoring, and asset tracking.

### Compatibility

Compatible with the Anduril Lattice REST API v1 (`/api/v1/entities/events`). Tested against Anduril Lattice SDK v4.0.0.

### How it works

The integration polls the Lattice long-poll API endpoint (`POST /api/v1/entities/events`) using a session-token cursor. Each poll returns a batch of entity lifecycle events. The session token is persisted between polls so the integration resumes from where it left off.

On the first poll (empty session token) the Lattice server delivers a snapshot of all currently live entities as `EVENT_TYPE_PREEXISTING` events, then transitions to streaming ongoing changes. Subsequent polls receive `EVENT_TYPE_CREATED`, `EVENT_TYPE_UPDATE`, and `EVENT_TYPE_DELETED` events as the operational picture evolves.

Two authentication modes are supported:
- **Static bearer token** — supply a single token; rotation is manual.
- **OAuth2 client credentials** — supply a client ID and secret; the integration fetches and refreshes access tokens automatically (30-minute lifetime).

For Anduril sandbox/developer environments, an additional sandbox authorization token is required.

## What data does this integration collect?

The Anduril Lattice integration collects entity lifecycle events from the Lattice long-poll API.

### Supported use cases

- **Real-time situational awareness** — track friendly, hostile, and neutral entities across air, surface, land, and sub-surface domains.
- **Asset health monitoring** — monitor connection status and battery/power state for Anduril hardware assets (towers, sensors).
- **Track analysis** — correlate track quality, sensor hit count, and kinematic data (position, velocity, heading) for sensor-detected contacts.
- **Threat detection** — alert on `DISPOSITION_HOSTILE` or `DISPOSITION_SUSPICIOUS` entity classifications.
- **Geospatial analysis** — visualize entity positions on Kibana Maps using the `anduril_lattice.entity.location.geo` geo_point field.

## What do I need to use this integration?

- **Anduril Lattice environment** — a production environment, or a sandbox via the [Anduril developer program](https://developer.anduril.com/guides/getting-started/set-up).
- **Credentials** — either a static bearer token, or an OAuth2 client ID and client secret generated from the Lattice admin console.
- **Network connectivity** — the Elastic Agent host must be able to reach the Lattice API over HTTPS (port 443).

## How do I deploy this integration?

### Agent-based deployment

Elastic Agent must be installed. For more details, check the Elastic Agent [installation instructions](https://www.elastic.co/guide/en/fleet/current/elastic-agent-installation.html). You can install only one Elastic Agent per host.

Elastic Agent polls the Lattice long-poll API and ships events to Elastic, where they are processed by the integration's ingest pipeline.

### Set up steps in Anduril Lattice

1. Log in to the Lattice admin console for your environment.
2. Navigate to **Integrations** and create a new integration to generate OAuth2 credentials (`client_id` and `client_secret`), or obtain a static bearer token from your Lattice administrator.
3. Note your Lattice API base URL (e.g., `https://lattice.yourdomain.com`).

Check warning on line 56 in packages/anduril_lattice/_dev/build/docs/README.md

View workflow job for this annotation

GitHub Actions / Lint user-facing content

Elastic.Latinisms: Latin terms and abbreviations are a common source of confusion. Use 'for example' instead of 'e.g'.
4. For sandbox environments, also obtain the `Anduril-Sandbox-Authorization` token from the [Lattice sandbox documentation](https://developer.anduril.com/guides/developer-tools/sandboxes).
5. Ensure the Elastic Agent host has HTTPS/443 access to the Lattice server.

#### Vendor resources

- [Anduril Lattice developer portal](https://developer.anduril.com)
- [Long-poll entity events API reference](https://developer.anduril.com/reference/rest/entities/long-poll-entity-events)
- [Getting started / authentication](https://developer.anduril.com/guides/getting-started/quickstart)
- [Lattice sandbox environments](https://developer.anduril.com/guides/developer-tools/sandboxes)

### Set up steps in Kibana

1. In Kibana, navigate to **Fleet** > **Integrations** and search for **Anduril Lattice**.

Check warning on line 69 in packages/anduril_lattice/_dev/build/docs/README.md

View workflow job for this annotation

GitHub Actions / Lint user-facing content

Elastic.MenuArrowsBold: Use '→' to separate menu items, not '>' or '=>'. Example: Select Manage index → Add lifecycle policy.
2. Click **Add Anduril Lattice** and configure the integration:
- **Lattice API URL** — the base URL of your Lattice environment (e.g., `https://lattice.example.com`).
- **Authentication** — provide either a static **Bearer Token**, or an **OAuth2 Client ID** and **Client Secret** for automatic token management.
- **Sandbox Token** — leave empty for production; provide the sandbox token for developer environments.
- **Interval** — how often to poll for new events (default: `5s`).
3. Save and deploy the policy to the Elastic Agent.

### Validation

After deploying the integration, verify data is flowing:

1. Open **Kibana** > **Discover**.

Check warning on line 81 in packages/anduril_lattice/_dev/build/docs/README.md

View workflow job for this annotation

GitHub Actions / Lint user-facing content

Elastic.MenuArrowsBold: Use '→' to separate menu items, not '>' or '=>'. Example: Select Manage index → Add lifecycle policy.
2. Select the `logs-anduril_lattice.entity-*` index pattern.
3. Check that events appear with `event.dataset: anduril_lattice.entity`.
4. Confirm entity fields such as `anduril_lattice.entity.entity_id`, `anduril_lattice.entity.ontology.template`, and `anduril_lattice.entity.mil_view.disposition` are populated.
5. Open **Kibana Maps** and add a layer using the `anduril_lattice.entity.location.geo` field to visualize entity positions.

## Troubleshooting

- No data collected: Verify the Lattice API URL is reachable from the Elastic Agent host and that the bearer token or OAuth2 credentials are valid.
- Authentication failures: For OAuth2, ensure the token URL follows the pattern `{base_url}/api/v1/oauth/token` and that the client credentials have the correct permissions in the Lattice admin console.
- Self-signed certificates: If the Lattice environment uses a private CA, configure the `SSL Configuration` field with the custom `certificate_authorities` path.
- Sandbox environments: Ensure the `Sandbox Authorization Token` field is populated; the `Anduril-Sandbox-Authorization` header is required for developer sandboxes.
- Session token loss on restart: The CEL input persists the session token in agent state. On cold restart the integration re-delivers a full entity snapshot as `EVENT_TYPE_PREEXISTING` events.

## Performance and scaling

The Lattice long-poll API returns all entities in the environment with no server-side filtering. In large environments with many active entities, the initial snapshot may produce thousands of events across multiple polling cycles. Tune `Batch Size` (default 100, max 2000) and `Interval` (default 5s) for your environment's entity density.

Check notice on line 97 in packages/anduril_lattice/_dev/build/docs/README.md

View workflow job for this annotation

GitHub Actions / Lint user-facing content

Elastic.WordChoice: Consider using 'can, might' instead of 'may', unless the term is in the UI.

For more information on architectures that can be used for scaling this integration, check the [Ingest Architectures](https://www.elastic.co/docs/manage-data/ingest/ingest-reference-architectures) documentation.

## Reference

### Inputs used
{{ inputDocs }}

### API usage

These APIs are used with this integration:

- `POST /api/v1/entities/events` — [Long-poll entity events](https://developer.anduril.com/reference/rest/entities/long-poll-entity-events): returns batches of entity lifecycle events with a session-token cursor for incremental collection.
- `POST /api/v1/oauth/token` — OAuth2 token endpoint: used to obtain and refresh bearer tokens when OAuth2 client credentials are configured.

### Vendor documentation links

- [Anduril Lattice developer portal](https://developer.anduril.com)
- [Lattice API overview](https://developer.anduril.com/reference/overview/overview)
- [Go SDK (public)](https://github.com/anduril/lattice-sdk-go)

### Data streams

#### entity

The `entity` data stream collects entity lifecycle events from the Anduril Lattice long-poll API. Each event represents a state change for a tracked entity in the Lattice Common Operational Picture — creation, update, deletion, or preexisting snapshot on session start.

Entity sub-types (distinguished by `anduril_lattice.entity.ontology.template`):
- `TEMPLATE_TRACK` — sensor-tracked transient contacts (aircraft, vessels, UAVs)
- `TEMPLATE_ASSET` — persistent managed assets (sensors, towers, Anduril hardware)
- `TEMPLATE_GEO` — geospatial zones (keep-in, keep-out, engagement zones)
- `TEMPLATE_SENSOR_POINT_OF_INTEREST` — sensor-detected points of interest
- `TEMPLATE_SIGNAL_OF_INTEREST` — RF/EW signal entities

##### entity fields

{{ fields "entity" }}

##### entity sample event

{{ event "entity" }}

{{ ilm }}

{{ transform }}
14 changes: 14 additions & 0 deletions packages/anduril_lattice/_dev/deploy/docker/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
services:
anduril_lattice:
image: docker.elastic.co/observability/stream:v0.20.0
hostname: anduril_lattice
ports:
- 8090
volumes:
- ./files:/files:ro
environment:
PORT: '8090'
command:
- http-server
- --addr=:8090
- --config=/files/config-entity.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
rules:
# OAuth2 token endpoint - returns access token for OAuth2 auth mode
- path: /api/v1/oauth/token
methods: [POST]
request_headers:
Content-Type:
- "application/json"
responses:
- status_code: 200
headers:
Content-Type:
- "application/json"
body: >-
{"access_token":"test-access-token-xxxx","token_type":"Bearer","expires_in":1800}
# Page 1: new session (empty sessionToken) — returns TEMPLATE_TRACK entity event
# Matches both Python json.dumps (space after colon) and CEL encode_json (no space)
- path: /api/v1/entities/events
methods: [POST]
request_headers:
Content-Type:
- "application/json"
request_body: '/.*"sessionToken":? *"".*/'
responses:
- status_code: 200
headers:
Content-Type:
- "application/json"
body: >-
{"sessionToken":"mock-cursor-page-1","entityEvents":[{"eventType":"EVENT_TYPE_PREEXISTING","time":"2025-12-17T15:32:42Z","entity":{"entityId":"15af4024-05ac-44a3-a756-638e161bd977","description":"Sanctuary","isLive":true,"location":{"position":{"latitudeDegrees":42.35423678341084,"longitudeDegrees":-71.04721146609882,"altitudeHaeMeters":0},"velocityEnu":{"e":4.86,"n":-5.05,"u":-7.67e-07},"speedMps":7.005,"attitudeEnu":{"x":2.14e-08,"y":5.04e-08,"z":-0.391,"w":0.920}},"aliases":{"name":"Sanctuary Passenger Ship"},"milView":{"disposition":"DISPOSITION_ASSUMED_FRIENDLY","environment":"ENVIRONMENT_SURFACE"},"ontology":{"platformType":"SURFACE_VESSEL","template":"TEMPLATE_TRACK"},"provenance":{"integrationName":"legacy_integration","dataType":"ais","sourceUpdateTime":"2025-12-17T15:32:42Z"},"tracked":{"trackQualityWrapper":9,"sensorHits":1},"indicators":{"simulated":true},"signal":{"frequencyCenter":{"frequencyHz":{"value":9.4e9}},"frequencyRange":{"minimumFrequencyHz":{"frequencyHz":{"value":9.0e9}},"maximumFrequencyHz":{"frequencyHz":{"value":9.8e9}}},"bandwidthHz":8.0e8,"signalToNoiseRatio":14.5,"pulseWidthS":2.5e-6,"scanCharacteristics":{"scanType":"SCAN_TYPE_CIRCULAR","scanPeriodS":3.0},"emitterNotations":["EN-001","EN-002"]},"transponderCodes":{"mode3":5421,"modeS":{"id":"ABX1234","address":11189196}},"targetPriority":{"highValueTarget":{"isHighValueTarget":true,"targetPriority":2,"isHighPayoffTarget":false},"threat":{"isThreat":false}},"createdTime":"2025-12-17T14:51:52Z","expiryTime":"2025-12-17T15:32:52Z"}}]}
# Page 2: continuation with page-1 cursor — returns TEMPLATE_ASSET entity event
- path: /api/v1/entities/events
methods: [POST]
request_headers:
Content-Type:
- "application/json"
request_body: '/.*"sessionToken":? *"mock-cursor-page-1".*/'
responses:
- status_code: 200
headers:
Content-Type:
- "application/json"
body: >-
{"sessionToken":"mock-cursor-page-2","entityEvents":[{"eventType":"EVENT_TYPE_UPDATE","time":"2025-12-17T15:30:00Z","entity":{"entityId":"tower-45d9bf21-0001-0001-0001-000000000001","isLive":true,"aliases":{"name":"Tower 45d9bf21"},"milView":{"disposition":"DISPOSITION_FRIENDLY","environment":"ENVIRONMENT_LAND"},"ontology":{"platformType":"FIXED_SENSOR","template":"TEMPLATE_ASSET"},"location":{"position":{"latitudeDegrees":34.05223,"longitudeDegrees":-118.24368,"altitudeHaeMeters":85.0}},"health":{"connectionStatus":"CONNECTION_STATUS_ONLINE","healthStatus":"HEALTH_STATUS_HEALTHY","updateTime":"2025-12-17T15:30:00Z","activeAlerts":[{"alertCode":"ALERT_LOW_STORAGE","level":"ALERT_LEVEL_WARNING","activatedTime":"2025-12-17T15:00:00Z","description":"Storage capacity below 15 percent"}]},"geoShape":{"polygon":{"rings":[{"positions":[{"position":{"latitudeDegrees":34.053,"longitudeDegrees":-118.245},"heightM":0},{"position":{"latitudeDegrees":34.053,"longitudeDegrees":-118.242},"heightM":0},{"position":{"latitudeDegrees":34.051,"longitudeDegrees":-118.242},"heightM":0},{"position":{"latitudeDegrees":34.051,"longitudeDegrees":-118.245},"heightM":0}]}]}},"sensors":{"sensors":[{"sensorId":"sensor-001","operationalState":"OPERATIONAL_STATE_OPERATIONAL","sensorType":"SENSOR_TYPE_EO","sensorDescription":"Electro-optical camera","lastDetectionTimestamp":"2025-12-17T15:29:55Z","fieldsOfView":[{"projectedFrustum":{"upperLeft":{"latitudeDegrees":34.060,"longitudeDegrees":-118.252},"upperRight":{"latitudeDegrees":34.060,"longitudeDegrees":-118.237},"bottomRight":{"latitudeDegrees":34.050,"longitudeDegrees":-118.237},"bottomLeft":{"latitudeDegrees":34.050,"longitudeDegrees":-118.252}}}]}]},"powerState":{"sourceIdToState":{"Battery":{"powerStatus":"POWER_STATUS_DISCHARGING","powerType":"POWER_TYPE_BATTERY","powerLevel":{"percentRemaining":87.5,"voltage":31.84}}}},"provenance":{"integrationName":"anduril_tower","dataType":"anduril","sourceUpdateTime":"2025-12-17T15:30:00Z"},"indicators":{"simulated":true},"createdTime":"2025-12-17T10:00:00Z"}}]}
# Terminal page: cursor-page-2 token — entityEvents is null (no more events)
- path: /api/v1/entities/events
methods: [POST]
request_headers:
Content-Type:
- "application/json"
request_body: '/.*"sessionToken":? *"mock-cursor-page-2".*/'
responses:
- status_code: 200
headers:
Content-Type:
- "application/json"
body: >-
{"sessionToken":"mock-cursor-final","entityEvents":null}
# Second-round resume: same final cursor — returns null (simulates ongoing polling)
# Verifies cursor persistence: program must NOT reset sessionToken to "" between intervals
- path: /api/v1/entities/events
methods: [POST]
request_headers:
Content-Type:
- "application/json"
request_body: '/.*"sessionToken":? *"mock-cursor-final".*/'
responses:
- status_code: 200
headers:
Content-Type:
- "application/json"
body: >-
{"sessionToken":"mock-cursor-final","entityEvents":null}
6 changes: 6 additions & 0 deletions packages/anduril_lattice/changelog.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# newer versions go on top

Check notice on line 1 in packages/anduril_lattice/changelog.yml

View workflow job for this annotation

GitHub Actions / Lint user-facing content

Elastic.Versions: Use 'later versions' instead of 'newer versions' when referring to versions.
- version: "0.1.0"
changes:
- description: Initial release of the Anduril Lattice integration.
type: enhancement
link: https://github.com/elastic/integrations/pull/20021
Loading
Loading