feat(S2-a): ingest durable app MQTT points securely

This commit is contained in:
Brent Perteet
2026-08-20 15:22:40 -05:00
parent daa3407b9d
commit 8bcc12ec96
14 changed files with 979 additions and 59 deletions

View File

@@ -0,0 +1,21 @@
-- App-path ingest idempotency + provenance (Sprint 2, S2-a; SRS-SYN-2/7, §3.4.2).
-- Adds the UUIDv7 idempotency key and forward-compat provenance tags to locate_points.
-- CreateEnum
CREATE TYPE "PointOrigin" AS ENUM ('APP', 'LOCATOR', 'MAGLINK');
-- CreateEnum
CREATE TYPE "PointUploadPath" AS ENUM ('APP_MQTT', 'DEVICE_MQTT', 'REST_BATCH');
-- AlterTable
ALTER TABLE "locate_points" ADD COLUMN "pointId" TEXT,
ADD COLUMN "origin" "PointOrigin" NOT NULL DEFAULT 'LOCATOR',
ADD COLUMN "uploadPath" "PointUploadPath",
ADD COLUMN "originClientId" TEXT,
ADD COLUMN "createdAt" TIMESTAMPTZ(6);
-- CreateIndex
CREATE UNIQUE INDEX "locate_points_pointId_key" ON "locate_points"("pointId");
-- CreateIndex
CREATE INDEX "locate_points_jobId_createdAt_idx" ON "locate_points"("jobId", "createdAt");

View File

@@ -53,6 +53,22 @@ enum LocateMode {
SONDE
}
// Which producer a point originated from (telemetry-schema.md `origin`). Tagged now
// for SYN-6 path arbitration; no arbitration LOGIC runs yet (deferred, sprint-2 scope).
enum PointOrigin {
APP
LOCATOR
MAGLINK
}
// Which transport carried the point to the cloud (telemetry-schema.md `uploadPath`).
// Same forward-compat tagging as PointOrigin.
enum PointUploadPath {
APP_MQTT
DEVICE_MQTT
REST_BATCH
}
model Organization {
id String @id @default(cuid())
name String
@@ -185,6 +201,25 @@ model LocatePoint {
id BigInt @id @default(autoincrement())
jobId String
deviceId String?
// Client-generated UUIDv7, the idempotency key across every upload path
// (app MQTT, device-direct MQTT, REST batch — SRS §3.4.2 / telemetry-schema.md).
// A replayed UUID is ingested exactly once (unique constraint below). Nullable:
// the legacy device-direct path (LogIngest/PointsIngest) predates it and does not
// supply one yet; the app path (SYN-2) always does.
pointId String? @unique
// Provenance tags (telemetry-schema.md). Set on the app path now; carried for
// forward-compat with SYN-6 arbitration, which does not run yet.
origin PointOrigin @default(LOCATOR)
uploadPath PointUploadPath?
originClientId String?
// Event time asserted by the producer (record creation, telemetry-schema.md
// `createdAt`). Ingest orders and dedups by this, NOT by arrival (`receivedAt`),
// so replays and out-of-order delivery converge to the same stored ordering.
createdAt DateTime? @db.Timestamptz(6)
lat Decimal @db.Decimal(10, 8)
lng Decimal @db.Decimal(11, 8)
altitude Decimal? @db.Decimal(8, 3)
@@ -217,7 +252,11 @@ model LocatePoint {
job Job @relation(fields: [jobId], references: [id], onDelete: Cascade)
device Device? @relation(fields: [deviceId], references: [id], onDelete: SetNull)
// (jobId, recordedAt) serves GPS-time reads; (jobId, createdAt) serves the
// event-time ordering the app path and portal read use (SRS-SYN-2). pointId is
// already uniquely indexed above — that index also backs the dedup lookup.
@@index([jobId, recordedAt])
@@index([jobId, createdAt])
@@map("locate_points")
}