Files
tcp-ingestion/.planning/phase-1-telemetry/README.md
T
julian c8a5f4cd68 Add Phase 1 and Phase 2 planning documents
ROADMAP plus granular task files per phase. Phase 1 (12 tasks + 1.13
device authority) covers Codec 8/8E/16 telemetry ingestion; Phase 2
(6 tasks) covers Codec 12/14 outbound commands; Phase 3 enumerates
deferred items.
2026-04-30 15:50:49 +02:00

103 lines
4.7 KiB
Markdown

# Phase 1 — Inbound telemetry
Implement a Node.js TCP server that ingests Teltonika telemetry over codecs 8, 8E, and 16; publishes normalized `Position` records to a Redis Stream; and ships with the operational baseline (Prometheus metrics, fixture-based tests, Dockerfile, Gitea CI/CD pipeline).
## Outcome statement
When Phase 1 is done:
- Devices in the deployed FMB/FMC/FMM/FMU fleet connect to a known TCP port, complete the IMEI handshake, and stream AVL frames.
- Every well-formed AVL record produces exactly one `Position` JSON entry on the `telemetry:teltonika` Redis Stream, with all GPS fields and IO element bag intact.
- CRC-mismatched frames are dropped (no ACK) so devices retransmit.
- Unknown-codec frames cause the connection to close with a structured `WARN` log entry; a Prometheus counter increments.
- **Device authority is observable but permissive by default** — every handshake is labeled `known` or `unknown` based on a configurable `DeviceAuthority`; the Phase 1 default `AllowAllAuthority` accepts everything, and an opt-in `RedisAllowListAuthority` (task 1.13) reads a Directus-published allow-list from Redis. Strict reject-on-unknown is gated behind a `STRICT_DEVICE_AUTH` flag.
- The service builds reproducibly via a Gitea Actions workflow, publishing a Docker image to the project's Gitea Container Registry, tagged by branch + git SHA.
- Tests cover every codec parser using hex captures sourced from the canonical Teltonika doc, with at least one synthetic edge-case fixture per codec.
## Sequencing
```
1.1 Project scaffold
├─→ 1.2 Core shell & framing types
│ ├─→ 1.3 Configuration & logging
│ ├─→ 1.4 Teltonika framing layer (incl. DeviceAuthority seam)
│ │ ├─→ 1.5 Codec 8 parser
│ │ ├─→ 1.6 Codec 8 Extended parser
│ │ └─→ 1.7 Codec 16 parser
│ └─→ 1.8 Redis publisher & main wiring
│ └─→ 1.10 Observability
│ ├─→ 1.11 Dockerfile & CI
│ │ └─→ 1.12 Production hardening
│ └─→ 1.13 Device authority (opt-in, deferrable)
└─→ 1.9 Fixture suite (cross-cutting; established alongside 1.5)
```
Tasks 1.5, 1.6, 1.7 can be done in parallel after 1.4 lands. Task 1.9 (fixture infrastructure) should land *with or before* 1.5 — it's the framework the codec tasks add to. Task 1.13 is the only Phase 1 task that can ship *after* the rest of Phase 1 is in production — `AllowAllAuthority` is functional from day one; the Redis allow-list lights up once the Directus-side publisher exists.
## Files modified
Phase 1 produces this layout in `tcp-ingestion/`:
```
tcp-ingestion/
├── .gitea/workflows/build.yml
├── src/
│ ├── core/
│ │ ├── types.ts
│ │ ├── publish.ts
│ │ ├── registry.ts
│ │ ├── session.ts
│ │ └── server.ts
│ ├── adapters/
│ │ └── teltonika/
│ │ ├── index.ts
│ │ ├── handshake.ts
│ │ ├── frame.ts
│ │ ├── crc.ts
│ │ ├── device-authority.ts (interface + AllowAllAuthority)
│ │ ├── redis-allow-list-authority.ts (task 1.13, opt-in)
│ │ └── codec/
│ │ ├── data/
│ │ │ ├── codec8.ts
│ │ │ ├── codec8e.ts
│ │ │ └── codec16.ts
│ │ └── command/ (empty in Phase 1)
│ ├── config/load.ts
│ ├── observability/
│ │ ├── logger.ts
│ │ └── metrics.ts
│ └── main.ts
├── test/
│ ├── fixtures/teltonika/
│ │ ├── codec8/
│ │ ├── codec8e/
│ │ └── codec16/
│ ├── codec8.test.ts
│ ├── codec8e.test.ts
│ ├── codec16.test.ts
│ ├── crc.test.ts
│ └── frame.test.ts
├── Dockerfile
├── package.json
├── pnpm-lock.yaml
├── tsconfig.json
├── .dockerignore
├── .gitignore
├── .prettierrc
├── eslint.config.js
└── README.md
```
## Tech stack (decided)
- **Node.js 22 LTS**, ESM-only.
- **TypeScript 5.x** with `strict: true`.
- **pnpm** for dependency management (deterministic, fast, easy to add workspaces later if needed).
- **vitest** for tests.
- **pino** for structured logging.
- **prom-client** for Prometheus metrics.
- **ioredis** for Redis Streams.
- **zod** for environment-variable validation.
If an implementer wants to deviate, they must update the relevant task file first.