elastic/integration-skillsApache-2.021 files

Cel Programs

Use for all CEL and mito work on integrations that collect from APIs — writing CEL programs, cel.yml.hbs templates, manifest configuration, mock-first development with the mito CLI, system test mock setup, and answering CEL/mito questions. Load this skill whenever any data stream uses the cel input type.

Specification
Skill ID
elastic/integration-skills/cel-programs
Publisher
elastic
Repository
integration-skills
Installs
0
Files
21
License
Apache-2.0
Synced
Sep 23, 2026
How to use it

Open any RiverX project, open the Skills panel in the chat, and search for this identifier. The files are fetched from the source repository at install time.

elastic/integration-skills/cel-programsInstalls these files
  • SKILL.md
  • references/builder-subagent-guidance.md
  • references/cel-auth-patterns.md
  • references/cel-code-style.md
  • references/cel-complexity-baselines.md
  • references/cel-expression.md
  • references/cel-function-reference.md
  • references/cel-idioms.md
  • references/cel-incremental-build.md
  • references/cel-pagination-patterns.md
  • references/cel-polymorphic-patterns.md
  • references/cel-rate-limiting.md
  • references/cel-system-tests.md
  • references/cel-taxonomy.md
  • references/cel-template-examples.md
  • references/expression-builder-subagent-guidance.md
  • references/mito-reference.md
  • references/reviewer-subagent-guidance.md
  • references/tests/map_drop.txt
  • references/tests/map_merge.txt
  • references/tests/map_shallow_merge.txt

What this skill tells the agent

cel-programs

When to use

Use this skill when tasks include:

  • creating or editing cel.yml.hbs agent stream templates
  • configuring data stream manifests for the cel input type
  • writing CEL programs with pagination, cursor management, or authentication
  • testing or debugging a CEL program locally with mito
  • setting up system tests with mock APIs for CEL-based data streams
  • prototyping a new CEL-based data stream's collection logic
  • any CEL or mito question, regardless of context

When not to use

Do not use this skill as the primary guide for:

  • ingest pipeline processor design (ingest-pipelines)
  • ECS field mapping (ecs-field-mappings)
  • package scaffolding (create-integration)
  • system test execution with the Elastic stack (integration-testing → references/system-testing.md)

Mandatory workflow — mock → mito → template

This is not a suggestion. Every CEL program MUST be developed in this order. The subagent must not write cel.yml.hbs until the CEL program has been validated with mito against a running mock. Skipping steps or reordering causes failures that are hard to debug.

Do NOT write more than ~10–15 new lines of CEL before running mito. Build the program incrementally in phases (skeleton → error handling → event mapping → pagination → cursor guard), validating with mito after each phase. Writing a large program in one shot leads to cascading compilation errors that are extremely hard to debug. Follow the phased approach in references/cel-incremental-build.md.

StepActionOutput
1. Create the system test mockWrite the elastic/stream config at _dev/deploy/docker/files/config-<stream>.yml with rules matching all API endpoints. Write test-default-config.yml.Mock config file, docker-compose service, test config
2. Start the mock locallystream http-server --addr=:8090 --config=...Running mock at http://localhost:8090
3. Create a plain `.cel` file and `state.json`Write the CEL program as a standalone .cel file. Create state.json with the same keys the future state: block will contain, but with literal test values instead of Handlebars. Point url at the local mock.program.cel, state.json in /tmp or working dir
4. Run mito and iterateBuild incrementally per references/cel-incremental-build.md: Phase 0 skeleton → Phase 1 error handling → Phase 2 events → Phase 3 pagination → Phase 4 cursor. Run mito -data state.json -log_requests program.cel after each phase. Do not proceed until mito output is correct.Validated CEL program
5. ONLY THEN write `cel.yml.hbs`Copy the working CEL expression into `program: \ in the Handlebars template. Replace literal test values with {{var}}` references. Configure manifests.Final integration template

Step 3 detail — translating template vars to mito state: When the future cel.yml.hbs will have a state: block like api_key: {{api_key}} and batch_size: {{batch_size}}, the state.json for mito testing uses the same key names with literal test values:

{
  "url": "http://localhost:8090",
  "api_key": "test-key",
  "batch_size": 50,
  "initial_interval": "24h"
}

This mirrors the runtime state the CEL input would provide. Add cursor to test subsequent-run behavior.

