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.
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
PositionJSON entry on thetelemetry:teltonikaRedis 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
WARNlog entry; a Prometheus counter increments. - Device authority is observable but permissive by default — every handshake is labeled
knownorunknownbased on a configurableDeviceAuthority; the Phase 1 defaultAllowAllAuthorityaccepts everything, and an opt-inRedisAllowListAuthority(task 1.13) reads a Directus-published allow-list from Redis. Strict reject-on-unknown is gated behind aSTRICT_DEVICE_AUTHflag. - 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.