{"scope":"salon","collecting":true,"platform":"https://primer.tech/.well-known/reviews-network.json","salonId":"cmkphohtv0063hcit5786g1rl","algo":"sha256-canonicaljson-v6","invitationRule":"inv-v2","minVerifierVersion":"1.3.0","head":{"seq":2,"entryHash":"8afd54eb46d6cf2592e84c9f718b6f22e13f50096f5d470f94b5a92d4b29e633","createdAt":"2026-09-04T13:22:13.875Z"},"entries":2,"exportUrl":"https://www.salonidealestet.ro/api/public/reviews/log?salon=cmkphohtv0063hcit5786g1rl","policyUrl":"https://www.salonidealestet.ro/verified-reviews-policy","anchors":[],"anchorAge":{"firstAnchoredAt":null,"firstConfirmedAt":null,"anchoredDaysCount":0,"confirmedDaysCount":0,"pendingDaysCount":0},"anchorHonestyNote":"This register has no anchored days yet. Nothing has been submitted to an OpenTimestamps calendar, so nothing here is externally timestamped — only internally hash-linked. Trust here accumulates with time and is not declared.","funnel":{"invited":0,"responded":0,"published":0,"publishedFromInvitation":0,"publishedOrganic":0,"withdrawnReviews":0,"negativeFinal":0,"negativeSurviving":0},"keyAnchors":[{"seq":2,"kid":"primer-salon-2026-08-prod","publicKeyX":"xS_gLW0UVkSZLhkvJ-OmnvR7j87ZC2Rcu2S9mW9cXec","scope":"salon","custody":"platform","supersedesKid":null,"activatedAt":"2026-09-04T13:22:13.875Z"}],"keyAnchorsUnresolved":[],"endpoints":{"jwksUrl":"https://www.salonidealestet.ro/.well-known/jwks.json","anchorsUrl":"https://primer.tech/api/public/reviews/anchors","exportUrl":"https://www.salonidealestet.ro/api/public/reviews/log?salon=cmkphohtv0063hcit5786g1rl","networkUrl":"https://primer.tech/.well-known/reviews-network.json"},"verifier":{"npmPackage":"primer-verify","repoUrl":"https://github.com/iclaudiumihaila/primer-verify","specUrl":"https://github.com/iclaudiumihaila/primer-verify/blob/276b95a11c65f630513dd91f2c84f54c72704577/SPEC.md","specVersion":"1.3.0"},"claimSources":{"collecting":"platform","funnel":"platform"},"disclosure":{"derivedFrom":"append-only review ledger + immutable Review rows","editableInputs":"none","invitationPolicy":"Every customer who completes an appointment is invited to review that visit — neither the salon nor Primer chooses who is asked, because any selection would distort this register. The invitation is sent on behalf of the salon, on the legitimate interest of the salon, of Primer and of the public in a complete and unselected register (art. 6(1)(f) GDPR), together with the existing-customer exception in art. 12(2) of Romanian Law 506/2004. It carries no advertising of any kind, and it can be stopped for good, free of charge and in one step, from any message. ONE LIMIT IS STATED RATHER THAN HIDDEN: the invitation reaches the people whose contact details were collected through a booking door that showed them this notice and offered them the objection there, or who authorised messaging themselves. A contact collected before that door existed has not been invited and has not refused — the next booking through such a door brings them in. AND THE LEVERS THE SALON DOES HAVE ARE NAMED: it can put somebody on its own block list, it can relay an objection a customer states at its counter, and it can record a finished visit as an absence — a correction with no deadline, which ends an invitation already sent, is refused once a review has been written, and cannot be undone. All three only ever REMOVE a person, all three are counted on that salon's own register screen, the last one also raises a public signal in this register, and none of them lets a salon nominate who is invited — the only way a salon brings somebody into this population is by showing them this notice at a booking door. The right to object under art. 21 GDPR is carried in the first message and honoured without any balancing test. WHICH VISITS: every finished visit — closed at the till, closed with the finish button, or closed automatically at least 2 hours after its end time — unless it was cancelled or recorded as an absence, in which case no invitation is sent and any invitation already sent stops working. Who closed the visit decides nothing and is not a gate: this register publishes no closer for any single visit, and the source is recorded on the salon's own appointments screen. THIS RULE IS VERSIONED: it is inv-v2, declared as invitationRule beside these documents' hash rule, and disclosure.invitationRules publishes what every earlier version said and from which date this one applies — the previous rule invited only the visits a person had closed, and visits finished under it are not invited retroactively. At most one invitation every 30 days per person, at most 3 unanswered invitations ever, and never again after a STOP. ONE PERSON, ONE REVIEW: a customer whose review is still published at this salon is never invited again, however often they come back. There is no renewal invitation at all: it is switched off platform-wide, and no salon can switch it on. The gate is the published review, not the act of having written one — but a removal that is about the PERSON does not put them back in the pool: when a review is removed at the reviewer's own request, or under the right to erasure, the same narrow objection a STOP records is written for that person at that salon, so no further review invitation is ever sent to them there. Their booking confirmations and reminders are untouched — those are a different purpose. A removal on a ground about the CONTENT, such as a takedown or a court order, leaves the person's standing exactly as it was. These limits are platform-wide and identical for every salon — they are not configurable by a salon.","invitationUnit":"The unit of this policy is the PERSON: invited and responded in this document's funnel are counts of distinct people, one invitation per person per visit, and the limits stated in invitationPolicy are per person. A customer with three invited visits is one invited person, never three.","invitationRules":"The current invitation rule is inv-v2, and it is declared as invitationRule above. It says WHICH VISITS are asked about; invitationPolicy says which PEOPLE may be asked and on what legal ground, and the two are separate questions. What each rule changed: inv-v1 — only visits a PERSON closed were invited — at the till, or with the finish button. A visit nobody closed by hand, which the automatic close caught instead, was never asked about, so a register could shrink the population it published simply by not closing tickets. inv-v2 — every finished visit is invited — at the till or automatically, at least 2 hours after its end time, unless it was cancelled or recorded as an absence. Who closed the visit stops deciding anything: the source (completionSource) is no longer a gate, it is published for no single visit here, and it stays on the salon's own appointments screen. Applies to visits finished after 2026-09-01; visits finished before that date were governed by inv-v1 and are not invited retroactively. THE RULE FOR ONE VISIT IS THE RULE IN FORCE WHEN THAT VISIT FINISHED, never the rule this document declares — a register whose older entries were collected under inv-v1 is correct behaviour and not a fork, exactly as a chain that mixes hash rules is. AND ONE THING THAT IS NOT A RULE CHANGE: nothing here touches how an entry is hashed or what a payload contains, so the hash rule is unaffected and a reader should not look for a new one.","hashRules":"The current hash rule is sha256-canonicaljson-v6, and it is declared as algo above. Every rule a live chain may honestly carry is published here — sha256-canonicaljson-v2, sha256-canonicaljson-v3, sha256-canonicaljson-v4, sha256-canonicaljson-v5, sha256-canonicaljson-v6 — because THE ALGO GATE IS A MEMBERSHIP TEST, NOT EQUALITY WITH THE NEWEST RULE: a chain legitimately mixes rules, one per entry, and branding every pre-current entry unverifiable the day a new rule shipped would falsely unverify the honest back-catalogue. What each rule changed: sha256-canonicaljson-v2 — the base rule: sha256 over canonical JSON, with supersedesReviewId inside the ENTRY preimage so a reader can tell a corrected review from a hidden one from the export alone. sha256-canonicaljson-v3 — adds four economic flags to the PUBLISH/SUPERSEDE payload — paidVisit, paymentMethod, returningClient, visitCount. Present-as-null changes the canonical string, so the add could not be unconditional: that is why it is a new rule and not a field. sha256-canonicaljson-v4 — keeps those four flags but forces them present-as-null for an ANONYMOUS author, because an exact visit count re-identifies a hidden author on a small register. A v4 NAMED payload is byte-identical to a v3 one. sha256-canonicaljson-v5 — adds one field, synthetic (bool|null): true when the row was written by a declared seed path. Absent under every earlier rule, and absent means \"written before the flag existed\", never \"asserted genuine\". sha256-canonicaljson-v6 — adds one field, authorLanguage (str|null): the language the review was WRITTEN in, taken at the write door from the page the author actually read and validated against this platform's locale list. It exists because nothing in this register knew a review's language and the public structured data published one anyway — from the locale segment of whichever URL a page was fetched at, and from the salon's configured default — so one review could be published as several different languages at once. Absent under every earlier rule; null means NOT RECORDED, and neither ever means \"this review is in the salon's language\". Nothing else changes: sha256-canonicaljson-v6 is sha256-canonicaljson-v5 plus this key. NONE of the rules after the base one touches the ENTRY preimage — all of them change only the PUBLISH/SUPERSEDE payload the payloadHash is taken over, which is why an entry's link structure re-derives identically under every rule. The rule for ONE entry is the rule stamped ON that entry (its hashAlgo field), never the rule this document declares: a permalink showing an entry under an older rule inside a register declaring the newest one is correct behaviour and not a fork. AND ONE THING THAT IS NOT A RULE CHANGE: the funnel no longer publishing an absolute count of visits is a change to what these DOCUMENTS represent, not to how a ledger entry is hashed — no funnel field has ever entered an entry preimage or a payload, so there is no rule after sha256-canonicaljson-v6 and a reader should not look for one. AND ONE THING ABOUT THE TOOL THIS DOCUMENT NAMES: the published verifier release quoted in verifier above may predate the newest rule in the list above, and while it does it will report ALGO_UNKNOWN — \"I cannot check this\" — against the entries carrying that rule, never INVALID. Everything else about those entries still verifies: the chain links, the Merkle inclusion and the anchors are rule-independent. Read a mixed run that way rather than as a finding against the register, and check the repository for a release naming sha256-canonicaljson-v6. AND A SECOND THING THAT IS NOT A RULE CHANGE, on the same law one level down: NO REVIEW PUBLISHED FROM THIS BUILD ONWARD CARRIES A COUNT OF THAT PERSON'S VISITS. visitCount is still a field of the sha256-canonicaljson-v3/sha256-canonicaljson-v4/sha256-canonicaljson-v5/sha256-canonicaljson-v6 payload and is still hashed — the rule did not move, and no new rule exists — but this platform no longer derives or stores it, so a new entry commits visitCount as null for EVERY author, named as well as anonymous, exactly as the sha256-canonicaljson-v4 coarsening already committed it for anonymous ones. Entries published BEFORE this build keep the integer they committed to and re-derive byte-for-byte: the payload is rebuilt from the review row, so changing what an old entry publishes would have broken every proof already anchored — which is why this is a change to what is WRITTEN and not to how anything is hashed. A null therefore means \"this platform does not publish a person's visit count\", never \"this customer had no visits\", and a mixed register — old entries with a number, new ones with null — is correct and expected. AND THE ENTRY RULE ITSELF, so a reader need not leave this document to recompute one: entryHash = sha256(canonicalJson({algo, seq, salonId, reviewId, kind, payloadHash, prevHash, supersedesReviewId, createdAt})) — those fields, that order, nothing else. Two shapes an independent reimplementation gets wrong, named here because they were measured rather than imagined: the preimage key is algo, while the EXPORTED entry names the same value hashAlgo — one value, two field names, and a verifier threads the exported hashAlgo back in as algo. And salonId is INSIDE the preimage but is NOT a field of the exported entry: it comes from the register's own address, the salon the export was fetched for, which is exactly what binds an entry to its register.","economicFlags":"WHAT THIS REGISTER SAYS ABOUT MONEY, AND WHAT IT DELIBERATELY DOES NOT. A review payload carries paidVisit and paymentMethod under sha256-canonicaljson-v3, sha256-canonicaljson-v4, sha256-canonicaljson-v5 and sha256-canonicaljson-v6, and from this build onward this platform writes them as follows. paidVisit IS TRUE OR ABSENT, NEVER FALSE: true when the visit was closed through Primer's own till, null in every other case. A null therefore means UNDECLARED — this platform does not know how the visit was settled — and it NEVER means \"unpaid\": the salon may have taken the money at its counter, on paper, or on a device this platform never sees, and the register cannot tell those apart. WHAT THIS REMOVES AND WHAT IT DOES NOT, SAID PLAINLY BECAUSE A READER CAN CHECK IT: the absence is the EXACT COMPLEMENT of the positive — true when the till saw the visit, null in every other case, by rule and with no exception, A SNAPSHOT TAKEN AT THE PUBLISH INSTANT AND FROZEN THERE rather than a standing fact about the visit: a till transaction created after the review was published leaves the entry null, and one voided afterwards leaves it true — so THE ABSENCES CAN BE COUNTED, and this document does not pretend otherwise. Over the entries of NAMED authors (an anonymous author's economic fields are coarsened to null under sha256-canonicaljson-v4, sha256-canonicaljson-v5 and sha256-canonicaljson-v6 whatever the row holds, so those entries carry no signal either way; under sha256-canonicaljson-v3 they are NOT coarsened, so an anonymous entry of that generation publishes the row's own values) such a count recovers ONE QUANTITY PER GROUPING THE PAYLOAD SUPPORTS — over the whole register, or per day, per stylist, per service, per location, per rating, because every one of those travels in the same payload beside the flag: the share of the visits REVIEWED IN THIS REGISTER that were not settled through Primer's own till. That is NOT a fiscalisation rate and must not be published as one — a salon may ring a visit up on a register this platform never sees, and only reviewed visits appear here at all, so the figure is silent about every visit nobody reviewed. What changed for paidVisit is therefore the ASSERTION, not the arithmetic: this platform no longer states, in signed bytes beside a named customer, that one particular visit did not go through a till. paymentMethod IS NULL ON EVERY REVIEW PUBLISHED FROM THIS BUILD, for every author, named as well as anonymous: how one business's takings divide between cash and card is that business's own affair, and no reader of one review needs it to judge the review. It is no longer derived at all — the till is asked whether a transaction exists, never which one or how it was paid. UNLIKE paidVisit THAT ONE IS A TOTAL REDUCTION: for the reviews published from this build the cash/card mix cannot be recovered from anything this register publishes, by counting or otherwise. THIS IS NOT A RULE CHANGE: both keys still exist and are still hashed, the rule did not move, and there is no rule after sha256-canonicaljson-v6 that touches THESE TWO FIELDS — only what this platform WRITES changed. Entries published BEFORE this build keep the values they committed to and re-derive byte-for-byte, because a payload is rebuilt from the review row and changing what an old entry publishes would break every proof already anchored. A register that mixes older entries naming a method with newer ones that do not is correct and expected. Both flags remain PLATFORM ASSERTIONS either way: this export carries no POSTransaction rows, so a third party can check that the value we committed to is the value we hash, never that we classified the visit honestly.","synthetic":"Each review published under hash rule sha256-canonicaljson-v5 or sha256-canonicaljson-v6 carries a 'synthetic' field in its signed payload: true when the row was written by a declared seed path, false on a real write. A production register can never carry true — the write door refuses a declared-synthetic write against a production tenant before the transaction. The field is ABSENT on every entry published under an earlier rule (v2, v3, v4), and absent means 'written before the flag existed', NEVER 'asserted genuine': back-filling it would rewrite payloads already committed and already anchored. It is a platform assertion, not a third-party-verifiable fact — you can verify that the value we committed to is the value we hash, not that we classified it honestly.","authorLanguage":"WHAT LANGUAGE A REVIEW IS IN, AND WHEN THIS REGISTER SAYS SO. Each review published under hash rule sha256-canonicaljson-v6 carries an authorLanguage field in its signed payload: a primary language subtag (en, ro, fr, hu, de, es) recorded AT THE WRITE DOOR from the page the author actually read and validated there, or null. The field is ABSENT on every entry published under an earlier rule (sha256-canonicaljson-v2, sha256-canonicaljson-v3, sha256-canonicaljson-v4, sha256-canonicaljson-v5) and it is never back-filled, because those rows genuinely do not know and a filled-in guess would rewrite payloads already committed and already anchored. ABSENT MEANS \"written before the field existed\"; NULL MEANS \"not recorded at that door\". NEITHER MEANS \"this review is in the salon's language\" — and that guess is exactly what this field replaces: before it, the machine-readable inLanguage on this platform's public pages was taken from the locale segment of whichever URL a page was fetched at, and from the salon's own configured language, so one review could be published as several different languages at the same time. FROM THIS BUILD, NO PUBLIC SURFACE PUBLISHES A LANGUAGE FOR A REVIEW THAT DOES NOT CARRY ONE: the key is omitted rather than guessed, and an omission is the honest reading. Like the economic flags and synthetic it is a PLATFORM ASSERTION, not a recomputable fact — nobody can check from a public export what language a human wrote in; what a reader can check is that the value we committed to is the value we hash. It is also NOT a claim about the review TEXT being translated: review text is never translated anywhere on this platform, in any direction.","signing":"This document is signed by a key held and operated by Primer, not by the salon. What the signature proves is that Primer served these exact bytes for this exact origin — never that their contents are true. The key's fingerprint is written into this salon's own append-only chain as a KEY_ANCHOR entry, so a republication under a different key is detectable rather than invisible.","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.","signatureAbsence":"When this document cannot be signed it is served UNSIGNED and says so (signature: null, signatureUnavailable), never withheld and never faked. The reason is machine-readable and names the actual gap: NO_SIGNING_KEY_CONFIGURED means this deployment has no key at all; SIGNING_KID_NOT_CONFIGURED means it has one but no key id to publish it under; SIGNING_KEY_INVALID means the configured key is not base64(PKCS#8 DER) of an Ed25519 key. Three different things to go and fix, so they are three different codes. In every case the chain, the counters and the anchors below are unaffected and remain fully verifiable from the public export; only the origin binding is missing.","funnel":"funnel is the collection pipeline — invited, responded, published (broken out as publishedFromInvitation and publishedOrganic, which sum to published), withdrawnReviews, and the negativeFinal/negativeSurviving pair. THE UNITS ARE NOT ALL THE SAME, and mixing them without saying so is how a response rate quietly understates itself as a salon's clientele becomes more loyal: invited and responded are counts of DISTINCT PEOPLE, not of visits and not of messages — a customer with three invited visits is one invited person, so the response rate is people over people and cannot exceed one. published, publishedFromInvitation, publishedOrganic and withdrawnReviews are counts of REVIEWS. NO ABSOLUTE COUNT OF VISITS IS PUBLISHED HERE ANY MORE: this funnel used to open with a count of completed visits, and that figure is gone from every public document as of this build — its absence means \"this build no longer publishes it\", never \"this register has none\". It is still computed, and it is still on the salon's own register screen, where the three anti-manipulation gaps beside it are legible to the people who can act on them. Every figure that remains is a PLATFORM ASSERTION: it is computed over Appointment and Review rows this export deliberately does not carry (the export publishes hashes only, so one erasure has one place to chase), so a third party cannot recompute it from public data. It is published labelled, and it is the figure that would embarrass us if we were selecting: an invited population far larger than the responded one stands out here. The published split exists because published is NOT a subset of invited and never was: the organic door (a recognised device on the salon's own site) and legacy rows publish a review without an invitation ever sent, so publishedFromInvitation counts the invitation-door reviews and publishedOrganic the rest — reading published ⊄ invited as a red flag is reading the shape, not the numbers. negativeFinal and negativeSurviving are published as a PAIR so a reader can read \"two of two negatives still standing\" off two integers without trusting a rate they cannot recompute."},"documentOrigin":"https://www.salonidealestet.ro","signedAt":"2026-09-04T16:53:26.724Z","signedBy":{"scope":"salon","custody":"platform","kid":"primer-salon-2026-08-prod"},"signature":{"alg":"EdDSA","kid":"primer-salon-2026-08-prod","origin":"https://www.salonidealestet.ro","signedAt":"2026-09-04T16:53:26.724Z","canonicalization":"sha256-canonicaljson-v2","sig":"NhDaUCdU02Rop5eT1PbdiG_1L43CqT8w3weF8M98WBLppYwWnnA-RSLJ28AGkw-dExJ7RTuRYLnqMlrlf5keDQ"}}