{"scope":"anchors","day":"2026-10-16","merkleRoot":"7ea3bcc08745310c0789f071c058029f2858ab172943866b74c933520ce96ce9","leafCount":6,"leavesStored":6,"leaves":[{"salonId":"cmj2w4mlb0004fti9apf9cftw","seq":405,"entryHash":"0d72d6d67514743cd2cbb3c5b5fe81264922a97d1a825ad62f487fc44a04d20f","leafHash":"36436e3a0c7c2377320d4bccd4b9295053e512e63e335b4bbfcb8b02357623aa"},{"salonId":"cmkphohtv0063hcit5786g1rl","seq":144,"entryHash":"e094cf2db5e2ff8ade1463fc231a8ff53103b099fb6724a302e7eeae2f1c8219","leafHash":"ab143e75e80e6db9a2b1514a87b049c3780fdc251e302a6a51907220a5aa1d03"},{"salonId":"cmmgouon00k01gpwkj57yah3i","seq":205,"entryHash":"2525ca0b2a03f3ba859b02e2013c4d183f452a0c68434b311e1e1092a2e3acf3","leafHash":"12a101a7cf07ea16c03fde3f4b87b4961984c233a31b691737f9db6e487b08f4"},{"salonId":"commit:v1:cmj2w4mlb0004fti9apf9cftw","seq":41,"entryHash":"3514266f50660439dd08b2d2a8c34f28757941c95ce20c9dd1e8d152ba33843f","leafHash":"ee6b8aae41070347e7ccb26d66fc0a4bde5fd226fef230e335eb168ab26e4eb8"},{"salonId":"commit:v1:cmkphohtv0063hcit5786g1rl","seq":40,"entryHash":"e1c0fe5bd7754370eab5517dee6ed1eeca4e2363daaf7ff5895656f04becb623","leafHash":"6fc2b419a9782f8e75a7f6a8d90a2d1fd8b1eae42b2a7f9120ad3b2d3d8e3325"},{"salonId":"commit:v1:cmmgouon00k01gpwkj57yah3i","seq":39,"entryHash":"6825f3628d374f6d685905c27277980aec30259391fa8f745014206f64e20801","leafHash":"baabc08db7bd98c45168f853650eb6a098ff6214a0591620979c53de12c5a1b6"}],"leavesOmitted":false,"leafRule":"sha256(0x00 || canonicalJson({entryHash, salonId, seq}))","nodeRule":"sha256(0x01 || left || right); an odd node is PROMOTED to the next level, never duplicated (CVE-2012-2459); leaves sorted by salonId ascending","anchor":{"status":"CONFIRMED","calendar":"https://a.pool.opentimestamps.org","submittedAt":"2026-10-17T03:20:01.157Z","confirmedAt":"2026-10-17T15:40:00.058Z","bitcoinBlockHeight":971800,"bitcoinBlockHash":"000000000000000000018f8688983b23328129fe1758560bf5e838fe215381cf","bitcoinBlockMerkleRoot":"de21d2fff48fbdd76ff70dbee480f3d8720e06a5cbaca3b7ef8ecc441ef14c8a","otsUrl":"https://primer.tech/api/public/reviews/anchors/2026-10-16/ots","otsBytes":1169,"otsSha256":"a0edbf20c712adc4a5924f9f38c9df0cb19df978b2e43549c5d923fcd3ad4d03"},"endpoints":{"jwksUrl":"https://primer.tech/.well-known/jwks.json","anchorsUrl":"https://primer.tech/api/public/reviews/anchors","networkUrl":"https://primer.tech/.well-known/reviews-network.json"},"claimSources":{"leavesStored":"platform"},"disclosure":{"anchor":"Daily Merkle root over the per-salon chain heads, submitted to public OpenTimestamps calendars. SUBMITTED means a calendar accepted the digest; it is NOT a Bitcoin confirmation. CONFIRMED means the proof carries a Bitcoin attestation naming a block height. Note for anyone checking by hand: GET <calendar>/timestamp/<digest> returns 404 for genuine digests — the calendar indexes the commitment at the pending attestation, not the file digest — so verification is done from the proof, never by re-querying the calendar. TWO CALENDARS, TWO ROLES, because one word is doing two jobs: the calendar field published beside this note is the SUBMISSION endpoint the digest was handed to, which is a POOL address; the ATTESTING calendar is the one named inside the .ots proof at its pending attestation, and it is routinely a different host, because a pool answers from one of its members. A verifier that finds the two disagreeing has found an ordinary hand-off, not a contradiction. We publish the submission endpoint because that is the fact we hold; the attesting calendar is READ OUT OF THE PROOF, which is the checkable artefact and is served at the otsUrl beside this block. Neither name is evidence on its own: what a proof commits to is a Bitcoin block, never a calendar. WHEN A NEW ENTRY GETS INTO A PROOF, because everything above describes a day that is already anchored: anchoring runs once a night over the day that has closed, so an entry written today is anchored the following night and until then sits in a live tail no proof covers yet. Reaching CONFIRMED is a different event on a schedule of its own and can happen at any hour of the day; it moves an existing head from SUBMITTED to CONFIRMED and never brings a new entry into a proof.","anchorTimestamps":"submittedAt and confirmedAt are OUR record of when the digest was handed to a calendar and when the Bitcoin attestation was first read out of the proof. They are platform assertions: an OpenTimestamps proof commits to a block, never to our clock, so nothing in the proof can corroborate either instant. What the proof DOES state — the block height and the merkle root it commits to — is published beside them and is checkable. The calendar is the one word here that names two different things, so both are labelled: the calendar field beside this block is the SUBMISSION pool the digest was handed to, OUR record like the two instants; the ATTESTING calendar is named inside the proof at its pending attestation, may be a different host, and is read out of the proof rather than from any field we publish.","signingKeySet":"This document is signed with the PLATFORM key on every origin it is served from, including a salon's own domain — a network-level fact does not become a salon's claim by being served there. So endpoints.jwksUrl points at the PLATFORM key set, which is the one that can check this signature; a salon origin's own /.well-known/jwks.json publishes the SALON key set and would answer UNKNOWN_KID for the kid below. The authoritative copy of this document, and the one a verifier should use, is the platform's: documentOrigin is inside the signed bytes, so the two origins are two different documents by design.","bitcoinBlockHash":"bitcoinBlockHash is recorded ONLY when a public block explorer corroborated the block height read out of the proof. NULL means \"not corroborated\", never \"no block\" — the proof itself is the evidence, the explorer is a second opinion, and a third party being unreachable is not evidence in either direction.","bitcoinBlockMerkleRoot":"bitcoinBlockMerkleRoot is the value the PROOF commits to, in OpenTimestamps internal byte order. Block explorers print it REVERSED. Reverse it byte by byte before comparing, or the comparison fails on a genuine proof.","leafCountVsLeavesStored":"leafCount is what the head row declared when it was written and is frozen at the database; leavesStored is how many leaf rows are on disk right now, counted over ALL of them and never capped. Both are published so a disagreement is visible to a reader rather than only to us. A THIRD number can differ from both: leaves[] is what this response publishes, and it stops at NETWORK_SALON_SCAN_LIMIT — so leaves.length < leafCount is a publication cap, while leavesStored != leafCount is a disagreement in the database.","leafSeq":"Each leaf carries seq: that salon's cumulative ledger position on the anchored day. It is REQUIRED — the leaf preimage is sha256(0x00 || canonicalJson({entryHash, salonId, seq})), so without it nobody outside this database could recompute the root published above. What it discloses, stated rather than glossed: for any salonId in the set, seq(day B) minus seq(day A) is that register's exact number of ledger entries between two anchored days — reviews, replies, response edits and withdrawals, removals and key anchors, added together. That is readable by anyone, for every salon in the set, on every origin this document is served from, including a competitor's. ONE CLASS OF LEAF IS DIFFERENT, AND THE SENTENCE ABOVE WOULD BE FALSE ABOUT IT: a leaf whose salonId begins commit:v1: is a DAILY BOOKING-POPULATION COMMITMENT, not a ledger head, and its seq counts CALENDAR DAYS — it advances by exactly one every day, on every salon, whatever happened that day. Subtracting two of those gives a number of days and nothing else; that is the whole reason the commitment lives on its own chain instead of on the register's. That class is also the one exception to the anchoring latency described above: a commitment entry is written once at the nightly seal and is immutable from that instant, so it rides the SAME night's root rather than the following one, while a ledger entry written during the day waits for the night after it. THE CONSEQUENCE, SAID RATHER THAN LEFT TO BE NOTICED: the day field on this head is the closed day the LEDGER half was cut at, and a commitment leaf riding that head is dated the day AFTER it. So do not read a head's day as the day of everything on it; read each commitment entry's own day, which is inside its entryHash. What NEITHER class discloses: review text, ratings, which KIND of entry moved the counter, and any customer, staff or appointment identity."},"documentOrigin":"https://primer.tech","signedAt":"2026-10-17T22:01:05.392Z","signedBy":{"scope":"platform","custody":"platform","kid":"primer-network-2026-08-prod"},"signature":{"alg":"EdDSA","kid":"primer-network-2026-08-prod","origin":"https://primer.tech","signedAt":"2026-10-17T22:01:05.392Z","canonicalization":"sha256-canonicaljson-v2","sig":"2pQdG5ej0yrBXiiiN8Ah_ObtfCrCpp24bti4Q1aK0AfF5UHmSPrI3vxs0kCSS-bIpyV5ndphekRSz9TfidT5Bg"}}