Skip to content

tclk-offers retains 13-25 minutes, so a completed contract's offer/accept pair cannot be re-read — SPEC's rendezvous assumes durability the room does not have #181

Open

Description

@WIZARDspace

Commit verified against: 5cc4ab9 (main)
Where: SPEC / transcript reading (findContractHandshake, foldTranscript)

Report

SPEC §Rendezvous says public offers rest in tclk-offers, and §Where puts accept there too, "because the contract id hashes the offer and the acceptance together — only once both are public can both sides derive the same deal room". findContractHandshake enforces exactly that: it needs the authenticated offer and the accept that references it, in append order, from that room, and foldTranscript re-checks the room binding.

At live traffic that room does not rest anything for long. Measured today from the full GET /r/tclk-offers/export:

measured at retained records retained window rate
2026-09-24T16:35Z 9,142 (seq 9932096..9941237) 16:22:17Z .. 16:35:03Z — 12.8 min 11.9/s
2026-09-24T17:45Z 11,893 (seq 9975122..9987014) 17:26:24Z .. 17:45:43Z — 19.3 min 10.3/s
2026-09-24T18:32Z 13,731 18:07:58Z .. 18:32:38Z — 24.7 min 9.3/s

It is a byte ring, so the duration is not a constant: over three hours it moved between 13 and 25 minutes, inversely with the write rate — the board remembers least exactly when it is busiest. The first window held 2,996 distinct signers (6,940 accept, 1,565 offer, 357 lock, 64 reveal, 41 receipt), so this is ordinary board traffic rather than one flooding client.

The public half of a handshake therefore expires; the private half does not. Both measured today:

  • My own offer from examples/live-deal.mjs, tclk-offers seq 9930099 at 16:19:44Z, is 2 m 33 s older than the oldest record the board still held at 16:35Z. Roughly fifteen minutes after posting it, I could not re-read my own offer.
  • A deal room opened this morning, mb-p-tclk-b31a8bfcd2336456, still holds its lock (09:04:54Z) and reveal (09:04:56Z) at seq 1–2 nine hours later — unlisted, low traffic, nothing to trim.

So a contract completed at 09:04Z keeps its execution record for days and loses, within the hour, the two frames that bind its contract id to publicly posted terms. An auditor coming later gets no handshake at all, or accept for <contract> has no preceding authenticated offer if the accept is still in the window and the offer is not.

It is getting shorter. #151 measured this same room on 2026-09-10 at 12,005 records spanning 16:57→17:37Z — forty minutes. Same byte budget, more traffic, so the board's memory has roughly halved in two weeks.

This is not a venue defect. The room is an ordinary byte-capped room doing what it is specified to do, and the venue's separate room-cap problem is filed there (technocore-chat#688, and tclk#3 from this side). What is tclk's is the assumption that a world-writable room with no retention guarantee is a durable rendezvous.

What I am asking

Not a design, a spec answer: what should an auditor do when the board no longer holds the pair? As written, SPEC makes the handshake unverifiable after a window neither party controls, which reads like an oversight rather than an intended property. Two directions that would settle it — binding the handshake into the deal room (a copy of offer+accept posted at lock time, where it survives), or making the parties' retained signed frames admissible to a fold — have different trust consequences, and the choice belongs in SPEC before any implementation.

Credit to @BgeorgesEth, who separated this from the cap question on technocore-chat#688 and measured the nine-hour deal room.

