<?xml version="1.0" encoding="UTF-8"?>
<rfc xmlns:xi="http://www.w3.org/2001/XInclude"
     ipr="trust200902"
     category="info"
     submissionType="independent"
     docName="draft-elkhatabi-verifiable-telemetry-ledgers-00"
     version="3">
  <front>
    <title abbrev="TrackOne VTL">Verifiable Telemetry Ledgers for Resource-Constrained Environments</title>
    <seriesInfo name="Internet-Draft" value="draft-elkhatabi-verifiable-telemetry-ledgers-00"/>

    <author fullname="Bilal El Khatabi" initials="B." surname="El Khatabi">
      <organization>TrackOne Project</organization>
      <address>
        <email>elkhatabibilal@gmail.com</email>
      </address>
    </author>

    <date year="2026" month="March" day="2"/>
    <area>General</area>
    <workgroup>Independent Submission</workgroup>
    <keyword>telemetry</keyword>
    <keyword>merkle</keyword>
    <keyword>timestamping</keyword>
    <keyword>iot</keyword>

    <abstract>
      <t>
        This document specifies a verifiable telemetry ledger profile for
        resource-constrained sensing environments. The profile defines how a
        gateway accepts framed telemetry, applies anti-replay policy,
        projects accepted frames into canonical facts, builds deterministic
        daily Merkle commitments, and anchors daily artifacts with external
        timestamp proofs. OpenTimestamps (OTS) is the default anchoring
        mechanism; optional parallel attestation methods (RFC 3161 timestamp
        protocol and peer signatures) are also described.
      </t>
      <t>
        The goal is interoperability and independent auditability, not new
        cryptographic primitives.
      </t>
    </abstract>
  </front>

  <middle>
    <section numbered="true" toc="include" anchor="introduction">
      <name>Introduction</name>
      <t>
        Long-lived telemetry deployments such as environmental monitoring,
        heritage conservation, and infrastructure health need evidence that
        measurements were not silently altered after a collection. A coastal
        sensor array that reports temperature every six hours over a five-year
        deployment produces tens of thousands of records; stakeholders such as
        regulators, researchers, and insurers may need to verify, years later,
        that the data they rely on is the same data the sensors produced.
      </t>
      <t>
        Existing standards provide important building blocks:
      </t>
      <ul>
        <li>Deterministic CBOR encoding (<xref target="RFC8949"/>),</li>
        <li>Timestamping via trusted timestamp authorities (<xref target="RFC3161"/>),</li>
        <li>CBOR-based Merkle tree proofs (<xref target="COSE-MERKLE"/>), and</li>
        <li>Supply-chain transparency architectures (<xref target="SCITT"/>).</li>
      </ul>
      <t>
        However, none of these building blocks defines a complete operational
        profile for the constrained case: devices with intermittent uplinks,
        limited compute budgets, and no persistent Internet connectivity at the
        collection point. SCITT assumes a transparency service is reachable for
        receipt-oriented workflows. COSE Merkle proofs define proof encodings,
        but not batching policy or anchoring lifecycle. RATS addresses device
        identity attestation, but not telemetry commitment.
      </t>
      <t>
        The discussion below compares this profile directly with
        <xref target="SCITT"/> and <xref target="COSE-MERKLE"/> because those
        documents define adjacent, but not identical, transparency and proof
        disclosure models.
      </t>
      <t>
        This document fills that gap. It specifies a practical profile that
        combines these building blocks for low-power telemetry systems:
      </t>
      <ol>
        <li>Emit encrypted framed telemetry.</li>
        <li>Ingest and validate frames with anti-replay.</li>
        <li>Project accepted frames into canonical facts.</li>
        <li>Build deterministic daily Merkle commitments.</li>
        <li>Chain days with previous-day root linkage.</li>
        <li>Anchor the day artifact using external timestamp proofs.</li>
        <li>Verify independently from disclosed artifacts.</li>
      </ol>
      <t>
        The profile is informed by TrackOne pre-production validation,
        including constrained uplink simulations, hardware-in-loop testing,
        and daily batching and verification workflows.
      </t>

      <section numbered="true" toc="include" anchor="relationship-existing-work">
        <name>Relationship to Existing Work</name>
        <t>
          This document is complementary to, not a replacement for, the
          following work:
        </t>
        <ul>
          <li>
            <em>SCITT</em> (<xref target="SCITT"/>): SCITT defines architectures
            for transparent, append-only logs of signed statements with
            receipts. This profile differs in that it is optimized for
            disconnected, daily-batch operation where a transparency service is
            not assumed to be continuously reachable.
          </li>
          <li>
            <em>COSE Merkle Tree Proofs</em> (<xref target="COSE-MERKLE"/>):
            COSE-MERKLE defines proof encodings. This profile defines a batching
            and commitment contract and may use COSE-based proof encodings in a
            future revision.
          </li>
          <li>
            <em>RATS</em>: This profile explicitly defers device identity,
            attestation, and key lifecycle to deployment-specific mechanisms.
          </li>
          <li>
            <em>RFC 8949</em> (<xref target="RFC8949"/>): This profile defines a
            TrackOne deterministic CBOR commitment profile for gateway-side
            commitments. It does not attempt to define a general-purpose CBOR
            profile beyond the commitment path described here.
          </li>
        </ul>
      </section>
    </section>

    <section numbered="true" toc="include" anchor="terminology">
      <name>Conventions and Terminology</name>
      <t>
        The key words "<bcp14>MUST</bcp14>", "<bcp14>MUST NOT</bcp14>",
        "<bcp14>REQUIRED</bcp14>", "<bcp14>SHALL</bcp14>",
        "<bcp14>SHALL NOT</bcp14>", "<bcp14>SHOULD</bcp14>",
        "<bcp14>SHOULD NOT</bcp14>", "<bcp14>RECOMMENDED</bcp14>",
        "<bcp14>NOT RECOMMENDED</bcp14>", "<bcp14>MAY</bcp14>", and
        "<bcp14>OPTIONAL</bcp14>" in this document are to be interpreted as
        described in BCP 14 <xref target="RFC2119"/> <xref target="RFC8174"/>
        when, and only when, they appear in all capitals, as shown here.
      </t>
      <t>Terms:</t>
      <ul>
        <li>Frame: One NDJSON telemetry unit containing header and AEAD fields.</li>
        <li>Fact: Canonical telemetry record projected from an accepted frame.</li>
        <li>Commitment profile: The serialization, hash, and Merkle rules that produce deterministic commitment outputs.</li>
        <li>Ledger set for day D (F_D): The set of accepted facts committed for day D.</li>
        <li>Day artifact: The authoritative canonical day record written as <tt>day/YYYY-MM-DD.cbor</tt>.</li>
        <li>Authoritative: Used for the canonical artifact that verifiers MUST treat as the cryptographic source of truth.</li>
        <li>Projection: A non-authoritative representation (for example JSON) derived from an authoritative artifact.</li>
        <li>OTS metadata sidecar: <tt>proofs/YYYY-MM-DD.ots.meta.json</tt>, linking an artifact digest to an OTS proof path.</li>
        <li>Replay unit: The pair <tt>(dev_id, fc)</tt>, where <tt>fc</tt> is a frame counter for device <tt>dev_id</tt>.</li>
        <li>Day boundary: A UTC calendar day boundary. Day labels in this profile use <tt>YYYY-MM-DD</tt> in UTC.</li>
        <li>Disclosure class: The level of artifact disclosure associated with a verification claim.</li>
      </ul>
    </section>

    <section numbered="true" toc="include" anchor="roles">
      <name>System Roles</name>
      <ul>
        <li>Device (Pod): Produces framed telemetry.</li>
        <li>Gateway: Validates, decrypts, applies anti-replay, projects facts, batches, and anchors day artifacts.</li>
        <li>Verifier: Recomputes commitments and validates proofs from disclosed artifacts.</li>
        <li>OTS Calendar(s): Provides OTS attestations for day artifact hashes.</li>
        <li>Optional TSA: An RFC 3161 timestamp authority over the same digest.</li>
        <li>Optional Peers: Co-sign daily roots for short-term provenance.</li>
      </ul>
    </section>

    <section numbered="true" toc="include" anchor="data-model">
      <name>Data and Commitment Model</name>

      <section numbered="true" toc="include" anchor="frame-contract">
        <name>Frame Contract</name>
        <t>A frame is transported as NDJSON with fields:</t>
        <figure>
          <artwork type="json"><![CDATA[
{
  "hdr": { "dev_id": 101, "msg_type": 1, "fc": 42, "flags": 0 },
  "nonce": "base64-24B",
  "ct": "base64-ciphertext",
  "tag": "base64-16B"
}
]]></artwork>
        </figure>
        <t>
          Gateways MUST validate header field presence, header ranges, and AEAD
          authentication before fact emission.
        </t>
        <ul>
          <li><tt>hdr.dev_id</tt> MUST be an unsigned integer in the range 0..65535.</li>
          <li><tt>hdr.msg_type</tt> MUST be an unsigned integer in the range 0..255.</li>
          <li><tt>hdr.fc</tt> MUST be an unsigned integer in the range 0..(2^32-1).</li>
          <li><tt>hdr.flags</tt> MUST be an unsigned integer in the range 0..255.</li>
          <li><tt>nonce</tt> MUST be base64 text that decodes to exactly 24 bytes.</li>
          <li><tt>tag</tt> MUST be base64 text that decodes to exactly 16 bytes.</li>
        </ul>
        <t>
          Frames that fail parse, range, or AEAD validation MUST be rejected
          before fact commitment and MUST NOT produce committed facts.
        </t>
      </section>

      <section numbered="true" toc="include" anchor="anti-replay">
        <name>Anti-Replay Semantics</name>
        <t>The replay unit is <tt>(dev_id, fc)</tt>.</t>
        <ul>
          <li>A gateway MUST consume at most one frame per <tt>(dev_id, fc)</tt> into the ledger.</li>
          <li>Duplicate or out-of-window frames MUST NOT produce committed facts.</li>
          <li>The RECOMMENDED default replay window is 64 frame counter values per device.</li>
          <li>Frames arriving with <tt>fc</tt> more than <tt>window_size</tt> behind the highest accepted counter for a device MUST be rejected.</li>
          <li>Frames arriving with <tt>fc</tt> more than <tt>window_size</tt> ahead of the highest accepted counter for a device MUST also be rejected, because an excessive forward jump is treated as out of window by the current gateway profile.</li>
        </ul>
        <t><strong>Structured Rejection Evidence</strong></t>
        <t>
          Gateways SHOULD produce structured rejection evidence for rejected
          frames. A rejection record SHOULD include at minimum:
        </t>
        <ul>
          <li><tt>device_id</tt>,</li>
          <li><tt>fc</tt> (or null if unavailable),</li>
          <li><tt>reason</tt>,</li>
          <li><tt>observed_at_utc</tt>, and</li>
          <li><tt>frame_sha256</tt>.</li>
        </ul>
        <t>
          Rejection evidence is an audit artifact and MUST NOT be hashed into
          ledger commitments.
        </t>
        <t><strong>Replay State Persistence</strong></t>
        <t>
          Gateways SHOULD persist replay state across restart. If replay state
          is lost, gateways SHOULD record a continuity break event and SHOULD
          NOT silently re-accept counters that could already have been
          committed.
        </t>
        <t><strong>Scope Limitation</strong></t>
        <t>
          This profile provides tamper-evidence for committed facts. It does
          not prove the absence of selective pre-commitment drops by a
          malicious gateway.
        </t>
      </section>

      <section numbered="true" toc="include" anchor="fact-projection">
        <name>Frame-to-Fact Projection</name>
        <t>
          The frame-to-fact projection transforms a validated, decrypted frame
          into a canonical fact suitable for commitment. The committed fact is
          a structured record derived from the decrypted payload, not a copy of
          transport bytes.
        </t>
        <ol>
          <li>The gateway MUST verify AEAD authentication.</li>
          <li>The gateway MUST decrypt the ciphertext.</li>
          <li>The gateway MUST parse the decrypted plaintext according to the frame's <tt>msg_type</tt>.</li>
          <li>The gateway MUST construct a fact object.</li>
          <li>The gateway MUST serialize that fact object under the gateway commitment profile.</li>
          <li>The canonical fact bytes are then hashed for Merkle inclusion.</li>
        </ol>
        <t>A committed fact MUST contain at minimum:</t>
        <ul>
          <li><tt>device_id</tt>,</li>
          <li><tt>timestamp</tt>,</li>
          <li><tt>nonce</tt>, and</li>
          <li><tt>payload</tt>.</li>
        </ul>
        <t>
          Ciphertext, raw transport bytes, and the authentication tag MUST NOT
          be part of the committed fact object. The exact payload schema is
          deployment-specific; the deterministic projection contract is the
          normative requirement.
        </t>
      </section>

      <section numbered="true" toc="include" anchor="cbor-profile">
        <name>Deterministic CBOR Commitment Encoding</name>
        <t>
          This section does not define a new general-purpose CBOR variant. It
          records the narrow deterministic CBOR encoding used for commitment
          bytes in the current TrackOne gateway and ledger implementation.
          The identifier <tt>trackone-cbor-map-v1</tt> names this commitment
          recipe so verifiers can tell which byte-level rules were used.
        </t>
        <t>
          The authoritative commitment artifacts, namely CBOR fact
          artifacts and the canonical day artifact, use a constrained
          subset of deterministic encoding under Section 4.2.1 of
          <xref target="RFC8949"/>. For TrackOne commitment bytes, the
          following concrete choices apply:
        </t>
        <ul>
          <li>All commitment-path items MUST use definite-length encoding.</li>
          <li>Integers MUST use the shortest encoding width permitted by <xref target="RFC8949"/>.</li>
          <li>Map keys MUST be CBOR text strings.</li>
          <li>Map keys MUST be sorted by encoded key length ascending, then by lexicographic order of the encoded key bytes.</li>
          <li>Finite floating-point values MUST be encoded using the shortest of float16, float32, or float64 that exactly preserves the value.</li>
          <li>NaN, positive infinity, and negative infinity MUST be rejected in commitment paths.</li>
          <li>CBOR tags MUST NOT appear in commitment bytes.</li>
          <li>Supported values are unsigned integers, negative integers, byte strings, text strings, arrays, maps, booleans, null, and deterministic finite floats.</li>
        </ul>
        <t>
          Implementations MUST NOT accept generic CBOR serializers as
          authoritative commitment encoders. An encoder is acceptable only
          if it yields the same bytes as these rules.
        </t>
        <t>
          JSON projections of fact artifacts and day artifacts are
          optional and non-authoritative. They MUST NOT be used as
          commitment inputs. When produced, such projections SHOULD follow
          <xref target="RFC8785"/>.
        </t>
        <t>
          Device-side or embedded components MAY use other internal encodings,
          including different deterministic CBOR layouts optimized for local
          constraints. Those encodings are not the authoritative commitment
          encoding described here unless they are explicitly identified by a
          distinct <tt>commitment_profile_id</tt> and verified under their own
          rules.
        </t>
      </section>

      <section numbered="true" toc="include" anchor="merkle-policy">
        <name>Deterministic Commitment Tree Calculation</name>
        <t>
          For a given day <tt>D</tt>, the current commitment profile computes a
          daily root from the canonical fact commitment bytes produced under
          <xref target="cbor-profile"/>. The following steps describe that
          calculation.
        </t>
        <t><strong>Leaf Digests</strong></t>
        <ul>
          <li>Each canonical fact byte string is hashed with <tt>SHA-256</tt>, yielding a 32-byte leaf digest.</li>
        </ul>
        <t><strong>Digest Ordering</strong></t>
        <ul>
          <li>To make the daily root independent of file order or ingest order, leaf digests MUST be sorted in ascending byte order before reduction.</li>
          <li>Lowercase hexadecimal is a representation format for artifacts and examples only; internal Merkle computation operates on raw hash bytes.</li>
          <li>Sorting by lowercase hexadecimal is equivalent to bytewise ascending order over the raw digests.</li>
        </ul>
        <t><strong>Pairwise Reduction</strong></t>
        <ul>
          <li>The sorted digests are reduced pairwise by computing <tt>SHA-256(left_child_bytes || right_child_bytes)</tt>, where both operands are raw 32-byte digests.</li>
          <li>If a layer has an odd number of digests, the final digest is duplicated to form the last pair.</li>
          <li>The current commitment profile does not prepend domain-separation bytes to leaf or parent hashes.</li>
        </ul>
        <t><strong>Empty Day</strong></t>
        <ul>
          <li>If no facts are committed for the day, the daily root is the <tt>SHA-256</tt> digest of zero bytes: <tt>e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855</tt>.</li>
        </ul>
        <t>
          The resulting daily root is deterministic for the set of committed
          facts. Because the leaf digests are sorted before reduction, the
          result depends on the committed fact set rather than on ingestion
          order.
        </t>
        <t>
          Any future change to this calculation that alters commitment bytes
          (for example, adding domain separation) MUST use a new
          <tt>commitment_profile_id</tt>.
        </t>
      </section>

      <section numbered="true" toc="include" anchor="day-artifact-schema">
        <name>Day Artifact Schema</name>
        <t>
          The authoritative day artifact is a CBOR-encoded day record produced
          under <xref target="cbor-profile"/>. The day record contains the
          following fields:
        </t>
        <ul>
          <li><tt>version</tt> (uint): day-record schema version, currently 1.</li>
          <li><tt>site_id</tt> (tstr): site identifier.</li>
          <li><tt>date</tt> (tstr): UTC day label in <tt>YYYY-MM-DD</tt> form.</li>
          <li><tt>prev_day_root</tt> (tstr): previous day root as 64 lowercase hexadecimal characters.</li>
          <li><tt>batches</tt> (array): array of batch objects.</li>
          <li><tt>day_root</tt> (tstr): deterministic day root as 64 lowercase hexadecimal characters.</li>
        </ul>
        <t>Each batch object contains:</t>
        <ul>
          <li><tt>version</tt> (uint): batch-record schema version, currently 1.</li>
          <li><tt>site_id</tt> (tstr): site identifier.</li>
          <li><tt>day</tt> (tstr): UTC day label in <tt>YYYY-MM-DD</tt> form.</li>
          <li><tt>batch_id</tt> (tstr): batch identifier.</li>
          <li><tt>merkle_root</tt> (tstr): batch Merkle root as 64 lowercase hexadecimal characters.</li>
          <li><tt>count</tt> (uint): number of committed facts in the batch.</li>
          <li><tt>leaf_hashes</tt> (array of tstr): sorted leaf hashes as lowercase hexadecimal strings.</li>
        </ul>
        <t>
          <tt>day/YYYY-MM-DD.cbor</tt> is authoritative. The corresponding
          <tt>day/YYYY-MM-DD.json</tt> file is a projection only.
        </t>
        <t>
          This document uses normative field tables rather than CDDL. A future
          revision may add a formal CDDL appendix if broader independent
          implementations require it.
        </t>
      </section>

      <section numbered="true" toc="include" anchor="day-chaining">
        <name>Day Chaining</name>
        <t>Day records include <tt>prev_day_root</tt>.</t>
        <ul>
          <li>The genesis day for a site MUST set <tt>prev_day_root</tt> to 64 ASCII zero characters, representing 32 zero bytes.</li>
          <li>Non-genesis days MUST set <tt>prev_day_root</tt> to the previous committed day's <tt>day_root</tt>.</li>
        </ul>
        <t>
          Because day labels are UTC-based, chaining semantics are also
          defined on UTC day boundaries.
        </t>
      </section>
    </section>

    <section numbered="true" toc="include" anchor="artifacts">
      <name>Artifacts and Verification Bundles</name>
      <t>Illustrative artifact layout:</t>
      <ul>
        <li><tt>facts/&lt;fact-id&gt;.cbor</tt> — authoritative canonical facts</li>
        <li><tt>facts/&lt;fact-id&gt;.json</tt> — optional projections</li>
        <li><tt>day/YYYY-MM-DD.cbor</tt> — authoritative canonical day artifact</li>
        <li><tt>day/YYYY-MM-DD.json</tt> — optional projection</li>
        <li><tt>blocks/YYYY-MM-DD-00.block.json</tt> — block and batch metadata</li>
        <li><tt>day/YYYY-MM-DD.cbor.sha256</tt> — convenience digest</li>
        <li><tt>day/YYYY-MM-DD.cbor.ots</tt> — OTS proof</li>
        <li><tt>proofs/YYYY-MM-DD.ots.meta.json</tt> — OTS binding metadata</li>
      </ul>
      <t>
        Deployments MAY store artifacts differently and MAY export them as
        bundles. The path shapes above are illustrative.
      </t>
      <t>At minimum, an OTS sidecar MUST bind:</t>
      <ul>
        <li><tt>artifact</tt>,</li>
        <li><tt>artifact_sha256</tt>, and</li>
        <li><tt>ots_proof</tt>.</li>
      </ul>
      <t>
        Verifiers MUST recompute the day artifact digest and compare it with
        the sidecar before accepting any proof validation result.
      </t>
    </section>

    <section numbered="true" toc="include" anchor="anchoring-verification">
      <name>Anchoring and Verification</name>

      <section numbered="true" toc="include" anchor="anchoring-contract">
        <name>Anchoring Contract</name>
        <t>
          The generic anchoring contract is simple: a gateway computes the
          SHA-256 digest of the authoritative day artifact and submits that
          digest to one or more external timestamping channels. Verifiers MUST
          first recompute the day artifact digest locally; proof validation
          occurs only after digest binding validation succeeds.
        </t>
        <t>
          A deployment conforming to this profile MUST use at least one
          anchoring channel. OTS is the default channel described by this
          document; RFC 3161 and peer signatures are optional parallel
          channels.
        </t>
      </section>

      <section numbered="true" toc="include" anchor="ots-profile">
        <name>OTS Anchoring Profile</name>
        <t>
          When <xref target="OTS"/> is used, the gateway stamps
          <tt>SHA-256(day/YYYY-MM-DD.cbor)</tt> and stores an OTS proof plus an
          OTS sidecar.
        </t>

        <section numbered="true" toc="include" anchor="ots-lifecycle">
          <name>OTS Anchoring Lifecycle</name>
          <ol>
            <li><em>Submission</em>: the gateway submits the day artifact digest to one or more OTS calendars.</li>
            <li><em>Pending</em>: a calendar may return an incomplete proof while awaiting Bitcoin commitment.</li>
            <li><em>Upgrade</em>: the gateway or a background process may later upgrade the proof to include completed attestations.</li>
            <li><em>Verification</em>: a verifier recomputes the artifact digest and validates the proof.</li>
          </ol>
          <t>
            Gateways SHOULD submit to multiple independent calendars to reduce
            single-calendar unavailability risk.
          </t>
        </section>

        <section numbered="true" toc="include" anchor="delayed-anchoring">
          <name>Handling Delayed or Failed Anchoring</name>
          <ul>
            <li><strong>Calendar unreachable</strong>: the gateway MUST still write the day artifact and SHOULD retry later.</li>
            <li><strong>Upgrade delay</strong>: verifiers SHOULD treat pending proofs as unanchored and flag the condition, not as invalid.</li>
            <li><strong>Proof retention</strong>: gateways MUST retain proof files for at least the operational retention period of the corresponding day artifacts.</li>
          </ul>
        </section>
      </section>

      <section numbered="true" toc="include" anchor="parallel-attestation">
        <name>Optional Parallel Attestation</name>
        <t>Deployments MAY also produce:</t>
        <ul>
          <li>An <xref target="RFC3161"/> timestamp response over the same day-artifact digest.</li>
          <li>A peer signature quorum over <tt>(site_id, day, day_root, context)</tt>.</li>
        </ul>
        <t>
          When multiple channels are present, verifiers SHOULD validate all
          available channels independently and report per-channel results.
        </t>
        <t>
          If a verifier is configured in strict mode for optional channels,
          failure of those channels MUST cause overall verification failure.
        </t>
      </section>

      <section numbered="true" toc="include" anchor="verify">
        <name>Verification</name>
        <t>Verifiers SHOULD apply checks in the following fail-fast order:</t>
        <ol>
          <li>Validate that disclosed artifacts are sufficient for the claimed disclosure class.</li>
          <li>Recompute the Merkle root from canonical fact CBOR artifacts.</li>
          <li>Compare the recomputed root to <tt>day_root</tt>.</li>
          <li>Recompute <tt>SHA-256(day/YYYY-MM-DD.cbor)</tt> and compare it to the sidecar <tt>artifact_sha256</tt>.</li>
          <li>Validate the OTS proof when OTS is required or present.</li>
          <li>Validate optional RFC 3161 and peer attestations as configured.</li>
        </ol>
        <t>Verifier implementations SHOULD expose machine-usable failure categories:</t>
        <ul>
          <li>malformed or missing artifacts,</li>
          <li>Merkle mismatch,</li>
          <li>missing or invalid OTS proof,</li>
          <li>sidecar mismatch or digest mismatch, and</li>
          <li>optional-channel failure.</li>
        </ul>
      </section>
    </section>

    <section numbered="true" toc="include" anchor="disclosure-bundles">
      <name>Disclosure Classes</name>
      <t>
        Verification claims depend on what artifacts are disclosed. This
        profile defines three disclosure classes.
      </t>
      <ul>
        <li><strong>Class A (Public Recompute)</strong>: sufficient material for independent fact-level recomputation.</li>
        <li><strong>Class B (Partner Audit)</strong>: controlled disclosure with redacted or partitioned fact material.</li>
        <li><strong>Class C (Anchor-Only)</strong>: existence and timestamp evidence only.</li>
      </ul>
      <t>A Class A bundle MUST include:</t>
      <ul>
        <li>all canonical fact artifacts required to recompute the claimed day root,</li>
        <li>the canonical day artifact,</li>
        <li>block and batch metadata, and</li>
        <li>the OTS proof plus its sidecar metadata.</li>
      </ul>
      <t>
        A Class A bundle SHOULD also include a machine-readable verification
        manifest and SHOULD record the <tt>commitment_profile_id</tt>.
      </t>
      <t>A Class B bundle MUST include:</t>
      <ul>
        <li>the canonical day artifact,</li>
        <li>block and batch metadata,</li>
        <li>the OTS proof plus sidecar metadata, and</li>
        <li>cryptographic commitments to any withheld or partitioned fact material, and</li>
        <li>a policy statement describing withheld or partitioned fact material.</li>
      </ul>
      <t>
        Class B outputs MUST NOT be represented as publicly recomputable.
      </t>
      <t>
        A Class C disclosure MUST be labeled as existence and timestamp
        evidence only and MUST NOT claim fact-level reproducibility.
      </t>
      <t>A Class C bundle MUST include:</t>
      <ul>
        <li>the canonical day artifact, and</li>
        <li>at least one timestamp proof artifact (for example, an OTS proof or an RFC 3161 response).</li>
      </ul>
      <t>
        If a verification manifest is present, it SHOULD include:
      </t>
      <ul>
        <li><tt>disclosure_class</tt>,</li>
        <li><tt>commitment_profile_id</tt>,</li>
        <li>artifact path and digest entries,</li>
        <li>per-channel anchor status, and</li>
        <li>a list of checks executed.</li>
      </ul>
    </section>

    <section numbered="true" toc="include" anchor="versioning">
      <name>Versioning</name>
      <t>
        This profile has several independent version surfaces:
      </t>
      <ul>
        <li>Document revision (for example <tt>-00</tt>, <tt>-01</tt>) is editorial and is not part of commitment output.</li>
        <li>Artifact schema versions are carried by the <tt>version</tt> fields in day and batch records.</li>
        <li><tt>commitment_profile_id</tt> identifies the canonical CBOR, hash, and Merkle rules that define commitment outputs.</li>
      </ul>
      <t>
        The commitment profile defined in this document is
        <tt>trackone-cbor-map-v1</tt>. If a verifier encounters an unsupported
        <tt>commitment_profile_id</tt>, it MUST reject the verification claim
        rather than silently using a fallback interpretation.
      </t>
    </section>

    <section numbered="true" toc="include" anchor="conformance-vectors">
      <name>Conformance Vectors</name>
      <t>
        Determinism claims in this profile are testable. Implementations that
        claim conformance to <tt>trackone-cbor-map-v1</tt> MUST be able to
        reproduce the published conformance vectors for that profile.
      </t>
      <t>Published vector sets SHOULD include coverage for:</t>
      <ul>
        <li>empty day,</li>
        <li>single fact,</li>
        <li>odd leaf count,</li>
        <li>power-of-two leaf count,</li>
        <li>duplicate leaf hashes,</li>
        <li>genesis chaining,</li>
        <li>non-genesis chaining, and</li>
        <li>a full Class A disclosure example.</li>
      </ul>
      <t>
        Cross-implementation checks MUST verify byte-for-byte parity across
        independent implementations. Any mismatch in canonical bytes or roots
        is a conformance failure.
      </t>
      <t>
        Published vector bundles MUST include the
        <tt>commitment_profile_id</tt>.
      </t>
    </section>

    <section numbered="true" toc="include" anchor="security">
      <name>Security Considerations</name>
      <t>
        This profile does not introduce new cryptographic primitives. Security
        depends on correct composition of existing primitives and on
        operational discipline.
      </t>
      <t><strong>Replay and Duplicate Suppression</strong></t>
      <t>
        The <tt>(dev_id, fc)</tt> replay unit enforces single-consumption: at
        most one committed fact per unique replay unit.
      </t>
      <t><strong>Tamper Evidence</strong></t>
      <t>
        Once a day artifact is anchored, mutation of that artifact changes its
        digest and invalidates the proof. Day chaining extends this property
        across days.
      </t>
      <t><strong>Proof Substitution</strong></t>
      <t>
        Sidecar metadata binds an artifact digest to a proof path. Verifiers
        MUST recompute the artifact digest independently.
      </t>
      <t><strong>Calendar Trust and Withholding</strong></t>
      <t>
        A compromised or unavailable calendar can delay or withhold proofs.
        Multi-calendar submission reduces single-calendar dependency but does
        not eliminate coordinated compromise risk.
      </t>
      <t><strong>Operational Misuse</strong></t>
      <t>
        Test or placeholder proofs MUST NOT be treated as production
        attestations.
      </t>
      <t><strong>Confidentiality Boundary</strong></t>
      <t>
        This profile addresses integrity and timing provenance only. Payload
        confidentiality remains the responsibility of the deployment's AEAD and
        key-management layers.
      </t>
      <t><strong>Pre-Commitment Censorship</strong></t>
      <t>
        This profile proves inclusion and timestamping of committed facts. It
        does not prove completeness of all observed or emitted frames.
      </t>
    </section>

    <section numbered="true" toc="include" anchor="privacy">
      <name>Privacy Considerations</name>
      <t>Telemetry payloads may include sensitive operational data. Operators SHOULD:</t>
      <ul>
        <li>minimize personally identifiable data in committed artifacts,</li>
        <li>separate identity metadata from measurement payload when possible,</li>
        <li>apply retention and access controls, and</li>
        <li>publish only data appropriate for the chosen disclosure class.</li>
      </ul>
      <t>
        Privacy-preserving disclosures remain valid, but they MUST NOT be
        described as publicly recomputable unless Class A conditions are met.
      </t>
    </section>

    <section numbered="true" toc="include" anchor="iana">
      <name>IANA Considerations</name>
      <t>This document has no IANA actions.</t>
      <t>In particular, this revision does not request:</t>
      <ul>
        <li>a CBOR tag allocation,</li>
        <li>a media type registration, or</li>
        <li>a new registry entry.</li>
      </ul>
      <t>
        The current profile is identified by in-band version and profile
        fields, not by IANA allocation.
      </t>
    </section>

    <section numbered="true" toc="include" anchor="implementation-status">
      <name>Implementation Status</name>
      <t>Note to RFC Editor: Please remove this section before publication.</t>
      <t>
        This section records implementation status at the time of posting, per
        <xref target="RFC7942"/>.
      </t>
      <t>
        This draft reflects the TrackOne pre-production implementation
        snapshot at release <tt>0.1.0-alpha.6</tt>.
      </t>
      <t>
        A pre-production reference implementation exists and is aligned with
        the current <tt>0.1.0-alpha.6</tt> release. It is suitable for
        interoperability review and validation, but it is not a production
        deployment.
      </t>
      <ul>
        <li>Repository: https://github.com/bilalobe/trackone</li>
        <li>License: MIT</li>
      </ul>
    </section>

    <section numbered="true" toc="include" anchor="interop-notes">
      <name>Interoperability Notes and Open Questions</name>
      <ul>
        <li>Media type strategy for canonical CBOR day artifacts.</li>
        <li>Whether a future revision should define a formal CDDL appendix.</li>
        <li>Whether future disclosure bundles should adopt COSE-MERKLE proof encodings.</li>
        <li>Registry strategy for disclosure and anchor-status vocabularies.</li>
        <li>Whether a future commitment profile should introduce domain separation.</li>
      </ul>
    </section>
  </middle>

  <back>
    <references>
      <name>References</name>
      <references>
        <name>Normative References</name>
      <reference anchor="RFC2119" target="https://www.rfc-editor.org/rfc/rfc2119">
        <front>
          <title>Key words for use in RFCs to Indicate Requirement Levels</title>
          <author fullname="Scott Bradner" initials="S." surname="Bradner"/>
          <date year="1997" month="March"/>
        </front>
        <seriesInfo name="BCP" value="14"/>
        <seriesInfo name="RFC" value="2119"/>
        <seriesInfo name="DOI" value="10.17487/RFC2119"/>
      </reference>
      <reference anchor="RFC8174" target="https://www.rfc-editor.org/info/rfc8174">
        <front>
          <title>Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words</title>
          <author fullname="Benjamin Leiba" initials="B." surname="Leiba"/>
          <date year="2017" month="May"/>
        </front>
        <seriesInfo name="BCP" value="14"/>
        <seriesInfo name="RFC" value="8174"/>
        <seriesInfo name="DOI" value="10.17487/RFC8174"/>
      </reference>
      <reference anchor="RFC8949" target="https://www.rfc-editor.org/rfc/rfc8949">
        <front>
          <title>Concise Binary Object Representation (CBOR)</title>
          <author fullname="Carsten Bormann" initials="C." surname="Bormann"/>
          <author fullname="Paul Hoffman" initials="P." surname="Hoffman"/>
          <date year="2020" month="December"/>
        </front>
        <seriesInfo name="STD" value="94"/>
        <seriesInfo name="RFC" value="8949"/>
        <seriesInfo name="DOI" value="10.17487/RFC8949"/>
      </reference>
      </references>

      <references>
        <name>Informative References</name>
      <reference anchor="RFC3161" target="https://www.rfc-editor.org/rfc/rfc3161">
        <front>
          <title>Internet X.509 Public Key Infrastructure Time-Stamp Protocol (TSP)</title>
          <author fullname="C. Adams"/>
          <author fullname="P. Cain"/>
          <author fullname="D. Pinkas"/>
          <author fullname="R. Zuccherato"/>
          <date year="2001" month="August"/>
        </front>
        <seriesInfo name="RFC" value="3161"/>
        <seriesInfo name="DOI" value="10.17487/RFC3161"/>
      </reference>
      <reference anchor="RFC7942" target="https://www.rfc-editor.org/rfc/rfc7942">
        <front>
          <title>Improving Awareness of Running Code: The Implementation Status Section</title>
          <author fullname="Y. Sheffer"/>
          <author fullname="A. Farrel"/>
          <date year="2016" month="July"/>
        </front>
        <seriesInfo name="BCP" value="205"/>
        <seriesInfo name="RFC" value="7942"/>
        <seriesInfo name="DOI" value="10.17487/RFC7942"/>
      </reference>
      <reference anchor="RFC8785" target="https://www.rfc-editor.org/rfc/rfc8785">
        <front>
          <title>JSON Canonicalization Scheme (JCS)</title>
          <author fullname="Anders Rundgren" initials="A." surname="Rundgren"/>
          <author fullname="B. Jordan"/>
          <author fullname="S. Erdtman"/>
          <date year="2020" month="June"/>
        </front>
        <seriesInfo name="RFC" value="8785"/>
        <seriesInfo name="DOI" value="10.17487/RFC8785"/>
      </reference>
      <reference anchor="SCITT" target="https://datatracker.ietf.org/doc/draft-ietf-scitt-architecture/">
        <front>
          <title>An Architecture for Trustworthy and Transparent Digital Supply Chains</title>
          <author fullname="Henk Birkholz" initials="H." surname="Birkholz"/>
          <author fullname="Antoine Delignat-Lavaud" initials="A." surname="Delignat-Lavaud"/>
          <author fullname="Cedric Fournet" initials="C." surname="Fournet"/>
          <author fullname="Yogesh Deshpande" initials="Y." surname="Deshpande"/>
          <author fullname="Steve Lasker" initials="S." surname="Lasker"/>
          <date year="2024"/>
        </front>
        <seriesInfo name="Internet-Draft" value="draft-ietf-scitt-architecture"/>
      </reference>
      <reference anchor="COSE-MERKLE" target="https://datatracker.ietf.org/doc/draft-ietf-cose-merkle-tree-proofs/">
        <front>
          <title>COSE Merkle Tree Proofs</title>
          <author fullname="Orie Steele" initials="O." surname="Steele"/>
          <author fullname="Henk Birkholz" initials="H." surname="Birkholz"/>
          <author fullname="Antoine Delignat-Lavaud" initials="A." surname="Delignat-Lavaud"/>
          <author fullname="Cedric Fournet" initials="C." surname="Fournet"/>
          <date year="2025"/>
        </front>
        <seriesInfo name="Internet-Draft" value="draft-ietf-cose-merkle-tree-proofs"/>
      </reference>
      <reference anchor="OTS" target="https://opentimestamps.org/">
        <front>
          <title>OpenTimestamps Protocol and Tooling</title>
          <author>
            <organization>OpenTimestamps Project</organization>
          </author>
          <date/>
        </front>
      </reference>
      </references>
    </references>

    <section numbered="true" toc="include" anchor="appendix-a">
      <name>Example Day Record (JSON Projection)</name>
      <t>
        This appendix shows a non-authoritative JSON projection of a day
        artifact. The authoritative artifact is the corresponding CBOR file.
      </t>
      <figure>
        <artwork type="json"><![CDATA[
{
  "version": 1,
  "site_id": "an-001",
  "date": "2025-10-07",
  "prev_day_root": "<genesis root hex>",
  "batches": [
    {
      "version": 1,
      "site_id": "an-001",
      "day": "2025-10-07",
      "batch_id": "an-001-2025-10-07-00",
      "merkle_root": "9d1f...c2",
      "count": 10,
      "leaf_hashes": [
        "01ab...",
        "7fe2..."
      ]
    }
  ],
  "day_root": "9d1f...c2"
}
]]></artwork>
      </figure>
    </section>

    <section numbered="true" toc="include" anchor="appendix-b">
      <name>Example Conformance Vector Bundle</name>
      <t>
        The values in this appendix were generated by the Python gateway code
        path and cross-checked against <tt>trackone_core._native</tt> (Rust)
        for deterministic CBOR and Merkle parity. This vector bundle reflects
        the <tt>0.1.0-alpha.6</tt> implementation snapshot.
      </t>
      <figure>
        <artwork type="ascii-art"><![CDATA[
commitment_profile_id:
  trackone-cbor-map-v1

fixture fact_a:
  device_id: pod-101
  timestamp: 2026-03-01T12:00:00Z
  nonce: ""
  payload.temp_c: 21.5

fixture fact_b:
  device_id: pod-102
  timestamp: 2026-03-01T12:10:00Z
  nonce: "n1"
  payload.temp_c: 22.0

fixture fact_c:
  device_id: pod-103
  timestamp: 2026-03-01T12:20:00Z
  nonce: "n2"
  payload.temp_c: 22.5

fixture fact_d:
  device_id: pod-104
  timestamp: 2026-03-01T12:30:00Z
  nonce: "n3"
  payload.temp_c: 23.0

empty-day-v1:
  artifact_sha256:
    c00c984fdd78476f1044fa52eae94606
    6f403460e6585044c39b125a13ee3d7e
  batch_merkle_root:
    e3b0c44298fc1c149afbf4c8996fb924
    27ae41e4649b934ca495991b7852b855
  day_root:
    e3b0c44298fc1c149afbf4c8996fb924
    27ae41e4649b934ca495991b7852b855

single-fact-v1:
  fact_cbor_hex:
    a4656e6f6e636560677061796c6f6164a16674656d705f63f94d606964657669
    63655f696467706f642d3130316974696d657374616d7074323032362d30332d
    30315431323a30303a30305a
  fact_leaf_hash:
    bb154e441ccdebec09969f1911b46394
    20f7830825b75b02ac52512aa5d32591

odd-leaf-layer-v1:
  artifact_sha256:
    6f81c6de96dc635ff29f73a60457205b
    a0874a97b2ad6f9f88b1f61870592825
  day_root:
    6c96b4f201e5f6f1badfef6c84d4003a
    b12a7034daeb20fa7f59c33f43c5ae18
  leaf_hashes_sorted:
    26e4affe56412f9e1d4323b27d3ca54c
    4add4fa971800bc25568c4b175d55581
    bb154e441ccdebec09969f1911b46394
    20f7830825b75b02ac52512aa5d32591
    e2003581ac4364cb322005c465c8d565
    e69f5578af1a614e2762c222a46fd7a5

power-of-two-v1:
  artifact_sha256:
    81cc87aaf2ecb8b7d9420faa910814aa
    47dd5c8b1ead76d2da19bef55afa48a8
  day_root:
    57bd26f73115f130dcf877a10c434ba2
    8686196daf81f5e48388833303600e73

duplicate-leaf-hash-v1:
  artifact_sha256:
    4fafb987ef0df50e5e382a09d140793a
    84180f4a86e67924eab1184e20a11c00
  day_root:
    9166c21933341729c08b3a1f61710d9d
    f5efc5aa00d3af9f596c2e166c65b54e

genesis-chain-v1:
  artifact_sha256:
    4fb6d4570d4662c63b682e2f2d993e9f
    a01669217b61ff64400b981b50b1a8c2
  day_root:
    bb154e441ccdebec09969f1911b46394
    20f7830825b75b02ac52512aa5d32591

non-genesis-chain-v1:
  artifact_sha256:
    8969bafb62ad9e9aaa6c8460a52320ba
    107975d06352d6562107c5070d792f7e
  day_root:
    e2003581ac4364cb322005c465c8d565
    e69f5578af1a614e2762c222a46fd7a5

class-a-bundle-v1:
  disclosure_class: A
  day_root:
    6c96b4f201e5f6f1badfef6c84d4003a
    b12a7034daeb20fa7f59c33f43c5ae18
  artifact_sha256:
    6f81c6de96dc635ff29f73a60457205b
    a0874a97b2ad6f9f88b1f61870592825
]]></artwork>
      </figure>
      <t>
        The full machine-readable vector set is maintained in the TrackOne
        repository alongside the reference implementation.
      </t>
    </section>

    <section numbered="false" toc="include" anchor="acknowledgments">
      <name>Acknowledgments</name>
      <t>
        Early structural drafts of this document were prepared with AI writing
        assistance. All technical content, design decisions, and normative
        requirements were reviewed against the TrackOne implementation.
      </t>
      <t>
        The author thanks the OpenTimestamps project for the public calendar
        infrastructure used during validation.
      </t>
    </section>
  </back>
</rfc>
