Skip to content

Offload station files (LP0)

The built-in data source that carries what an offload station's SD card holds (phase 42, decisions D319 to D324). It is not an external platform: a RangerEdge offload station stores the frames trackers offload to it over LP0, the OpenCollar Edge firmware's raw LoRa path, and a person uploads the card's files on the station's device page. Every line of a file is a delivery on this source, addressed by the tracker's LP0 address; every station is a gateway of it. Nothing is configured on the source itself. The guide is Offload stations.

Built from the firmware branch fix/existing-lp0-implementation of SmartParksOrg/smartparks-opencollar-edge-fw (68 commits ahead of 8.0.3, read on 2026-10-15): the LP0 module README, the station's SD writer (lp0_offload.c), the wire format of protocol version 4 and the master plan's importer decision; the frame codec is checked against the firmware's own replay fixture. No card had been read into Protect when this was written.

What the station writes

LOG_N/STATIONEUI_timestamp[_NNN].jsonl, one JSON document per line in the Semtech rxpk shape: gatewayEui (the station), rxpk.time and rxpk.tmst (the reception, tmst in Unix seconds), freq (MHz), datr (SF7BW250), codr, rssi (dBm), lsnr (dB), size and data. data is an ordinary LoRaWAN 1.0 uplink: MHDR 0x40, the tracker's DevAddr, FCtrl, a 16-bit counter (the upper half of the 32-bit counter is zero), port 29, the FRMPayload encrypted with the tracker's AppSKey and a MIC under its NwkSKey. The payload is the flash record stream (port, msg_id, len, data, store time, repeated), at most 228 bytes per frame. Since LP0 v5 a line also carries lp0: {"version": 5, "source_eui": "<tracker EUI>", "session": n, "window": n, "slot": n}, written by the station from its discovery with the tracker; files of earlier firmware have no lp0 object. The card's root may hold sdcheck.bin, the 64-byte file the station's storage test writes since the firmware of 2026-10-20; it is no archive, and the zip upload keeps only the LOG_*/*.jsonl members.

LP0 v5, the model of firmware issue 772

Tim's issue 772 in the firmware repository (2026-10-15) separates the LP0 link's authentication (a deployment master key, per-session keys derived from the discovery by an SP 800-108 CMAC KDF) from the payload's encryption (each collar's own unique DevAddr, NwkSKey and AppSKey, under LoRaWAN counter and MIC semantics), makes the station an opaque store that keeps routing metadata and no device key, and has the backend decrypt per device. LP0 v5 (the branch's docs/reviews/lp0-master-key-wire.md, 2026-10-16) implements it: the collar calls lorawan_tools_build with its own lp0_* credentials and the next value of its wrapping 16-bit counter (passed as a 32-bit counter with the upper half zero), the station authenticates the transport with the master key, reassembles the inner frame and stores it unchanged, and writes the lp0 object. This source was built for that model and reads v5 as it read the frames before: the keys per device, the decryption in the pipeline, the station a gateway. What v5 added (decision D327): the lp0 object is read (lp0_version, source_eui, lp0_session, lp0_window, lp0_slot in the provider metadata), the source EUI names an unknown identity on Needs attention and is cross-checked against the EUI of the device the address belongs to (a contradiction is DEVICE_IDENTITY_AMBIGUOUS, the frame decoded onto nobody), and a metadata version other than 5 is refused by name (METADATA_VERSIONS), a missing object being an older file. The master key is never stored in Protect; nothing here needs it. The 16-bit counter's wrap under unchanged keys is the firmware's limit: Protect keeps last_f_cnt and f_cnt32, and tries the upper half zero first, as the firmware signs.

Identities and keys

The tracker's LP0 address is its identity on this source (identity_type dev_addr, the address as eight uppercase hex digits). It is written when the address and keys are set on the device (PUT /devices/{id}/lp0-credentials); a frame from an address no device has makes an unknown identity on Needs attention. The two keys are the device's, encrypted at rest with the server's credentials key and returned to callers who may set them (device control in the project, or a server admin), so they can be read back and changed; a viewer sees only that they are set. There is no shared network key in Protect: a network whose devices share one pair is every device holding the same pair.

Stations

The first frame of a card makes the station a gateway of this source, named after the device whose card it is and linked to it (gateways.device_id), with kind: offload_station in its attributes; "Make an offload station" on the project's Offload page makes that row earlier, with nothing seen yet, so the station is listed before its first card. Every frame is a reception of that gateway with the line's RSSI, SNR and frequency, so the Gateways page, the coverage layer and the trackers' Connectivity tabs read it as they read a LoRaWAN gateway. A station's place is set by hand on its device page; the gateway's location can be set by hand too (Gateways page).

Processing

The file worker splits the file, stores every line as a source event with the station as the gateway and the reception metadata, and decodes it at once. The decryption is a step of the decoder pipeline, not of the file worker: a channel lp0 event without a decrypted frame is unwrapped with the device's keys before the driver runs and the result kept in its metadata, so a walk over retained frames after an identity is linked and "Decode again" use the same step. A frame of a device without keys fails with CREDENTIALS_MISSING; one that does not verify with PAYLOAD_DECODE_FAILED; a frame with the same address, counter and bytes as one of the last 30 days is a repeat and stored as a duplicate of the first. A reception time before LP0_EARLIEST_RECEPTION or after the upload is not believed as the delivery time.

Troubleshooting

Seen Meaning What to do
The upload answers "Not a station file" The file is not one JSON document per line, nor a zip of LOG_* folders Upload the .jsonl files or a zip of the card's folders; a raw log .txt goes through "Upload raw log" as before
A member "uploaded before" (duplicate on the answer's skipped row, with the earlier upload's time and row) The same bytes were uploaded for this station; the file was left out and nothing was decoded twice. An upload whose every file was uploaded before answers 201 with no files, not an error Nothing; the earlier row holds the frames. The page says "Nothing new" or how many files were left out
Frames "of trackers not known yet" Addresses no device has Needs attention: link each address to its device, then set the keys on the device
Frames "of a tracker without keys" The device is known, its keys are not Set the keys on the device, then "Decode again" on the file
Frames "that did not verify" A wrong key, or a frame of another device under this address Check the keys against the device's provisioning; the frames stay failed, never guessed
Every record of a card invalid as "ahead of its delivery" A station clock far behind Should not happen: a time before 2024 is not believed; report it with the file's trace