# Blue Cell telemetry and evidence design

Status: initial OSS design, 2026-10-04. The implementation in this repository is a local web sensor. The production source adapters and Red/Blue learning loop below are design targets, not shipped features.

## Observation contract

Blue Cell needs evidence generated by the target, independent of Red's transcript. The current path is:

```text
local web target
  ├─ requests routed through Caddy → access record + request body spool
  └─ readable log files or capture_process.py → Fluent Bit tail
       → Blue receiver (raw + normalized event, SQLite)
       → resident monitor → Blue agent read tools → CLI notification
```

The receiver gives each event an `event_id`, `target_id`, `schema_version`, `signal_type`, `event_type`, `source`, source file and offset, observed time, and occurrence time where available. Structured application logs also retain the full `raw_record` and all parsed `attributes`. Common severity, trace, span, and request identifiers are copied into top-level fields for correlation. Application-provided `source`, `target_id`, and event ID never become collector authority; a process wrapper's ID is retained as `source_event_id`. File log IDs are stable for an exact delivered record; after a crash, rereading the same file line with a new collector timestamp may produce a duplicate. This is preferable to silently deduplicating a repeated line at the same path and offset after log rotation. This is an internal versioned envelope; it is not a claim of full OCSF or OpenTelemetry compliance.

For target logs, the raw line is the audit source. Extracted fields are indexes and investigation hints, not replacements for it. Proxy access records are currently stored as normalized receiver fields; the original Caddy log is only available in its rolling local volume. This follows the distinction between original records, event time, and observed time in the [OpenTelemetry log data model](https://opentelemetry.io/docs/specs/otel/logs/data-model/) and its [general log attributes](https://opentelemetry.io/docs/specs/semconv/general/logs/). Later OTLP, syslog, Docker, host, and cloud adapters should map into this envelope while preserving source-specific fields and provenance.

## Current coverage and missing evidence

| Surface | Current source | Remaining gap |
| --- | --- | --- |
| HTTP ingress | Caddy access metadata and request body bytes read by the target | Direct connections to the original port, unproxied TLS, and response bodies are unseen. |
| Application logs | `/blue up ... --logs /absolute/host/directory` enrolls a read-only local directory; optional wrapper captures process stdout/stderr and emits start/exit lifecycle records with a reported PID and exit code. `/blue verify` distinguishes file opening from record delivery in the current collector run, and `/blue sources` lists files with retained events. | A running process is not attached automatically. Logs outside the mount, nested paths, and events the app never emits are unseen. Old file replay can verify transport but not fresh target activity. |
| Runtime and host | None by default | Process execution, file access, container events, and host audit need separate sensors and permissions. |
| Database, identity, cloud | None by default | Query/audit logs and external control-plane events need their own source adapters. |
| Collector health | Receiver exposes per-input file and record counts plus Fluent Bit intake, output, skipped-line, dropped-record, retry-failure, and paused-input counters. Accepted, rejected, and evicted counts persist across receiver restarts; the monitor notices collector restarts, receiver state replacement, known loss, and prolonged outage. | Counters reveal known loss, but zero counters cannot prove full coverage. An outage interval cannot be reconstructed from counters alone. Idle sources cannot be distinguished from silent target failure without an expected heartbeat. |

Fluent Bit's [Tail input](https://docs.fluentbit.io/manual/4.2/data-pipeline/inputs/tail) explicitly skips oversized lines when `skip_long_lines` is enabled. Its [filesystem output limit](https://docs.fluentbit.io/manual/4.2/data-pipeline/buffering) can evict oldest chunks under sustained backpressure. The receiver therefore exposes the documented [Fluent Bit loss and health metrics](https://docs.fluentbit.io/manual/4.2/administration/monitoring) through `/metrics`, and Blue's read tool includes them in `blue_sensor_scan`. A counter increase means an evidence gap; it must not be interpreted as a clean target.

## AI SOC comparison

[Google SecOps TIN](https://docs.cloud.google.com/chronicle/docs/secops/triage-investigation-agent) investigates only ingested SIEM data and uses search, enrichment, and process context to support an alert verdict. The [MLSys 2026 ADR system](https://proceedings.mlsys.org/paper_files/paper/2026/hash/f03cb785864596fa5901f1359d23fd81-Abstract-Conference.html) separates sensor telemetry from fast triage and deeper contextual reasoning, although its sensor targets AI-agent activity rather than general web-service events. These references support the order used here: establish source coverage and provenance first, then add case-oriented search and agent reasoning, then evaluate response actions. Blue already has continuous rule leads, bounded AI watch windows, indexed exact search for request ID, trace ID, and trusted source, and a one-hour receiver-time timeline query. It does not have process trees, arbitrary cross-source entity search, or a general case evidence index.

## Local to production evolution

1. **OSS local web:** keep the proxy and file tail as the no-SIEM default. The CLI accepts a read-only host log directory; `/blue status`, `/blue verify`, and `/blue sources` expose enrollment and observed records. Add a host-side owned-process enrollment command and verify each source with a fresh real target event. A user must route traffic through the proxy for ingress coverage.
2. **Additional local sensors:** attach Docker logs/events or a process/host sensor only after the user selects that source and the required host permission is available. Give each adapter a stable source ID, cursor, heartbeat, and drop counter. Preserve raw records and map them to the same envelope. Do not infer that absent logs mean no attack.
3. **Production:** replace local bind mounts with authenticated OTLP, syslog, SIEM, or cloud connectors feeding the same ingest contract through a durable queue. Separate tenant/target identities, enforce source authentication, track collector-to-store lag, and store raw evidence with retention and access controls. The Blue agent should read a case-oriented evidence API rather than depend on one collector technology.
4. **Closed loop:** tie a Red attempt and Blue observation to a shared engagement/run ID and target clock window. Record Red's attempted action, independent target-side evidence, Blue's detection decision, confidence, and response outcome. Feed only verified outcomes into evaluation and later improvement. A missing observation is a coverage failure, not a Blue false negative; an HTTP 403 is not proof that every backend side effect was blocked.

A native-process test on 2026-10-04 connected the actual Caddy 2.11.6 body-tap proxy, Fluent Bit 5.1.3, fixture web service, receiver, and monitor. The test measured 60 ms from a traversal request to correlated proxy and target-log delivery, and 543 ms to the rule incident. It retrieved and compared the complete 9 MiB request body. During a deliberate collector outage, new proxy and target records remained absent from the receiver, then arrived after collector restart; the monitor reported the outage, recovery, restart, and incident. An oversized target-log line raised Fluent Bit's skipped-line counter and a collection-gap notification. Native execution set `BLUE_PROXY_LOG_PREFIX` to the host log path and `BLUE_BODY_SPOOL_DIR` to the host body directory; Docker retains its built-in paths. The Docker daemon on this workstation remained unavailable, so Compose startup, interactive CLI enrollment, and a live model-backed watch were not exercised in this test. Those are the next integration gate. An enrolled source with no arriving record remains unverified; model quality and automated response require separate evaluation.

The default Blue agent exposes only its five sensor read tools. A caller can explicitly supply a different tool list when building a graph, but the shipped graph does not inherit additive plugin tools. This keeps the initial observe-and-report authority separate from later response actions.