For the full mock-first workflow details, CLI flags, execution model, and quality standards: load references/mito-reference.md.


cel.yml.hbs template anatomy

The cel.yml.hbs file at data_stream/<stream>/agent/stream/cel.yml.hbs is a Handlebars template that renders the final CEL input configuration. It has these sections in order:

interval: {{interval}}
resource.tracer:
  enabled: {{enable_request_tracer}}
  filename: "../../logs/cel/http-request-trace-*.ndjson"
  maxbackups: 5
{{#if proxy_url}}
resource.proxy_url: {{proxy_url}}
{{/if}}
{{#if ssl}}
resource.ssl: {{ssl}}
{{/if}}
{{#if http_client_timeout}}
resource.timeout: {{http_client_timeout}}
{{/if}}
resource.url: <constructed from vars>
state:
  <credentials and pagination config from vars>
redact:
  fields:
    - <sensitive state keys>
max_executions: <number, for heavy pagination>
program: |
  <CEL expression>
tags:
{{#if preserve_original_event}}
  - preserve_original_event
{{/if}}
{{#each tags as |tag|}}
  - {{tag}}
{{/each}}
{{#contains "forwarded" tags}}
publisher_pipeline.disable_host: true
{{/contains}}
{{#if processors}}
processors:
{{processors}}
{{/if}}

Handlebars patterns

PatternPurpose
{{var_name}}Direct variable substitution
{{#if var_name}}...{{/if}}Conditional block for optional config
`{{#each tags as \tag\}}`Iteration over list vars
{{#contains "forwarded" tags}}Check if list contains value

Key template fields

  • resource.url — base URL, often constructed from multiple vars (e.g., {{url}}/api/v1/endpoint)
  • resource.headers (ga 8.18.1) — static headers the same for every request (Content-Type, Accept, API version headers). Set here rather than in-program when headers never vary. Applied before auth headers.
  • state: — block where manifest vars are injected as CEL state; credentials and pagination settings go here
  • redact.fields — list state keys containing secrets to redact from debug logs
  • max_executions — override default 1000 for integrations with heavy pagination (e.g., 5000)
  • program: | — the CEL expression; must be a YAML literal block scalar

Do NOT set data_stream.dataset in integration packages

Integration packages (type: integration) must never include data_stream.dataset in cel.yml.hbs or define a data_stream.dataset manifest var. The framework automatically routes documents to the correct data stream. Setting data_stream.dataset overrides this routing and causes documents to land in the wrong index — typically resulting in "0 hits" during system tests.

Only input-type packages (type: input) use data_stream.dataset because they have no predefined data streams.

Data stream manifest configuration

The data stream manifest.yml defines the CEL input stream and its variables.

Standard vars every CEL stream should include

VarTypePurpose
urltextAPI base URL
intervaltextPolling interval (e.g., 5m)
initial_intervaltextLookback window on first run (e.g., 24h)
enable_request_tracerboolEnable HTTP request tracing
http_client_timeouttextRequest timeout (e.g., 30s)
proxy_urltextHTTP proxy URL
sslyamlTLS configuration
tagstext (multi)Event tags
preserve_original_eventboolKeep original event
processorsyamlBeat processors

Auth-specific vars depend on the API (API key, OAuth client_id/secret/token_url, bearer token, etc.).

Declare enable_request_tracer in the data stream manifest, not at the input level. Input-level tracing enables logging for all data streams in the policy.

Package-level vs data-stream-level vars

  • Package-level vars in the root manifest.yml under policy_templates[].inputs[].vars: shared across streams (e.g., url, auth credentials)
  • Data-stream-level vars in data_stream/<stream>/manifest.yml under streams[].vars: stream-specific (e.g., interval, batch_size, initial_interval)

Scope of the CEL program

The CEL program's responsibility is data collection only:

  1. Fetch data from the API endpoint(s)
  2. Handle pagination — walk through all pages within a single polling cycle
  3. Manage cursor state — store timestamps or page tokens in cursor so the next polling interval resumes where the last one left off, avoiding re-collection of already-fetched events
  4. Emit raw events — output {"message": e.encode_json()} for each record

The CEL program does not handle:

Use this skill in a project

Open any RiverX project, search the Skills panel for elastic/integration-skills/cel-programs, and install it in one click.

Use this skill