Skip to content
Back to skills

Verify

ASecurity

Drive CrowTelemetry's OTLP ingest end-to-end — boot the real receiver, POST OTLP JSON with curl, inspect the SQLite db.

  • 18 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 3, 2026
ai-agentsgoswiftsqldatabase

Works with

  • cli

Security analysis

A100/100

Scanned September 3, 2026

npx -y skills add corveil/crow --skill verify --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Verify?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Verify
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/corveil-verify/badge)](https://www.skillsdirectory.com/skills/corveil-verify)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: verify
description: Drive CrowTelemetry's OTLP ingest end-to-end — boot the real receiver, POST OTLP JSON with curl, inspect the SQLite db.
---

# Verifying CrowTelemetry changes

The package's runtime surface is `TelemetryService` (`OTLPReceiver` HTTP
listener on localhost + `TelemetryDatabase` SQLite file). There is no
standalone binary — build a throwaway harness executable that depends on this
package and boots the service:

```swift
// Sources/otlp-harness/main.swift — usage: otlp-harness <port> <data-dir>
import Foundation
import CrowTelemetry
let service = try TelemetryService(port: UInt16(CommandLine.arguments[1])!,
                                   dataDirectory: CommandLine.arguments[2]) { id in
    print("[harness] data received for session \(id)")
}
Task { try await service.start() }
dispatchMain()
```

Harness `Package.swift`: swift-tools-version 6.0, `platforms: [.macOS(.v14)]`,
`.package(path: "<repo>/Packages/CrowTelemetry")`. Build with `swift build`,
run in the background, then drive it.

Gotchas:
- Ingest is OTLP **HTTP/JSON only** (`OTEL_EXPORTER_OTLP_PROTOCOL=http/json`),
  paths `/v1/metrics` and `/v1/logs`, POST only. One request per connection
  (the receiver closes it after responding). `Content-Length` and
  `Transfer-Encoding: chunked` both work; a compressed body or a non-JSON
  `Content-Type` is rejected by name.
- `Content-Type: application/json` is **required** on POST — the receiver fails
  closed. Pass it explicitly with curl, which otherwise sends
  `application/x-www-form-urlencoded` for `-d` and gets a 400:

  ```
  curl -X POST http://localhost:<port>/v1/logs \
       -H 'Content-Type: application/json' --data-binary @payload.json
  ```
- The resource must carry `crow.session.id` (a UUID string) in
  `resource.attributes`, or the payload is silently skipped.
- Datapoint values: `asDouble` (number) or `asInt` (string **or** number).
- Sum metrics take `aggregationTemporality` (1=delta, 2=cumulative, or the
  `AGGREGATION_TEMPORALITY_*` name) and `isMonotonic`; cumulative sums are
  normalized to deltas at insert.
- Decoding is deliberately tolerant (#823): scalars are accepted in either
  their number or string form, and a malformed record is skipped with a
  `skipped malformed N logRecords` log line rather than failing the export.
- Event names arrive **bare** in the `event.name` attribute (`user_prompt`);
  they are qualified to `claude_code.user_prompt` at ingest, which is the form
  `countEvents` and `turnAnalytics` match on.
- Decode failures log the `DecodingError` coding path, which names the
  offending field. Set `CROW_TELEMETRY_LOG_RAW_BODIES=1` to also log a
  truncated raw body — off by default because bodies carry account IDs and
  emails. Repeat failures are throttled to one report per minute per path.

Minimal payload:

```json
{"resourceMetrics":[{"resource":{"attributes":[{"key":"crow.session.id","value":{"stringValue":"<UUID>"}}]},
 "scopeMetrics":[{"metrics":[{"name":"claude_code.cost.usage",
   "sum":{"aggregationTemporality":2,"isMonotonic":true,"dataPoints":[{"asDouble":1.0}]}}]}]}]}
```

Observe results with the sqlite3 CLI against `<data-dir>/telemetry.db`:
`SELECT metric_name, value, attributes_json FROM metrics ORDER BY id` and
compare `SUM(value)` per metric to the expected total. Events land in the
`events` table via `/v1/logs`.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.