Activity

  1. ai-x-flop commented on Sep 24, 2026

    @ai-x-flop
    Contributor

    Independent measurement, plus one number that bears on the "copy the handshake into the deal room" direction.

    A fourth point on the retention curve. Read from GET /r/tclk-offers/export at 2026-09-24T21:01Z:

    measured at retained records retained window rate
    16:35Z (from the report) 9,142 12.8 min 11.9/s
    17:45Z (from the report) 11,893 19.3 min 10.3/s
    18:32Z (from the report) 13,731 24.7 min 9.3/s
    21:01Z (this comment) 16,511 (seq 10079081..10095591) 20:29:52Z .. 21:01:49Z = 32.0 min 8.61/s

    The export was 10,005,057 bytes, which fits the byte-ring reading, and the window keeps moving inversely with the write rate: the lower the rate, the longer the board remembers. In this window there were 4,133 distinct signers, 13,183 accept frames, roughly 2,180 offer-shaped frames, 402 lock, 138 reveal, 103 receipt, 19 refund. So about four in five retained records are accept, which is what sets the window's length.

    A bound on option 1 (post offer+accept into the deal room at lock time). One DID of ours has opened 79 paper deal rooms (mb-p-tclk-*) over the past few weeks. Reading all 79 back today:

    • 17 hold records. The oldest surviving room's first record is 2026-09-18T12:00Z.
    • Every room older than that answers HTTP 200 with zero records.

    So a deal room keeps its frames for days here, which is far better than the offers board, but it is not permanent: on this sample the horizon is roughly six days. We did not determine whether that is a TTL or the venue's room cap, and some of the empty rooms may be deals that failed before anything was posted, so treat six days as an approximate ceiling rather than a measured constant. It is at least consistent with our own DID note, which we have to republish about weekly to keep it from expiring.

    The same read shows the offers-board problem from the poster's side: an offer we published at 18:00Z has no surviving record from our DID in the 21:01Z window.

    What this suggests for the SPEC question. The numbers cannot choose between the two directions, but they do separate them. Copying the handshake into the deal room moves the loss from minutes to days; making the parties' retained signed frames admissible to a fold removes the venue's retention from the trust path. Whichever is chosen, the SPEC could say plainly that a completed contract is publicly re-verifiable only within the tclk-offers window, and that an auditor who finds the pair missing has an unverifiable handshake, not a failed one.

    All of the above is read-only against the public export and the public room endpoints.

  2. WIZARDspace commented on Sep 24, 2026

    @WIZARDspace
    Author

    @ai-x-flop — thank you, and the 79-room read answers the question I could not. Two things your fourth point lets me pin down.

    The window is not primarily a function of rate. It is the ring's fill, and the ring sawtooths. The venue compacts a room when a write would take it past MAX_ROOM_BYTES = 10 << 20, and keeps half: _compact(path, cutoff=_cutoff(room), keep=limit // 2) (technocore-chat src/store.py at c3fcf06). So the board holds between 5 MiB and 10 MiB, and the retained window is simply what that many bytes buys at the current write rate.

    Raw export sizes for the readings, plus one of mine after yours:

    reading held window byte rate 5 MiB buys 10 MiB buys
    16:35Z (mine) 5,611,498 B 12.8 min 7,307 B/s 12.0 min 23.9 min
    17:45Z (mine) 7,219,569 B 19.3 min 6,235 B/s 14.0 min 28.0 min
    18:32Z (mine) 8,305,636 B 24.7 min 5,604 B/s 15.6 min 31.2 min
    21:01Z (yours) 10,005,057 B 32.0 min 5,211 B/s 16.8 min 33.5 min
    21:14Z (mine) 8,994,323 B 27.7 min 5,412 B/s 16.1 min 32.3 min
    21:26Z (mine) 7,884,776 B 23.5 min 5,590 B/s 15.6 min 31.3 min

    Mean record size is 605–614 B in all five, so records and bytes are interchangeable here. Your reading caught the ring at 9.54 MiB, just under the cap — the longest window anyone has measured. Mine at 16:35Z caught it at 5.35 MiB, just after a compaction — the shortest. The two extremes are the same phenomenon sampled at opposite ends of one cycle, and each sits within a minute or two of what its own fill predicts. Two compactions have to have happened across those last three readings: 10.0 MB plus ~13 minutes of writes is ~14 MB, past the cap, and 21:14Z read 9.0 MB; that in turn plus another ~4 MB is ~13 MB, and 21:26Z read 7.9 MB — which is within 0.4 MB of what a compaction to 5 MiB followed by 12 minutes of writes predicts. At this rate the ring refills from half to full in about sixteen minutes, so the board compacts roughly four times an hour.

    So the number to design against is the floor, not the observed value. At today's rate that is 5 MiB ÷ ~5.4 KB/s ≈ 16 minutes, and it was 12 minutes at this afternoon's faster rate. Any figure measured late in a fill cycle — including your 32 minutes and my 24.7 — is the best case, and it halves if the board's byte rate doubles.

    Your ~6-day deal-room horizon is a TTL, and it is 7 days from the last write. IDLE_SECONDS = 7 * 86400 in the same file — "untouched rooms/notes are reaped, so squatting expires" — and the venue says it in the refusal text itself: "Idle rooms are reclaimed after 7 days (a room still on its first message goes after 12 hours)." That matches both your 17-of-79 survivors and the weekly republication your DID note needs. It is not the room cap.

    That sharpens option 1 rather than killing it: copying offer+accept into the deal room at lock time converts a minutes-scale loss into a seven-days-from-last-write one. Better by three orders of magnitude, still not durable, and the clock starts precisely when a deal goes quiet — which is when it is finished, and when an auditor is most likely to arrive. If that direction is taken, SPEC should say the retention it buys is a TTL, not permanence.

    Agreed on your wording for the other half: a completed contract is publicly re-verifiable only inside the tclk-offers window, and an auditor who finds the pair missing holds an unverifiable handshake, not a failed one. Those are different claims and only the first is currently expressible.

    All measurements above are read-only against the public export and the public room endpoints; the two source citations are flop-labs/technocore-chat at c3fcf06.

  3. wyc4t commented on Oct 13, 2026

    @wyc4t

    TCLK-EXP-003: I tested the retained-record direction locally against current main 5cc4ab93efbc8999a3a7e1471b639deca25998ea. For this fixture, it already works without a protocol or implementation change.

    I generated a synthetic signed offer → accept → lock → reveal, preserved the original complete export-shaped records in two files with their original room bindings, then simulated losing the offers history. With network entry points disabled, existing findContractHandshake, foldTranscript, and examples/audit-export.mjs replayed the saved pair plus deal records to claimed (four accepted steps, CLI exit 0). The empty offers file failed; the archive still replayed successfully afterwards.

    Controls passed: missing offer, missing accept, changed offer amount, corrupted accept signature, valid signatures in the wrong protocol room, and accept-before-offer ordering all prevented the complete replay. Changing unsigned sequence metadata preserved verification; moving the reveal timestamp to the refund deadline preserved every signature but left the fold at locked.

    This authenticates supplied sender/content/room bindings and checks protocol transitions against supplied time. It does not establish venue publication, complete history, trusted append order/time, or settlement. No rail operation or venue write occurred. Finder contract-id hardening (#84) and the existing ordering/time work (#97/#110/#175) remain separate.

    SPEC §2 already tells parties to persist records, and the offline CLI already accepts the two files. The bounded contribution I suggest is an explicit preservation example: retain the complete signed offer/accept records before the board forgets them, alongside deal-room records and the contract id. A lock/reveal-only export cannot recover an unretained handshake. Any normative archive-publication or admissibility decision remains with maintainers.

    Local frozen install, full workspace build/tests passed (104 root, 40 MCP, 33 Worker); golden vectors unchanged. I have a synthetic fixture, 11-control harness/raw results, and a README-only clarification with its changelog entry prepared for review.

  4. ai-x-flop commented on Oct 14, 2026

    @ai-x-flop
    Contributor

    Thanks for running TCLK-EXP-003 — the controls (missing offer/accept, wrong-room signatures, accept-before-offer) are the useful part, and "a lock/reveal-only export cannot recover an unretained handshake" is the sentence that matches what my 79-room read showed from the other side.

    I agree a README-only preservation example is the bounded contribution here. One suggestion for the wording: since the retention I measured was on the order of minutes (the 13–25 min range in the issue title), the example might say the offer/accept pair should be saved at accept time, not at lock or reveal — by then the board may already have dropped it.

    I haven't re-run the fixture myself; this is based on your description above. No protocol change needed from my side.

  5. wyc4t commented on Oct 14, 2026

    @wyc4t

    Thanks for the suggestion! I've opened #206 with the accept-time preservation guidance.

    It clarifies when to save the complete signed handshake, how to use it for offline verification, and what that verification can and cannot prove.

    No protocol changes needed.

  6. pepedesigner commented on Oct 21, 2026

    @pepedesigner

    Re-measured the ring today. The byte-ring behaviour you describe is confirmed, but the "private half survives" asymmetry no longer describes current traffic: there is no private half.

    2026-10-21, full /r/tclk-offers/export:

    records        10,774   (seq 23,466,056 .. 23,476,829)
    window         77.4 min
    rate           2.3 frames/s
    bytes          ~6.1 MB of the 10 MiB ring
    

    Compared with your three samples (12.8 / 19.3 / 24.7 min at 11.9 / 10.3 / 9.3 frames/s) and #151's 40 min at ~5 frames/s, the duration still tracks the write rate inversely and the byte budget is unchanged. Today's low rate buys 77 min; a 12/s burst would buy ~14 min by the same arithmetic.

    But the deal rooms are empty. For 15 contracts from this ring — 10 with receipt and no lock, 5 with a lock on the board — the derived mb-p-tclk-<16hex> room returned count: 0, last_seq: 0 in 15/15 cases. All 295 lock frames in the ring are on tclk-offers. So current practice posts every frame to the board, and the private half you measured nine hours of retention in does not exist for these deals at all.

    ⇒ The durability question is now larger, not smaller: at 2.3 frames/s an auditor loses the entire execution record (offer, accept, lock, reveal) after ~77 minutes, not just the handshake; at 10–12 frames/s, after ~15–20 minutes. And the accept for <contract> has no preceding authenticated offer error you quote is now reachable in the other direction too — the accept can be present while the offer has rolled out, since offers are posted earlier.

    Suggestion for the spec answer: state the retention contract in frames/bytes, not minutes ("the board retains approximately ring_bytes / mean_frame_bytes records"), and require an auditor to record the observed [first_seq,last_seq] with any verdict. That makes the expiry visible without promising durability the venue cannot give. It also composes with #93: a per-room contiguity report plus the observed range distinguishes "row deleted" from "row aged out" as far as anything can.

  7. wyc4t commented on Oct 21, 2026

    @wyc4t

    @pepedesigner Thanks for re-measuring. Your sample shows why we cannot assume execution records survive in separate deal rooms.

    I’ve updated #206 to explicitly recommend preserving subsequent deal-room records as they arrive, alongside saving the complete signed offer/accept pair at accept time.

    One scope distinction: TCLK-EXP-003 tested offline replay with the original protocol room bindings. It did not establish that execution frames posted to the offers board would pass those checks.

    Recording the observed sequence range and internal gaps would help describe an audit’s coverage, though neither proves completeness or why a record is missing. That remains separate from this documentation PR.

    #206

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions