Skip to content

20.3 Geocaching Integration Examples and Conformance

liu weikai edited this page Sep 28, 2026 · 1 revision

Geocaching Integration Examples and Conformance

This page provides a runnable local example of the actual directory codec and transaction interface. It does not publish anything on the public network. Follow Directory Operations when adding authenticated LXMF transport.

Install the reference dependencies

From a checkout of the codex/geocaching branch:

python -m venv .venv-geocaching
# Activate this environment using your shell's normal activation command.
python -m pip install -r tools/geocaching/interop-requirements.txt

The requirement file pins the versions used by the reference implementation. Run the example from the repository root. It uses a temporary SQLite database and the independently checked-in signed fixture; it does not need a network interface, private author key or existing directory database.

Publish, query and retrieve through the application boundary

import os
import sys
import tempfile
from pathlib import Path

root = Path.cwd()
sys.path.insert(0, str(root / "tools" / "geocaching"))
from directory_store import DirectoryStore, packed, unpacked, verify_cache

fixture = root / "modules/core_geocaching/tests/fixtures/signed-cache-v1.bin"
signed = unpacked(fixture.read_bytes())
verified = verify_cache(signed)
reader = os.urandom(16)

with tempfile.TemporaryDirectory() as temporary:
    store = DirectoryStore(Path(temporary) / "directory.sqlite", os.urandom(16))
    try:
        def request(operation, body):
            request_id = os.urandom(16)
            raw = packed([1, 0, operation, request_id, 8192, body])
            # Production adapters must authenticate LXMF before calling handle.
            response_raw = store.handle(reader, raw)
            assert response_raw is not None, "No durable response"
            response = unpacked(response_raw)
            assert response[:4] == [1, 1, operation, request_id]
            assert response[4] == 200, response[5]
            # An identical retry must return the same persisted result.
            assert store.handle(reader, raw) == response_raw
            return response[5]

        caps = request(0, [])
        assert caps[2] == [0, 1, 2, 3, 4]
        published = request(1, [signed])
        assert published[0] == verified.cache_id
        page = request(2, [[-900000000, -1800000000,
                            900000000, 1800000000], 7, None, 20, None])
        assert len(page[1]) == 1 and page[2] is None
        summary = page[1][0]
        detail = request(3, [summary[0], summary[2], None])
        retrieved = verify_cache(detail[0])
        assert retrieved.summary == summary
        assert retrieved.cache_id == verified.cache_id
        assert detail[1:] == [1, 0]
        print("Verified:", retrieved.record[8], retrieved.cache_id.hex())
    finally:
        store.close()

DirectoryStore.handle() deliberately assumes an already authenticated source. Calling it directly is useful for conformance testing, but exposing it to unauthenticated input as a production API would bypass the transport trust boundary.

Creating your own signed record

Use a persistent author identity and a fresh 16-byte creation nonce for each new cache. Encode the exact 16-field record described in Wire Format. Sign the domain prefix plus raw record with the author's Ed25519 key, then wrap the raw bytes and 64-byte signature as SignedCache.

For an update, retain the identity, nonce and creation timestamp, increment the revision and set the previous hash to the preceding revision's exact hash. Revisions are immutable. Persist the signed outgoing request before sending it so a restart can retry the same bytes and request ID. Coordinate writers sharing an identity to avoid signing competing revisions. Neither a directory nor a transport bridge can recreate a lost author private key.

The reference uses RNS.Identity.get_public_key() for the 64-byte public identity and RNS.Identity.sign() for signing. The corresponding verification path is verify_cache() in directory_store.py. Do not publish fixture identities or example records as user content.

Transport integration

Use directory_service.py as the reference for LXMF reception: validate destination, source identity and message signature before dispatching the binary application payload. Set both application fields described in the wire guide. Match responses by source, operation and request ID. A Reticulum packet acknowledgement or LXMF delivery proof does not replace the application response.

For browser integrations, reuse or study DirectoryClient in site/geocaching/src/protocol-client.js. It handles signed discovery, capabilities, request serialization, bounded retries, exact detail retrieval and author verification. Its query() emits result events; get() retrieves and verifies full details. makeGpx() exports verified signed records. The worker boundary keeps this network processing out of the UI thread.

Conformance checklist

  • Parse the checked-in fixtures and reproduce their cache IDs and revision hashes.
  • Reject an altered signature, invalid text, noncanonical encoding and trailing bytes.
  • Verify directory summaries against the returned full signed record.
  • Retry the same request after losing its reply; verify the original durable response is returned.
  • Reject a reused request ID carrying different bytes.
  • Continue query cursors without changing reader identity, bounds, mask, author, limit or budget.
  • Start a new query after cursor expiration; do not silently splice different query snapshots.
  • Fetch exact revisions during replication and commit objects before advancing the checkpoint.
  • Preserve competing valid revisions as conflict evidence.
  • Verify that a new live query sees newly published data without rebuilding the website.
  • Disconnect and reconnect transport; verify fresh directory validation and query execution.
  • Confirm local saved browsing still works with all network access unavailable.

The final two checks require runtime integration tests. Passing a codec fixture test alone does not establish network recovery or device storage behavior.

Clone this wiki locally