c8a5f4cd68
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.
103 lines
4.7 KiB
Markdown
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.
|