mirror of
https://github.com/vicliu624/trail-mate.git
synced 2026-10-23 13:33:03 +00:00
291 lines
18 KiB
Markdown
291 lines
18 KiB
Markdown
# Geocaching client runtime
|
|
|
|
## Current index generations
|
|
|
|
The shard-head codec and readers support immutable current-only generations.
|
|
The existing 52-byte shard head uses byte 46 as a CRC-covered format flag:
|
|
`0` selects the legacy append prefix; `1` selects a current-only generation at
|
|
`<bucket>.gci.c<sequence as 16 lowercase hex digits>`. Byte 47 remains reserved.
|
|
Entries retain the existing 152-byte encoding and verified journal/checkpoint
|
|
locators. A current generation contains each live key once, in nondecreasing
|
|
record-sequence order, without tombstones. Its head sequence identifies the
|
|
generation and can exceed the sequence of its retained records after deletion.
|
|
|
|
Readers select the head visible to their committed root, read only that generation,
|
|
and do not consult obsolete append entries. Empty and shrinking generations are
|
|
valid. A nonempty generation must have exactly the length in its head; malformed
|
|
references and CRC failures are still rejected. Legacy heads and readers retain
|
|
their existing checks.
|
|
|
|
The production transaction writer maintains current-only object, cache-head,
|
|
and draft shards (tables 1, 2, and 4). It folds all mutations for the same bucket in a transaction into one
|
|
replacement, verifies the flushed file by read-back CRC, then publishes the
|
|
shard head through the existing root commit. Failed verification leaves the
|
|
parent head unchanged and requires journal recovery. The bounded writer and its
|
|
optional legacy lookup are allocated in PSRAM only; neither retains a complete
|
|
shard or GPX payload.
|
|
|
|
Checkpoint import and rotation preserve this format for tables 1, 2, and 4.
|
|
They write checkpoint references to the sequence-named generation before
|
|
publishing its head. The checkpoint reader enforces strict key order across
|
|
pages; the appender checks the previous entry to maintain that order within each
|
|
bucket, rather than searching the whole bucket for every imported row. Journal
|
|
appends retain their existing replay behavior. Reference checks, checkpoint
|
|
digest validation, and replacement equivalence still precede root publication.
|
|
|
|
Updating a legacy object, cache-head, or draft bucket converts that bucket during
|
|
the write. The existing recovery audit also detects untouched legacy buckets in
|
|
these tables. After a writable operation completes, idle maintenance uses the
|
|
existing checkpoint rotation to convert them without waiting for the ordinary
|
|
256-record interval. Foreground work takes priority; a pinned checkpoint slot
|
|
defers another attempt until the root changes. No separate migration scan or
|
|
service is created. Current-format local lists remain read-only. Downloaded
|
|
detects legacy cache-head buckets or missing object display fields and requests
|
|
one bounded, transactional compatibility upgrade before retrying that page.
|
|
The remaining tables retain append shards. Exclusive index-slot
|
|
cleanup recognizes generation filenames; older generations remain until their
|
|
retired index slot is cleaned.
|
|
|
|
Local lists and the standalone Map can open a CRC-checked committed index root
|
|
before journal inventory or suffix replay. The read lease is not a write-ready
|
|
snapshot: saving, downloading and publishing still require full recovery and
|
|
reference validation. An interrupted operation that has not reached the index
|
|
root may therefore remain absent from local views until recovery runs. The
|
|
existing repair floor and archive checks still apply, and an unreadable referenced
|
|
record triggers recovery rather than being silently accepted.
|
|
|
|
A read-only Downloaded session does not create the draft/publication storage
|
|
adapter. The storage worker creates it when the draft list or draft map section
|
|
is requested, or when full recovery prepares a write-capable session. Network
|
|
query and dispatch services retain their separate on-demand activation.
|
|
|
|
Old saved objects without display metadata are upgraded once, using their
|
|
retained signed response and committed Installed record. The upgrade admits
|
|
writes through full suffix recovery and reference validation, authenticates the
|
|
exact cache ID and revision, preserves retention fields, and rewrites only its
|
|
object projection and unchanged cache head. It does not open GPX files, change
|
|
installation generations or cleanup proofs, or start network services. Failure
|
|
leaves the old data intact and reports an error instead of retrying forever.
|
|
Subsequent Downloaded pages read only current heads and object projections;
|
|
full details still verify the retained signed record. An operation-scoped volume
|
|
session avoids repeated format reads while checking media/USB transitions on
|
|
every read; CRC and reference checks are unchanged.
|
|
|
|
The native runtime regressions simulate 25 ms per SD I/O and 38,904 bytes of
|
|
internal free heap with PSRAM available. The oldest saved-object upgrade finishes
|
|
in 9,660 simulated milliseconds; reopening its Downloaded page takes 1,225 ms.
|
|
The tests also cover already projected legacy buckets, an object-only legacy
|
|
bucket, and a corrupted author signature. They assert unchanged edited GPX,
|
|
unchanged installation/cleanup metadata, no GPX reads during upgrade, no history
|
|
reads or writes after reopening, offline full details, and released PSRAM.
|
|
These timings are regression evidence, not hardware measurements.
|
|
|
|
## Archive a published cache
|
|
|
|
Foreground publication and download dispatch use their already persisted
|
|
48-byte request key. The dispatcher reserves and reloads that request directly
|
|
instead of selecting it through unrelated attempt-expiration and task-history
|
|
scans. Durable intent, cancellation and current identity checks still run before
|
|
transport submission. A request already submitted is not reserved again while
|
|
its client waits for the directory response. The reply budget starts after
|
|
transport submission; durable preparation has a separate bounded wait. First
|
|
responses matching current work go directly to validation and result commit.
|
|
Historical receipt lookup is reserved for old or duplicate requests.
|
|
|
|
LXMF resource advertisements use MessagePack text field names, matching Python
|
|
Reticulum. Binary field names cause the reference parser to reject the
|
|
advertisement and close the link before receiving the publication body. Native
|
|
wire vectors and pinned Python decoder checks cover this interoperability rule.
|
|
|
|
Open the cache in **My caches**, save any edits, and choose **Archive cache**.
|
|
The device first saves an archived draft, then asks for confirmation before
|
|
publishing the next signed revision with the original author identity.
|
|
The local copy remains available. Saving the draft alone does not withdraw
|
|
the public cache: the list shows **Archive not confirmed** until the directory
|
|
accepts that revision. Reopen it and choose **Confirm archive** to retry an interrupted
|
|
publication. **Delete local cache** only removes this device's copy.
|
|
|
|
The standalone Map excludes an archived local draft as soon as the archived
|
|
state is saved, including while directory confirmation is pending. The record
|
|
remains in **My caches** so publication can be retried or its local copy deleted.
|
|
|
|
Archival is a terminal record state. A directory receiving the signed revision
|
|
can exclude it from active results; it does not erase signed history or copies
|
|
held by other devices and independent directories.
|
|
|
|
Publication event logs distinguish transport submission from a persisted
|
|
directory acknowledgement:
|
|
|
|
```text
|
|
[Geocaching][Publication] submitted cache_prefix=15058ac4 revision=3 state=2
|
|
[Geocaching][Publication] confirmed cache_prefix=15058ac4 revision=3 state=2
|
|
```
|
|
|
|
`cache_prefix` is the first four bytes of the cache ID, not its name or complete
|
|
ID. State `2` is Archived. Only `confirmed` proves that the matching directory
|
|
response was accepted and its result committed; `submitted` and generic Wi-Fi
|
|
TX success alone do not prove archival. These are event logs, not periodic polls.
|
|
|
|
Device discovery and browsing use a disposable RAM session. Opening Discover,
|
|
checking directory capabilities, querying, refreshing and paging do not open SD
|
|
storage or recover previous queries. A response is displayed after the existing
|
|
transport authentication and query validation; no journal commit is required.
|
|
|
|
`LiveQueryPort` owns one request, one page and two response fingerprints. Its
|
|
object size is capped at 3 KiB, excluding the query client and transport stack.
|
|
An accepted transport submission retries the same request after 15 seconds;
|
|
a rejected submission retries after one second. The query client's existing
|
|
120-second deadline cancels further sends. Closing abandons the RAM request.
|
|
Exact accepted duplicates can be acknowledged without disk access.
|
|
|
|
Opening an item requests its exact signed revision and shows the full description
|
|
and hint after author verification. Only the open detail owns these text buffers;
|
|
closing it cancels further requests. Online detail reads do not require SD.
|
|
Saved items read the complete signed record already retained by the download
|
|
transaction, so descriptions and hints remain available offline after restart.
|
|
Saving an open verified online detail copies its immutable response into a
|
|
separate PSRAM allocation for installation. The detail retains its own response,
|
|
so staying on the page or closing it cannot invalidate the other owner. The
|
|
installation reparses the copy to bind borrowed fields to its own bytes and
|
|
reuses the verified identity and hash. It does not repeat the network request
|
|
or Ed25519 verification, or allocate a second verification workspace.
|
|
The handoff still checks the source, request ID, revision hash, summary fields,
|
|
and that the verified signature borrows the retained response. New network
|
|
responses continue through full signature verification.
|
|
The GPX also includes both fields and the signed record; no second sidecar file
|
|
or background detail synchronization is introduced.
|
|
|
|
Storage starts when the user opens Downloaded, Published or the standalone Map,
|
|
or saves a cache. The standalone Map also displays local drafts with a selected
|
|
position, regardless of publication status, alongside downloaded caches. It
|
|
requests four metadata rows per asynchronous window, retains at most 32 markers
|
|
nearest the map center, and stops reading once that pass completes. Panning or
|
|
zooming selects a new nearby set. The shared map overlay capacity also includes
|
|
other layers, so dense views can show fewer than 32 geocaches. Drafts without a
|
|
selected position are excluded; an explicitly selected (0, 0) is valid. No GPX
|
|
parse or network discovery is performed by the map projection.
|
|
Downloaded rows may be displayed while unfinished downloads are being recovered;
|
|
creation and writes still wait for that recovery to finish. Draft pages read four
|
|
rows and one lookahead, expose committed names and positions before publication
|
|
history is resolved, and skip publication history entirely for unbound drafts.
|
|
Publication preparation still requires the complete history projection. Startup
|
|
validates request/task/attempt tables in the reference pass rather than scanning
|
|
the same tables a second time in the preceding integrity pass.
|
|
The draft editor uses the shared map location-selection route instead of latitude
|
|
and longitude text inputs. Confirming writes WGS84 E7 coordinates; cancellation
|
|
preserves the original position and all live editor fields. Existing coordinates
|
|
seed the map viewport, and the selected position is displayed read-only.
|
|
Reticulum starts when Discover is requested or publication readiness is checked.
|
|
Opening saved lists, saved details or the local draft list does not create a
|
|
network backend. Once started, the backend remains available to that session
|
|
and its pending network operations; closing the session releases its ownership.
|
|
Downloads and publications retain signature checks, durable transaction state,
|
|
verified GPX installation, restart recovery and durable reply receipts. Storage
|
|
failure or USB ownership does not prevent Discover from receiving a page.
|
|
Checkpoint rotation shares storage ownership with persistent operations but
|
|
does not delay network queries or pagination.
|
|
|
|
Installed downloads have a separate durable completion marker for file cleanup.
|
|
The optional seventh GpxInstall field is integer `1`, accepted only for the
|
|
Installed phase. Older six-field records remain eligible for one recovery pass.
|
|
The marker is committed only after target verification and successful history
|
|
retention; interrupted cleanup remains recoverable. Recovery also fills missing
|
|
list metadata from the retained signed record. Subsequent startup skips these
|
|
completed installations without reopening their GPX files. Downloaded pages use
|
|
the current head/object/install indexes, with four rows and one lookahead, rather
|
|
than scanning request history or hashing GPX files.
|
|
|
|
Local list row tokens combine the current catalogue's lifetime and its own
|
|
content generation. Download and transport progress do not change Published
|
|
row tokens; publication progress does not change Downloaded row tokens. Replacing
|
|
a catalogue gives it a new lifetime token, so stale selections from a closed
|
|
session cannot become valid when a new catalogue starts at generation one.
|
|
Status messages and action buttons update independently of row rendering.
|
|
|
|
Discover rows use the committed query page's content generation. Query phase,
|
|
failure text, detail responses and unrelated publication progress do not change
|
|
that version. A separate source-lifetime token and saved-membership generation
|
|
invalidate selections when the browser session is replaced or Downloaded
|
|
membership changes. Pagination retains the existing rows while requesting the
|
|
next page; committing the replacement page invalidates the previous selection.
|
|
|
|
Background publication recovery filters completed, cancelled and no-intent
|
|
requests before opening their parent tasks. Exact cache lookups still read
|
|
confirmed receipts, and eligible unfinished requests retain task/reference
|
|
validation. Selecting unfinished work scans the current request index; it does
|
|
not reopen every completed task or GPX file.
|
|
|
|
Legacy kind-3 browse tasks remain readable on existing cards. The indexed
|
|
dispatcher skips them without a startup scan that rewrites each task. Older
|
|
query storage adapters are retained for storage-format compatibility tests;
|
|
the device runtime no longer instantiates them.
|
|
|
|
Host verification uses the production runtime with simulated transport, SD and
|
|
clock: absent SD, USB ownership, busy sends, bounded retries, exact duplicate
|
|
checks, pagination during maintenance, saved downloads, publication updates,
|
|
index corruption, interrupted recovery and checkpoint reclamation. These checks
|
|
do not establish ESP peak heap usage or real network latency.
|
|
|
|
## Incremental commit validation
|
|
|
|
New task creation validates its matching task/request pair and requires both
|
|
keys absent before writing. Beginning a send attempt validates the existing
|
|
task, request, intent, and installation generation before changing lifecycle
|
|
state. These two owners preserve existing reference links and validate only
|
|
the changed rows. Attempt-only updates similarly check their outgoing target.
|
|
Deletion, rebinding, and recovery retain full reference validation.
|
|
|
|
Read faults can occur during journal readback after redundant preflight reads
|
|
are removed. Fault coverage requires unchanged committed data and rejection of
|
|
further writes until recovery; an unconfirmed journal may remain on storage.
|
|
|
|
## Public withdrawal and local removal
|
|
|
|
On a verified cache detail, `Archive public cache` is available only when the
|
|
record author matches the current device identity. Confirming creates a signed
|
|
successor with the same cache ID, the selected revision hash as its parent, and
|
|
state `2`. An existing authored draft is retained with the archived state.
|
|
The UI remains `Awaiting directory confirmation...` until the matching accepted
|
|
directory result is committed. An offline delete never substitutes for this
|
|
public operation. The same action is available on an owned Downloaded detail.
|
|
|
|
Directories hide archived heads from ordinary queries, including continuation
|
|
pages created before archival. They reject ordinary reads of a former active
|
|
revision with `410 cache_archived`, including retries of previously successful
|
|
GET requests. Explicit signed tombstone reads and registered directory peers
|
|
retain access for replication. Publication receipts and synchronization history
|
|
remain durable; already downloaded copies cannot be remotely erased.
|
|
|
|
`Delete local copy` only clears the selected installed head and advances its
|
|
installation generation in a journaled transaction. This removes the offline
|
|
entry from Downloaded and Map and prevents older installation tasks from
|
|
restoring it. Immutable historical objects and GPX files remain on SD; this is
|
|
not a promise of immediate physical file reclamation. Local map projections
|
|
invalidate on committed saves, removals and installation changes even when the
|
|
viewport stays unchanged. Work and payload buffers are allocated in PSRAM.
|
|
|
|
A later download may replace the retained GPX only after its bytes match the
|
|
last committed installation for that cache from an earlier generation. A
|
|
modified or unowned GPX is still rejected. Removing the head does not reactivate
|
|
old download tasks. When identical history already exists, installation
|
|
reacquires the completed receipt before checking the retained file and removing
|
|
its duplicate backup; that cleanup is also safe to resume after restart.
|
|
|
|
Archive retries reuse an existing author reservation's issuance time and verify
|
|
the reconstructed revision hash before signing. Advancing the wall clock must
|
|
not create different bytes for a previously reserved revision. Preparation
|
|
failures are reported in the detail action and once in the publication log.
|
|
|
|
Object references (table 1), installed heads (table 2), and local drafts (table
|
|
4) use current-reference shard generations. Repeated changes to one key retain
|
|
one lookup reference, with older generations still available to pinned roots.
|
|
Legacy shards convert when next written; untouched legacy buckets are detected
|
|
by writable recovery and converted by idle checkpoint maintenance. A card used
|
|
only for local draft reads retains its compatible legacy reader until a write
|
|
occurs. Legacy Downloaded records use the one-time metadata upgrade above.
|
|
|
|
Downloaded page reads allocate index buffers only. Signed-record verification
|
|
and the 8 KiB response buffer are allocated when a detail, download, or legacy
|
|
metadata upgrade acquires its lease, rather than during current-format startup.
|
|
All these buffers remain PSRAM-only and are trimmed after the lease is released.
|