Split the preference platform documentation by feature, with the load process diagram - #238
Draft
jwrosewell wants to merge 30 commits into
Draft
jwrosewell wants to merge 30 commits into
jwrosewell wants to merge 30 commits into
Conversation
The Preference Management Platform was one 83 line page that described an integration where the client script is loaded from the action URL on every answer. That is no longer how it works, so the page becomes a section with one page per feature. src/identifiers/pmp/index.md keeps the Identifiers_PMP page id, so every existing link still resolves, and carries the load process diagram as a mermaid sequence with the browser, the page, the loader tag, the bundle, the cloud and the client script. Beside it are integration, preferences, sharing, IsGdpr, consent management platform wiring, configuration and privacy notice wording. What is new in the behaviour these pages describe: - The platform adds the client script when the page carries none, using the cloud and the resource key it already holds, and logs that it did. A publisher who writes the tag themselves keeps control of its parameters. - data-object-name is optional and fod is used when it is absent. - The client script gathers the answer itself, so the action URL is a hook of the publisher's own rather than the way the script is loaded. - Nothing waits between the two cards, and the third party cookie result comes from the client script's device.thirdpartycookiesenabled. - IsGdpr sets gdprApplies, and its absence is a warning rather than a reason to hide the dialog. src/identifiers/fodid.md gains a section on how the answer reaches the cloud from a browser page, being the two sources in order, the identifier being created on the request that carries every snippet result, refresh(), a change of answer, the iteration maximum, and the record of inputs in session storage. It also states that a stated usage is read first and a consent string beside it is never examined, that a Global Privacy Platform string is not read at all because the Model Terms do not map it, and what bit 3 of the flags byte records. The consent management platform page is complete except for the IsGdpr worked example, which waits on the change that carries the property to the cloud and is marked as a placeholder with the reason in a comment. The privacy notice wording is a draft and is marked as awaiting review by the team who handle customer data protection questions. Links follow this repository's UTM rule, which is mode forbid in .utm-lint.json, so 51degrees.com links carry no campaign parameters. The lint was run against the worktree and is clean.
ThirdPartyCookiesEnabled is declared a string property in the device detection data, and the cloud's JSON builder lower cases the names of things and never their values (CloudJsonBuilderElement line 472), so a page reads device.thirdpartycookiesenabled as 'True' or 'False', and it can also be 'Unknown' or 'NotSupported' or absent. Every non-empty string is truthy, so a plain truth test takes the yes branch for a visitor whose browser blocks third party cookies, which is the reading that matters most. The sharing page now says so where the value is read, shows the wrong test and the comparison to make instead, and treats anything that is neither word as not knowing rather than as a no. The IsGdpr page says that its own value is a real boolean and points across, because the two properties genuinely differ and a reader of both should not have to guess. The configuration table sends anyone reading their own code to the sharing page first.
Contributor
…second The integration page said the platform adds the client script when it finds no client script object on the page. That describes a defect the browser acceptance tests found rather than the behaviour. Both tags are asynchronous, so an object that is not there yet says nothing about whether the page has a client script, and a page carrying both tags was given a second copy, ran every round twice and created two identifiers. The platform now asks the page for a client script tag instead, because a tag is in the page from the moment the browser has read it whether it has run or not, looks again once the browser has finished reading the page so a tag written below its own is found, and adds one only where the page really carries none. Where a tag is there it waits for that tag to run and says so, and the page quotes that message. The load process diagram gains the waiting branch, and the index, sharing and IsGdpr pages say tag where they said client script, so no page implies that the platform adds one whenever the object is missing. A tag that runs and leaves no object is now described as warned about rather than replaced, with the reason.
The section says a script is added only where the page carries no client script tag, so the heading above it should not say client script.
An audit against a running cloud, then a check of every claim against the code, found seven things this documentation described the old way, two of which would have cost a reader working code. The identifier's date. The 51Did engine now states the minute an identifier was made (DidOnPremiseEngine.UtcNowToTheMinute, on the cloud branch fix/51did-creation-minute), which is what the 51Did specification requires and what the OWID envelope carries. It used to state midnight on the day of issue, so every identifier issued since midnight looked equally fresh and a replay within the day left no trace in the date. The 51Did page now says the date is the minute of issue in UTC, that age is read from it, that two identifiers issued in the same minute share a date and still differ in their signatures, and that identifiers from cloud releases up to 4.4.33 carry a date up to a day earlier than the moment they were made. The comparison example, which said the date changed on every call and showed two responses "a few seconds apart" with different dates, now shows two issued in different minutes. The public key. The 51Did page told readers to fetch the signing key from GET /owid/api/v3/creator and read publicKeySPKI. That endpoint was dropped from the OWID specification and answers 404 on the cloud branch feature/owid-key-period, and the field no longer exists. The section now describes /owid/api/v3/public-key, which answers JSON carrying the key as publicKey, the encoding as format, and validFrom and validTo, being the moments the key came into force and the next key starts. It says to hold the key against the stated span rather than the date asked for, which is the point of the change, since one request then serves every identifier in a period. It also says the only format is spki, that format=pkcs is refused with 400, that cloud releases up to 4.4.33 answered text/plain with the PEM alone, and that a client can tell which answer it has from the Content-Type and 51D-Version headers. The old text dated these changes by the day the code changed, which a reader of a public page cannot act on, so they are dated by release instead. Key selection. The page said the endpoint returns the key with the latest creation time on or before the date. Selection is by the period in force, not by creation time. A reader following the old text would have picked a key by a field that bears no relation to when it was in force. The key schedule. The /api/v4/id/key example showed two fields where there are four, and said the datetime filter selects keys created since the timestamp when it selects on when their period starts. A verifier polling as documented silently misses keys. The four fields are now listed with what each is for, weekStart marked as a legacy duplicate not to be read, and created marked as never being the moment a key came into force. The schedule is written thirteen weeks ahead by a Monday run (ci/generate-did-key.py), so created is about three months before a key's own startsAt, which is what the text now says rather than that the whole schedule shares one generation moment. The endpoint needs a Resource Key, where public-key also takes a Licence Key, and the text now says so. The TCF purposes. The table printed the old sets. Standard asked for 9 and 10, which Appendix 1 of the Model Terms for Marketing does not list, and did not ask for 2 or 11, which it does, so a consent string that was exactly the appendix's standard marketing signal produced no identifier at all. The table now matches the appendix (pmp/src/iab-purpose-sets.json on the cloud branch feature/mtm-purpose-sets), says the sets come from it rather than from us, says which releases used the old sets, and says why Special Purpose 2 is not required. Licence keys from a browser. The service can refuse a licence key with a 401 when the caller looks like a web browser, on the v4 and OWID routes. Nothing described it anywhere. There is now an error message entry saying plainly that the key has not been rejected and there is nothing wrong with it, why the request is refused, that a resource key is never affected, and the three ways to resolve it, with the authentication pages pointing at it. Derived properties and HumanConfidence. Neither was documented. A derived property is calculated rather than read from a data file, so it arrives in its own top level derived section and can only be asked for by its qualified name. The accessible properties engine drops a name it cannot match, so a request naming only the bare name is answered with the "properties not available with this subscription" warning, which reads like an entitlement problem and is not one, and that trap is called out where a reader meets it. HumanConfidence is described with its three values and, importantly, that it has no value at all until the 51Degrees JavaScript has run, because two of the properties it reads, IsVisible and HasWebDriver, are set on the client. A server only request carries no value and a null reason naming them. An earlier draft of this change said such a request tends to answer Low, which the script and the cloud's own integration test both contradict. Also corrected: three pages said only the json and js endpoints accept a licence key alone. The OWID public key endpoint does too, and unlike those two it needs no values list because its payload is fixed. And the 51Did page called the stable comparable part FodId.Hash. The published FiftyOne.Did package calls it FodId.MatchKey, the name the Model Terms use, carried Hash as a deprecated alias in versions 4.5.85 to 4.5.90, and has no Hash member at all in 4.5.97, so the page names MatchKey throughout and says code written against Hash must move. Carried over unchanged from documentation pull request 235, commit c8c3f3c, onto the create last branch. Its consent section sits between the Global Privacy Platform and stated usage wording already here, and the lines applied are exactly the lines of that commit.
The cloud and 51Did alignment now on this branch documents that a derived property is dropped when it is asked for by its bare name, and answered with a warning that reads like an entitlement problem. The IsGdpr page told a reader to request the property "by name", which leads a reader listing properties with values straight into that trap, so it now gives derived.IsGdpr and points at the derived properties section. It also quotes the property's own published description, from scripts/IsGdpr.yaml in derived-properties, that the value is a default for a caller that knows nothing about its own position and not a determination, because the regulation also reaches an organisation by where it is established. The page said nothing of that, and a publisher established in the European Union serving a visitor elsewhere would otherwise read false as permission.
The pages said nothing waits between the two cards. James Rosewell narrowed that on 15 September 2026, in his words "The waiting in the PMP is now only needed whilst it waits for fod.complete. That only happens if the script is added by the PMP and it has not finished resolving before it needs the result from 3PC availability." So the second card follows the first at once, except where the client script has not called complete when the visitor answers the first card. Then the platform shows its waiting ring until complete arrives or data-timeout passes, and decides. The pages describe that as the state rather than only its usual cause, because the platform's own tests read it that way (pmp/tests/test-cases/controller.test.ts on the platform branch, including "a page's own tag still resolving at the click is waited for too"), and they say plainly that it nearly always means a script the platform added. Where there is nothing to wait for, being a result already known, a round that finished without the property, or a client script that can never answer, the card is decided at once. - sharing.md gains "When the Second Card Waits", and its third condition now says the card is withheld on 'False' and offered on 'True' or an unknown, which needs ThirdPartyCookiesEnabled and ThirdPartyCookiesEnabledJavaScript on the key. The note that blocking browsers never see the card is now conditional on the key carrying the property. - configuration.md says data-timeout bounds the shared read, the shared write and that one wait, and gives the cap's reason as the platform branch states it, the probe cookie having gone. - index.md says the same under what the visitor sees, and the load process diagram now runs the refresh and the second card side by side, with the waiting ring as the one alternative, and step 9 of the walkthrough matches it.
The platform branch now gives the 4500 millisecond cap one reason for all three waits, being that in each of them the visitor can do nothing until it ends, and says the cards are darkened and cannot be pressed while the second card waits. The configuration row and the sharing page said the older, read only reason and did not say the cards cannot be pressed, so they now say what pmp/src/config.ts and pmp/README.md on that branch say.
… time James Rosewell corrected the wait between the cards on 15 September 2026: "The timeout for the transition from card 1 to 2 needs to be set in the PMP loader and is a constant our side. It's not user configurable. The 2nd card doesn't need to wait for the 51Did, ONLY the decision on the whether 3PC are available, and only when the configuration allows for the use of 3PC." The pages had said data-timeout bounds that wait and that the card waits for the client script to call complete. Both were wrong, and the platform branch now does what he said, in commit 88643b8a on feature/create-last-c, which these pages were checked against. - data-timeout bounds only the shared read and the shared write, and the configuration row says it has nothing to do with the wait between the cards. The cap's reason is the shared read again, as config.ts states. - The wait is short and bounded by a fixed time inside the platform that a page cannot change. Its value is not given on any page, and the constant, Config.THIRD_PARTY_COOKIE_DECISION_WAIT_MS, is named in an HTML comment for maintainers only. - It waits for the third party cookie decision and nothing else, never for the 51Did, the refresh or IsGdpr, and the decision arrives with the first response that settles it, even mid round. - It can only happen where sharing is configured, being data-use-third-party-cookies not false and a network name present, and otherwise there is no second card, no ring and no wait. - When the fixed time passes the platform decides from what it holds, the likely answer included, so a likely 'False' still withholds the card, which decideSharing in controller.ts does. The load process diagram's second card branch now says "only where sharing is configured" and the ring waits "for a short fixed time", and the walkthrough and the visitor section say the same. The diagram was rendered with mermaid-cli to confirm it still parses.
…ata-timeout James Rosewell made two decisions on 15 September 2026. On the wait between the cards he said "make sure a single constant in the build process and that the value is explained in the PMP documentation. i.e. when 3PC is enabled and available on the browser the PMP waits upto X to validate 3PC works. If it doesn't then the 2nd card does not display." On the attribute he said "Can we remove data-timeout from the PMP configuration. If so let's do it." The platform branch, feature/create-last-c for cloud pull request 381, carries both as build constants in pmp/build-constants.yaml, being thirdPartyCookieWaitMs at 3000 milliseconds and sharedStoreTimeoutMs at 1500 milliseconds. That work was not yet committed when these pages were checked against its working tree, so the pages depend on it landing. - The rule is stated with its value on the sharing page, in the visitor section and the walkthrough of the index page, and on the configuration page. Where the configuration allows third party cookies and the browser may support them, the platform waits up to three seconds for the client script to confirm that third party cookies work, and if they are not confirmed in that time there is no second card and the answer stays with the site. - The second card is now shown only where third party cookies are confirmed. The pages had said a result that is not known offers the card, including a key without the property and a client script that never answers, and all of those now mean no second card, which is what decideSharing and thirdPartyCookiesFrom on the platform branch do. The third condition, the list of cases where nothing waits, the paragraph on blocking browsers, the storage table on the preferences page and the integration page's note on a build with no client script now say so. - The data-timeout row is gone from the attribute table. A new section on the configuration page gives both fixed times and what happens when each runs out, and says a page still setting data-timeout gets one console warning and the attribute is ignored. The sharing page and the walkthrough give the 1500 millisecond bound where they describe the shared read and the shared write. - The maintainers' comment named Config.THIRD_PARTY_COOKIE_DECISION_WAIT_MS and kept the value off the page. That constant no longer exists and the value now belongs on the page, so the comments name the two build constants instead. The load process diagram now shows the three outcomes of the second card decision, the three second wait with its two endings, and the 1500 millisecond bound on the shared read and the shared write. It was rendered with mermaid-cli 11.17.0 and checked by eye. The UTM lint is clean and every @ref and @subpage target resolves.
The drift check compared javascript-builder/assets/JavaScriptResource.mustache against javascript-templates main on every push and pull request. The canonical template is being changed by the create last programme, 51Degrees/cloud#373, so the moment that change merges upstream every pull request in this repository would go red, including ones with nothing to do with that programme, and the pull request that refreshes the bundled asset could not be green before the upstream merge. Push and pull request builds now compare against UPSTREAM_PINNED_REF, 13ba5e7db47f88efc3440377121958b4aee1d3ac, which is the commit the bundled asset was taken from. The weekly schedule and a manual run still compare against UPSTREAM_TRACKING_REF, main, so a real upstream change is still reported once a week, and a failure there is the signal to prepare the refresh rather than a fault in whatever was pushed. A new step picks one of the two from the event name and writes both the reference and a plain word for which kind it is into GITHUB_ENV, so the pass line and the failure annotation each name the reference compared against and say whether it was the pinned commit or the tracking branch. That the bundled asset really matches 13ba5e7 was checked rather than assumed. The blob git stores for the bundled file and the file served by raw.githubusercontent.com at that commit are both 34942 bytes with MD5 5bf31dcb4769877af16aeccca1e9c663. Verified by running the job's own steps locally, read out of this workflow file, with the bundled copy materialised from the git object because that is what actions/checkout produces on ubuntu-latest whilst the Windows working copy carries CRLF under core.autocrlf. pull_request pinned commit green push pinned commit green schedule tracking branch green workflow_dispatch tracking branch green cargo build --workspace --all-targets finished in 2m 38s with no warnings, and cargo test --workspace passed 621 tests with 8 ignored across 69 suites and no failures. 51Degrees/rust#52 lifts the pin, by moving the bundled asset and UPSTREAM_PINNED_REF to the same commit in one pull request, which is then green on its own. The bundled asset is deliberately not touched here.
This reverts commit caf8241. Its content was the documentation wording fixes of the 15 September 2026 review, but its message belonged to a Rust workflow change, read from a message file another review had left under the same name. The same content follows in the next commit under its own message. Reverted rather than amended, because the commit had already been pushed and the branch is never force pushed.
…s, and clear four pronouns Review of 15 September 2026, checked against pmp/src on the platform branch, feature/create-last-c. - The configuration and integration pages said that leaving data-action-url out writes a warning to the console. Config.getValue warns only for a mandatory attribute and dispatchAction in cloud.ts returns without a message where the template is absent, so nothing is written, and the pages now say so. - The IsGdpr page gave two reasons for the platform's warning where the value cannot be had. explainMissing in client-script.ts has a third, the property present with no value for this visitor, which points at derived.isgdprnullreason, so the page lists all three. - The Framework surface answers getTCData as well as ping, addEventListener and removeEventListener (tcf-api.ts), and the preferences page already relied on that lower down, so the list names it. - Four sentences opened with a pronoun whose noun sat a paragraph away, and each now names the thing. This is the change of caf8241 again. That commit carried another change's message by mistake and is reverted in the commit before this one. UTM lint clean for campaign documentation.
… by feature One page under the PMP section that says the platform follows the technical schema of the Transparency and Consent Framework and presents itself as a consent management platform, does not follow the IAB Europe Policies, and takes its purpose mapping from Appendix 1 of the Model Terms for Marketing. A table sets the platform against a consent management platform row by row, and a second table takes each framework feature and says how the platform implements it, with not implemented stated where true. Every claim about the platform is read from pmp/src on the create last branch, and every external link answered 200 on 15 September 2026. The page joins the section index after the consent management platform wiring page.
…l, and give 51Degrees' position on signing James Rosewell's decision of 15 September 2026. The page said the platform picked a registered CMP ID at random on each page load from a list the build reads. The platform is being changed so the choice is made once a day, and the page now says the ID is chosen at random each day from IAB Europe's list of registered consent management platforms, with no mechanism detail beyond that, in the registration row of the comparison table and in the CMP ID row of the feature table. A new section, The CMP ID, says why an ID from the list is used. Vendors and their tags check the CMP ID in a TC string against that list, which the Global CMP List section of the TC string and vendor list formats specification states, so a string that follows the technical specification but carries an ID that is not on the list is rejected. The same section states that 51Degrees would support a change by IAB Europe requiring consent management platforms to sign the TC string cryptographically, with the signer identified by a decentralised domain name tied to the operator, and that every 51Did is already signed that way as an OWID naming its creator's domain. Four cells of the comparison table that stated more than the sources do are reworded to what the CMP API specification, the TC string and vendor list formats specification and IAB Europe's own pages say, being the question asked, refusal, the legal basis and storage.
…n-marketing identifiers James Rosewell's decision of 15 September 2026. The 51Did page said that standard and personalized need the Special license key and that the properties come back with a no-value reason without it, and the platform page said every answer creates a 51Did. Neither said the plain consequence, which is that such a key creates non-marketing identifiers and nothing else. The 51Did page now says it under Usage policies and licensing, naming the Special license key as the licence key carrying the 51Did product, whether it arrives on the Resource Key or beside it on the request, and adds that the platform asks the visitor the question whatever the key is entitled to, so only the alternative answer creates an identifier on such a key. The platform page qualifies the sentence that said all three answers create one. Checked in the cloud service's 51Did engine, where the usage flags mark non-marketing as needing no licence and the two marketing usages as needing one, the licence lookup accepts only a key carrying the 51Did product and otherwise sets the no-value reason, and non-marketing falls back to licence id 0. Checked in the platform's own source, which reads no product, entitlement or licence information at all before showing the dialog.
…arry The commit that added these cross links says it linked the comparison page from the wiring and preferences pages, and it changed only the wiring page. The preferences page ends by saying the platform is not a consent management platform, so a reader there is the one most likely to want the comparison, and its Find Out More list now carries the link.
…heck runs The CMP ID section said that vendors and the tags they put on your page check the CMP ID in a TC string. The Global CMP List section of the TC string and vendor list formats specification says the list is used by vendors to decide whether a CMP ID found in a string is valid, and says nothing about where that check runs, so the page now says only what the specification says.
…ring, the platform string, Global Privacy Control and the platform answer The 51Did page said what happens to a Transparency and Consent Framework string and to a Global Privacy Platform string in two paragraphs, and said nothing about Global Privacy Control or about a US Privacy string. Four sub pages now answer one question each, for a developer deciding what a request will produce. - fodid/pmp.md, the Preference Management Platform answer reaching the cloud as a stated id.usage, the three answers and the usage each creates, the signal source bit clear, and what a key without the Special license key produces. - fodid/tcf.md, sending tcstring as a query parameter or a form field and not as a header, the purpose sets of Appendix 1 of the Model Terms for Marketing version 2, legitimate interest, Special Purpose 2 going unchecked, a stated usage winning, and the signal source bit set. - fodid/gpp.md, a Global Privacy Platform string under either name and a US Privacy string deriving no usage, a framework string beside one still being read, why the terms decide it, and the evidence keys endpoint as the way to tell what a service reads. - fodid/gpc.md, the Sec-GPC header and navigator.globalPrivacyControl being ignored, changing neither the usage nor the value inside the identifier, and the two ways a publisher acts on the signal in their own code. Every statement was read from the source. The purpose sets are pmp/src/iab-purpose-sets.json and Appendix 1 of https://m4ow.uk/mtm/2.txt. The read path and the stated usage winning are IabTcfElement. The absent fodid section is the engine's Process gate and the integration tests that assert it. The licence gate is TryResolveLicenseId and the CloudV5FODiD product. Nothing in the service, the client script or the platform mentions Sec-GPC, globalPrivacyControl, us_privacy, usprivacy or uspstring. fodid.md gains the four subpage entries and one sentence pointing at them from the section on consent. No release is named, because the change that stops a platform string being read names none.
…complete Every example that read a 51Did, IsGdpr or the third party cookie result used fod.complete. On the common path the visitor answers after the first round has ended, so the identifier is created in a later round, and complete does not deliver it to a handler registered by then. In the client script template at f47917c, complete(fn) calls fn straight away when the round has already finished and registers nothing, so it never fires again. Registered earlier it goes through onChange and does fire again, which makes the behaviour depend on when the page code ran. onChange(fn) registers the handler, catches it up when a round has already finished, and fires on every later response, so it is right either way. Changed on pmp/integration.md, pmp/isgdpr.md, pmp/cmp-wiring.md, pmp/sharing.md twice and devicedetection/features/thirdpartycookies.md. Each example now tests that the section is present before reading it, because a response carries no fodid section at all until a usage is available, and no device or derived section unless the resource key asks for those properties. The integration page says in one sentence why onChange is the one to use. The device detection page also called ThirdPartyCookiesEnabled a boolean and tested it for truth. It is a string property in the device detection data, read as 'True' or 'False', which pmp/sharing.md already sets out, so the page now says so and the example compares the text.
Doxygen 1.17.0 writes a fenced mermaid block as <pre class="mermaid"> in a mermaidgraph div and renders nothing itself, so the load process diagram on the Preference Management Platform page showed as a code listing. That is documentation issue 239. The library is held in this repository rather than fetched from a content delivery network, because 51degrees.com sends a Content-Security-Policy whose script-src is 'self' and three named hosts, read from the response headers of the published documentation on 15 September 2026, so a jsdelivr or cdnjs script would draw the diagram on the gh-pages preview and fail silently on the published site. - docs/mermaid.min.js, the mermaid 11.17.2 bundle from jsdelivr, one self contained file with no dynamic imports, MIT licensed with its notices inside it. 3.5 MB on disk, about 960 KB compressed on the wire. - docs/Doxyfile copies it into the output with HTML_EXTRA_FILES. The build's minifier skips any file already named *.min.js. - docs/header.html loads it on DOMContentLoaded, and only where the page carries a diagram, then calls mermaid.run(). A page with no diagram fetches nothing. - docs/HEADER-NOTES.md carries the reasoning, as that file asks. Checked by building the site with Doxygen 1.17.0, the version CI downloads, and loading the generated page in Chromium. The diagram drew as one SVG of 1024 by 1401 pixels, a page with no diagram fetched the bundle zero times, and the diagram still drew with the production Content-Security-Policy on the response. Six aliases added with the platform pages are also dropped, being pmp, Pmp, pmpsharing, pmpprivacy, cmpwiring and fiftyonedid. No page in this repository uses any of them, every page links with @ref and its own wording, and an alias carries fixed link text that does not fit the sentences these pages write.
… fodid section Two pages said the fodid properties come back with a no-value reason where no usage could be worked out. The service returns no fodid section at all. DidOnPremiseEngine.Process runs the engine only where the flow data carries one of the id.usage evidence keys, being query.id.usage, header.id.usage or the derived key IabTcfElement writes, so with no usage no element data is created and the JSON carries no fodid. The service ReadMe says the same in its notes on the input parameters, and the integration tests assert it, Partial_TcString_DoesNotResolve51Did and Malformed_TcString_DoesNotResolve51Did both asserting that the section is absent while values=FODiD.IdProbLic is on the request. A no-value reason is what a caller gets for the two cases where the engine does run and cannot finish, being a usage the licence does not cover and a usage value that is not one of the three, and the second also carries a warning. The 51Did page now separates the three outcomes. The consent management platform page loses the pointer to the section name on the 51Did page and points at the new framework string page instead, which is where the purpose sets now live.
…signal A cold read of the two pages found three places that needed the noun rather than the gesture. - The platform answer page said the client script asks a consent management platform second, which reads as a page running both. The platform serves a framework surface of its own, which is what the script's order actually decides between, and that is why an identifier made on such a page records a stated usage. - The same page said 'what changes is the answer' where it meant which answer produces an identifier. - The Global Privacy Control page now says which document decides, being Appendix 1 of the Model Terms for Marketing, which maps the framework's purposes and no other signal, and its first bullet names the client script rather than leaving the actor out.
…rty list opens with The Prebid integration page listed the IP intelligence properties the RTD module needs without ip.Ip and ip.IpV6, although the module writes them to device.ip and device.ipv6 in convert51DegreesIpToOrtb2, which the comment at modules/51DegreesRtdProvider.js:345 says are not gated on confidence. Both are added, and the bullet now says where they land. BrowserName, BrowserVersion and HumanConfidence are named as three more properties worth having on the same key. The module puts none of them on the bid request, checked by reading every deepSetValue and deepSetNotEmptyValue call in the module, so the paragraph says so rather than implying the module writes them. The HumanConfidence sentence is the published wording from the Configurator catalogue and the cloud overview page. The Configurator page gains one section, Starting from a shared list, with an anchor the other pages can point at. It says a shared link opens Step 1 with properties already chosen, that the reader still walks through Steps 2 and 3 and can change the selection, and that the Configurator adds each chosen property's dependencies as the list opens. The dependency example is ip.LocationConfidence, which the deployed catalogue at /api/data/properties gives a single dependency, location.JavaScript, and 53 properties in that catalogue depend on the same collector.
…tor starts again The sharing page told a reader how an answer moves to the group's store and never said that nothing in the dialog moves it back. The second card is offered only to a visitor who has not already shared, so it never returns to ask again, and a reader was left to work that out. Where the Answer Is Held now carries the whole story. The platform reads this site's own storage first and asks the cloud only where this site holds nothing, a successful share removes the copy on this site, and a later change of answer goes to the shared store while sharing is on and is kept here instead only where the cloud refuses the write. A short paragraph then says there is no way back through the dialog, with the two steps for undoing it, being clearing the group's third party cookie and answering Only this site when the question comes round again. The preferences page gains one line where it says where an answer is kept, and one after the snippet that removes the two local keys, because that snippet asks the visitor again only where the answer still lives on this site. Every statement was read from the platform's own source on the branch that carries this work, being the read order, the write path and its fallback, the removal of the local copy once a share succeeds, the record of a refusal, and the eligibility test on the second card. The maintainer comment about the three second wait no longer names a private repository or a path inside it, because this repository is public.
…id and the platform Six pages and the Prebid integration page sent a reader to the Configurator's front page, which opens with nothing chosen, so a reader had to know which of several hundred properties this work needs before they could generate a key that works. Every one of them now opens the ready made list at https://configure.51degrees.com/YldpCKbW, which carries 64 properties, checked with GET /props/YldpCKbW at 14:05 today. The list holds the six 51Did properties, the four factor properties device.PlatformName, device.PlatformVersion, device.BrowserName and device.BrowserVersion, derived.HumanConfidence, device.ThirdPartyCookiesEnabled and device.ThirdPartyCookiesEnabledJavaScript, the ten IP intelligence names the Prebid.js real time data module reads and the ten location values, all confirmed by name against that response. The link answers 200, checked with curl -sIL. Each link is followed by a pointer to Starting from a shared list on the Configurator page, so a reader knows the Configurator adds dependencies as the list opens and that they can change the selection before generating. The pages are the 51Did page, the platform answer page, and the platform's configuration, index, integration and IsGdpr pages. No link carries a campaign parameter, because this repository publishes to 51degrees.com and its .utm-lint.json forbids them, and utm-lint.ps1 reports clean.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This rewrites the developer documentation for the Preference Management
Platform as a section of nine pages with the load process diagram, so that
publishers are told how the create last flow now works, being two tags, the
client script gathering the visitor's answer itself and a 51Did created only
after every other value is known. It also carries the cloud and 51Did page
updates from #235.
Documentation work package of the create last programme, which is tracked
internally, and the work package issue
#237.
Draft, and it stays a draft. These pages describe behaviour that the
cloud release carrying the Preference Management Platform change
introduces, so they publish with that release and not before.
Merge order
take of the new client script, with the Preference Management Platform
change, including the build constants that fix the three second wait and
replace
data-timeout, live in that release or an earlier one, and thechange that stops the service reading a Global Privacy Platform string
merged before it. The privacy wording review of the website, which is
tracked internally, has to happen before this reaches a published release.
cmp-wiring.mdfollows later, once IsGdpr is live.
What this does
src/identifiers/pmp.mdwas one 83 line page describing an integrationwhere the client script is loaded from the action URL on every answer.
That is no longer how it works. The page becomes a section of nine, one
per feature, each short enough to read in one sitting.
pmp/index.mdkeeps theIdentifiers_PMPpage id, so every existinglink from
main.md,fodid.md,overview.md, the Prebid page and theWordPress page still resolves. It carries the load process diagram,
a mermaid sequence with the browser, the page, the loader tag, the
bundle, the cloud and the client script, followed by the same sequence
in ten numbered steps.
pmp/integration.md, the two tags, load order, the object name anddata-object-name, the action URL as an optional hook, and what happenswhen the page carries no client script.
pmp/preferences.md, the three answers, where each is kept, the windowevent, the getter and the Transparency and Consent Framework surface.
pmp/sharing.md, the network name, the second card and its three secondwait, the shared store and its endpoint.
pmp/isgdpr.md, whatIsGdpris and what the platform does with it.pmp/cmp-wiring.md, the page for a site running a consent managementplatform.
pmp/cmp-comparison.md, the platform set against a consent managementplatform row by row, and every framework feature with how the platform
implements it.
pmp/configuration.md, the attribute table and the two fixed waits.pmp/privacy.md, draft privacy notice wording.src/identifiers/fodid.mdgains a section on how the answer reaches thecloud from a browser page, and states that a stated usage is read first
and a consent string beside it is never examined, that a Global Privacy
Platform string is not read at all, and what bit 3 of the flags byte
records.
src/identifiers/overview.mdgets the platform's name right.This pull request also carries all of
#235, James
Rosewell's "Align the cloud and 51Did pages with what the service now
does", as commit
40539e5e0authored by him, because both rewrite the51Did page. That brings the match key, the minute date, the
/owid/api/v3/public-keyendpoint, the key schedule fields, the TCFpurpose sets from Appendix 1 of the Model Terms, derived properties and
HumanConfidence, and the licence key used from a web browser, across
src/identifiers/fodid.md,src/services/cloud/errormessages.md,src/services/cloud/overview.mdandsrc/services/cloud/resourcekeys.md.The lines applied are exactly the lines of #235. Where #235 and this work
described the same behaviour differently the code decided. #235's purpose
sets match
pmp/src/iab-purpose-sets.jsonand the appendix, so theyreplace the stale table. This work's Global Privacy Platform wording
stands over #235's, because the cloud change that stops reading GPP
merges before the release these pages publish with, and #235 was written
before that ruling.
What a reader is told that is new
client script tag at all, using the cloud that served it and the
resource key it already holds, and logs that it did. Where a tag is
there, run or not, it waits for that tag and adds nothing. A publisher
who writes the tag themselves keeps control of its parameters.
data-object-nameis optional andfodis used when it is absent.writes no code and the action URL is a hook of their own rather than the
way the script is loaded.
work. When the configuration allows third party cookies and the browser
may support them, the platform waits up to three seconds (3000
milliseconds) after the first card for the client script to confirm
that they work, and if they are not confirmed in that time there is no
second card and the answer stays with the site. The platform never waits
for the 51Did, the refresh or
IsGdpr, and where the result is alreadysettled when the visitor answers, nothing waits. A result that is not known, a
key without
ThirdPartyCookiesEnabledorThirdPartyCookiesEnabledJavaScript, and a client script that neveranswers all mean no second card. Where sharing is not configured there is
no second card and no wait. The result comes from the client script's
device.thirdpartycookiesenabled.data-timeoutis gone. The platform waits a fixed 1500 millisecondsfor the cloud when it reads the shared answer at start up and when it
writes an answer, and a page that still sets the attribute gets one
console warning and the attribute is ignored. Both times are set once in
the platform's build, and the configuration page gives them in a table
with what happens when each runs out. HTML comments name the two build
constants for maintainers.
'True'or'False', and can carry'Unknown'or'NotSupported', so the sharingpage shows the plain truth test that is wrong and the comparison to make
instead.
IsGdpris a real boolean, and the two pages say so to eachother, because a reader who has just read both should not have to guess
which is which.
IsGdprsetsgdprApplies, and its absence is a console warning ratherthan a reason to hide the dialog or withhold an identifier.
result,
refresh()exists, a change of answer makes a fresh identifier,and the iterations of a page view stop at ten.
Where the behaviour came from
Every statement about the client script was read from the template on the
javascript-templates branch of this programme, at commit
eb02894993af45f0bb869f9fcd27426277d30a2e, "fix: send an answer known atconstruction when no request would otherwise go". The three console
messages quoted in these pages are verbatim from lines 645, 1197 and 1429
of that file, and
var maxIterations = 10at line 75 is why the page saysten. The platform pages were read from the
pmp/srcwork in the cloud workpackage, including
client-script.tsandconfig.ts. The wait between thecards and the removal of
data-timeoutfollow James Rosewell's twodecisions of 15 September 2026, and were checked against the working tree
of
feature/create-last-c, the platform branch, and then against its commita0dbcd56. The two values arethirdPartyCookieWaitMs(3000) andsharedStoreTimeoutMs(1500) inpmp/build-constants.yaml. The rule forwhen the second card is shown comes from
offerSharinganddecideSharingin
controller.tsandthirdPartyCookiesFrominclient-script.ts, andthe warning comes from
warnIfTimeoutSetinconfig.ts. The pages match that workfrom
4b3b06439, and were checked again againsta0dbcd56.Two things a reviewer should look at
opposite of the convention used elsewhere. This repository's
.utm-lint.jsonis{ "mode": "forbid" }, because its content ispublished on 51degrees.com, so a campaign tag would overwrite
attribution. Proved rather than assumed, by adding one correctly tagged
link and watching
utm-lint.ps1fail on it. The lint is clean on thisbranch.
DoxygenLayout.xmlis unchanged, because it enumerates no pages.It holds the navigation tabs and the class, file, group and directory
layouts, and the page tree is built by Doxygen from
@pageand@subpageordered bysrc/pageorder.md. The new pages are wired as@subpageentries underIdentifiers_PMP. Every@refand@subpagetarget on the branch resolves to a
@pageid, a heading anchor or an@anchor, and nothing is unresolved.Not finished, on purpose
cmp-wiring.mdwaits onIsGdprreaching thecloud. pipeline-dotnet#413
merged on 15 September 2026, so only the cloud service's own change
remains, and then its release. The placeholder says so on the page and the
reason is in an
HTML comment.
Everything else on that page is written.
pmp/privacy.mdis a draft and has not been reviewed by anyone whohandles customer data protection questions. An HTML comment at the top
of the page says so. The review is tracked internally, for Tracy Barlow,
and this pull request should not merge into a published release before it
happens.
48e4b1794. Doxygen 1.17.0 writes thefence out as
<pre class="mermaid">inside amermaidgraphdiv andrenders nothing itself, so the block used to show as a code listing, with
the numbered walkthrough underneath carrying the same content. The
mermaid bundle is now held in this repository, copied into the output and
loaded from
docs/header.htmlonly where a page carries a diagram, whichanswers #239.
It is served from the site rather than from a content delivery network
because of the Content-Security-Policy 51degrees.com sends, and
docs/HEADER-NOTES.mdcarries the reasoning. The walkthrough stays,because it is readable without any script at all.
How it was checked
The documentation was not built.
ci/generate-documentation.ps1installspackages with
apt-get, downloads a Linux Doxygen, clones every APIrepository and checks out
gh-pages, so it is a Linux CI script. Instead:utm-lint.ps1 -RepoRoot . -Campaign documentationgivesUTM link lint: clean for campaign 'documentation', with Align the cloud and 51Did pages with what the service now does #235's pagesincluded.
@refand@subpagetarget on the branch resolves to a@pageid, a
{#id}heading anchor or an@anchor, with nothing unresolved.4b3b06439with@mermaid-js/mermaid-cli11.17.0 (mermaid 11.17.2) against a localChrome, and the image was checked by eye. Align the cloud and 51Did pages with what the service now does #235's own Doxygen 1.17.0 run
is recorded in its body.
Review follow up, 15 September 2026
Three commits were added by the review pass of this morning, each checked
against
pmp/srcon the platform branchfeature/create-last-c.caf824126, the wording fixes below, but pushed under the message of anunrelated Rust workflow change by mistake, because the review's commit
message file collided with one another review had written under the same
name. The content is right and the message is wrong.
f26bd7edf, a revert ofcaf824126, because the branch is never forcepushed. The revert message says why.
623911969, the same wording fixes again under their own message. The netdiff of the three commits against
4b3b06439is four files, 17 linesadded and 14 removed, and nothing else on the branch changed.
What
623911969changes, and why.pmp/configuration.mdandpmp/integration.mdsaid that leavingdata-action-urlout writes a warning to the console.Config.getValuewarns only for a mandatory attribute and
dispatchActioninpmp/src/cloud.tsreturns without a message where the template is absent,so nothing is written, and both pages now say so.
pmp/isgdpr.mdgave two reasons for the platform's warning whereIsGdprcannot be had.
explainMissinginpmp/src/client-script.tshas a third,the property present with no value for this visitor, which points at
derived.isgdprnullreason, so the page lists all three.pmp/preferences.mdlisted the Framework surface's commands asping,addEventListenerandremoveEventListener.pmp/src/tcf-api.tsanswersgetTCDataas well, and the page already relied on that lower down, sothe list names it.
pmp/isgdpr.md,pmp/preferences.mdandpmp/configuration.md, and eachnow names the thing.
Verified by a script that asserted exactly one match for each edit before
writing, by reading the diff back, and by
utm-lint.ps1 -RepoRoot . -Campaign documentation, which reports clean. The documentation was notbuilt, for the reason given above.
One claim in the body above was corrected in this edit. The diagram note
under "Not finished, on purpose" said the generator is Doxygen 1.8.15. The
preview at
https://51degrees.github.io/doc-preview/feature-create-last-g/4.5/_identifiers__p_m_p.html
is stamped Doxygen 1.17.0, which writes the fence out as
<pre class="mermaid">inside amermaidgraphdiv, and the page loads noscript that renders that, so the block still shows as a code listing and the
fix is still in
docs/header.htmlrather than in the page, which is#239. Issue #239
carries the same wrong version and needs the same correction.
Six further commits, pushed on 15 September 2026
The head is now
636ed21cc. Two of the six were written earlier in thereview pass and held back, and four answer James Rosewell's decisions of
this morning.
fa7df9f3d, a new page,src/identifiers/pmp/cmp-comparison.md,called "Compared With a Consent Management Platform". The page says the
platform follows the technical schema of the Transparency and Consent
Framework and presents itself to vendors as a consent management
platform, that it does not follow the IAB Europe Framework Policies, and
that what its three answers mean comes from Appendix 1 of the Model Terms
for Marketing. One table sets the platform against a consent management
platform row by row, and a second takes each framework feature and says
how the platform implements it, with "not implemented" stated where that
is true. Every claim about the platform was read from
pmp/srconfeature/create-last-c. The page joins the section index as a@subpageafter the wiring page.9c3100f8b, a link to the new page frompmp/cmp-wiring.md.0a891f143, the CMP ID, to James Rosewell's decision. The page saidthe platform picked a registered CMP ID at random on each page load from
a list the build reads. The registration row of the comparison table and
the CMP ID row of the feature table now say the ID is chosen at random
each day from IAB Europe's list of registered consent management
platforms, with no mechanism detail beyond that. A new section, "The CMP
ID", says why an ID from the list is used at all, being that vendors and
their tags check the CMP ID in a TC string against that list, so a string
that follows the technical specification but carries an ID that is not on
the list is rejected. The same section states that 51Degrees would
support a change by IAB Europe requiring consent management platforms to
sign the TC string cryptographically, with the signer identified by a
decentralised domain name tied to the operator, and that every 51Did is
already signed that way as an OWID naming its creator's domain. The
section makes no claim about IAB Europe's plans. The same commit rewords
four cells of the comparison table that stated more than their sources
do, being the question asked, refusal, the legal basis and storage.
b032413b5, the licence rule, to James Rosewell's decision that thedocumentation must make clear that a licence key without the 51Did
product cannot create standard or personalized identifiers. The 51Did
page already said the two marketing usages need the Special license key
and that the properties come back with a no-value reason without it, and
the platform page said every answer creates a 51Did. Neither said the
plain consequence.
fodid.mdnow says, under "Usage policies andlicensing", that a key without the Special license key creates only
non-marketingidentifiers, that the Special license key is the licencekey carrying the 51Did product whether it arrives on the Resource Key or
beside it on the request, and that the platform asks the visitor the
question whatever the key is entitled to, so only the alternative answer
creates an identifier on such a key.
pmp/index.mdqualifies thesentence that said all three answers create one.
80c1f0dde, the link to the comparison page frompmp/preferences.md.The message of
9c3100f8bsays it linked the page from the wiring andpreferences pages, and it changed only the wiring page, so this commit
makes that claim true.
636ed21cc, one sentence of "The CMP ID". It said that vendors and thetags they put on your page check the CMP ID. The specification says the
list is used by vendors to decide whether a CMP ID found in a string is
valid and says nothing about where that check runs, so the page now says
only what the specification says.
How the five were checked.
source fetched on 15 September 2026, being the
CMP API specification,
the
TC string and vendor list formats specification,
IAB Europe's page for consent management
platforms, its framework
page and the
Framework
Policies,
version 2026-05-29.5.0.b. The reason for using a registered CMP ID is the
Global CMP List section of the TC string and vendor list formats
specification, which says vendors use the list to decide whether the CMP
ID in a string is valid, and that page section is linked from the new
text.
the usage flags mark
non-marketingas needing no licence and the twomarketing usages as needing one, where the licence lookup accepts only a
key carrying the 51Did product and otherwise sets the no-value reason,
and where
non-marketingfalls back to licence id 0. The platform's ownsource reads no product, entitlement or licence information at all before
showing the dialog, which is why the question is still asked on a key
that cannot create a marketing identifier.
@refand@subpagetarget on the branch still resolves to a@pageid, a{#id}heading anchor or an@anchor, with nothingunresolved, over 424 references and 171 pages.
.utm-lint.jsondemands, and no em dash, no bare "Did" and no"privacy-safe" or "anonymous" about the identifier.
Preview check passed on
636ed21ccin 3 minutes 7 seconds and the UTMlink lint passed with it. The published preview carries the new page at
https://51degrees.github.io/doc-preview/feature-create-last-g/4.5/_identifiers__p_m_p__cmp_comparison.html,
in the navigation under the Preference Management Platform section after
the wiring page, with "The CMP ID" as one of its sections, and the
platform index and preferences pages both link to it. The 51Did page
carries the licence passage.
Two claims in the body above were corrected in this edit. The section is
nine pages rather than eight, in the opening paragraph and under "What this
does", and the new comparison page is listed with the others.
One thing to watch on merge order. The page now says the CMP ID is
chosen once a day. That change to the platform was being made on
feature/create-last-c, the platform branch, while this was written, sothese pages must not publish in a release that does not carry it. The merge
order above already requires that release to carry the platform change.
The check that the platform code makes the choice daily, and that it takes
the list as IAB Europe's registered platforms, belongs to whoever reviews
the platform change.
Five further commits for the consent signals, pushed on 15 September 2026
The head after these five was
004f3ffd3, which merges636ed21ccinto this work. Theseanswer James Rosewell's ask for a 51Did sub page per consent signal, and
three further items from this morning.
b12338f09, four new sub pages of the 51Did page, one per signal, insrc/identifiers/fodid/, wired as@subpageentries under a new "InThis Section" heading on
fodid.mdwith a sentence pointing at them fromthe section on consent.
fodid/pmp.md, "The Preference Management Platform Answer". The threeanswers and the usage each creates, the answer reaching the cloud as a
stated
id.usageso the signal source bit stays clear, the order theclient script and the cloud both apply, and what a key without the
Special license key produces, being
non-marketingidentifiers only.Links the platform section and the comparison page.
fodid/tcf.md, "Transparency and Consent Framework Strings". Sendingthe string as
tcstringin the query string or a form field and not asa header, the two purpose sets of Appendix 1 of the Model Terms for
Marketing, legitimate interest, Special Purpose 2 going unchecked,
personalizedtried beforestandard, a stated usage winning with thestring beside it never examined, and the signal source bit set.
fodid/gpp.md, "Global Privacy Platform and US Privacy Strings".Neither is read, under
gpporgppstringand for every sectionincluding the United States national one, a framework string beside one
still being read as usual, why the terms decide it, what to do instead,
and the
evidencekeysendpoint as the way to tell what a service reads.fodid/gpc.md, "Global Privacy Control". TheSec-GPCheader andnavigator.globalPrivacyControlare ignored, they set no usage andchange no value inside the identifier, and a publisher who wants to act
on the signal does so in their own code before asking for an identifier.
42e26908f,onChangerather thancompletein every example thatreads the identifier or a value that arrives with it, on
pmp/integration.md,pmp/isgdpr.md,pmp/cmp-wiring.md,pmp/sharing.mdtwice anddevicedetection/features/thirdpartycookies.md,each now testing that the section is present before reading it. In the
client script template at
f47917c,complete(fn)callsfnat oncewhen the round has already finished and registers nothing, so it never
fires again, while
onChange(fn)catches up and fires on every laterresponse. On the common path the visitor answers after the first round,
so the identifier arrives in a later one. The integration page says that
in one sentence. The same commit corrects the device detection page,
which called
ThirdPartyCookiesEnableda boolean and tested it fortruth, when it is a string property read as
TrueorFalse.48e4b1794, the load process diagram now renders, which is#239, and six
unused aliases are dropped.
docs/mermaid.min.jsis the mermaid 11.17.2bundle,
docs/Doxyfilecopies it into the output withHTML_EXTRA_FILES, anddocs/header.htmlfetches it onDOMContentLoadedonly where the page carries a diagram, then callsmermaid.run(). It is served from the site rather than from a contentdelivery network because 51degrees.com sends a Content-Security-Policy
whose
script-srcis'self'and three named hosts, read from theresponse headers of the published documentation, so a jsdelivr or cdnjs
script would draw the diagram on this preview, which sets no policy, and
fail silently once published.
docs/HEADER-NOTES.mdcarries thatreasoning, as that file asks. The aliases dropped are
pmp,Pmp,pmpsharing,pmpprivacy,cmpwiringandfiftyonedid, none of whichany page uses.
703079e8b, what a request with no usage actually gets back.fodid.mdandpmp/cmp-wiring.mdsaid thefodid.*properties comeback with a no-value reason where no usage could be worked out. The
service returns no
fodidsection at all.DidOnPremiseEngine.Processruns the engine only where the flow data carries one of the
id.usageevidence keys, the service ReadMe says the same in its notes on the input
parameters, and the integration tests
Partial_TcString_DoesNotResolve51Didand
Malformed_TcString_DoesNotResolve51Didassert the section is absentwhile
values=FODiD.IdProbLicis on the request. A no-value reason iswhat the two cases where the engine does run and cannot finish produce,
being a usage the licence does not cover and a value that is not one of
the three, and the second also carries a warning.
9696dde2d, a cold read of the two new pages that named the thing inthree places where a pronoun or a gesture stood in for it.
How these were checked.
ci/generate-documentation.ps1downloads, run on this branch with onlyOUTPUT_DIRECTORYoverridden. The four pages are generated, they appearin the navigation under the 51Did page, and the only unresolved
references in the whole build are two that predate this work, on
devicedetection/migrationguides/wurfl.mdanddevicedetection/otherintegrations/uaparser.md.locally and loaded in Chromium, where the mermaid block became one SVG of
1024 by 1401 pixels. A page with no diagram fetched the bundle zero
times, and the diagram still drew when the response carried the
Content-Security-Policy that 51degrees.com sends.
are
pmp/src/iab-purpose-sets.jsonin the cloud repository and Appendix 1of https://m4ow.uk/mtm/2.txt, which was fetched and read. The read path,
the stated usage winning and the signal source bit are
IabTcfElementandDidOnPremiseEngineonfeature/create-last-d1, the branch that stops theservice reading a Global Privacy Platform string. The licence rule
is that engine's licence lookup and the
CloudV5FODiDproduct. The threeanswers and how they travel are the platform's
controller.tsand theclient script template at
f47917c.sec-gpc,globalPrivacyControl,gpc,us_privacy,usprivacyanduspstringreturn no match on the cloud'smain, on all six create lastcloud branches, on the hot fix branch, in the client script template or
in the platform source.
names none. Its own commit
9bddab5etook4.4.37out of the serviceReadMe and the 51Did context note, leaving the version only in the test
gates where a
comment says to move it if another release is cut first. Production
answers
4.4.36today and itsevidencekeyslist still carriesquery.gpp.curl -sIL, beingglobalprivacycontrol.org, the W3C draft at w3c.github.io/gpc,
the IAB Tech Lab Global Privacy Platform and USPrivacy repositories, the
IAB Europe framework and Framework Policies pages, the IAB Tech Lab
framework repository and both Model Terms addresses.
utm-lint.ps1 -RepoRoot . -Campaign documentationreports clean afterthe merge, and the key scan reports
keys found in the commits to push: 0.One claim in the body above was corrected in this edit. "The diagram does
not render on the published site", under "Not finished, on purpose", is no
longer true and that entry now says what was done and where the reasoning
lives.
The decision that belongs to a person is whether a 3.5 MB third party
library should live in this repository at all, the alternative being a
change to the Content-Security-Policy that 51degrees.com sends, which is not
in this repository and is set by whoever runs the site.
Three further commits on resource keys and sharing, pushed on 15 September 2026
The head is now
4f9d74554. New shared property lists were registered thismorning, and the pages that tell a reader to make a resource key were sending
them to the Configurator's front page, which opens with nothing chosen.
7b258dd29, the Prebid key property list corrected, and a shared listexplained.
src/integrations/prebid.mdlisted the IP intelligenceproperties the Prebid.js real time data module needs without
IpandIpV6, although the module writes both todevice.ipanddevice.ipv6.Both are added, with a clause saying where each lands.
BrowserName,BrowserVersionandHumanConfidenceare named as three more propertiesworth having on the same key, in their own paragraph rather than in the
bullets, because the module puts none of the three on the bid request and
a page reads them from the same cloud answer instead.
src/services/configurator.mdgains one section, "Starting from a sharedlist", with the anchor
Configurator_SharedList, saying a shared linkopens Step 1 with properties already chosen, that the reader still walks
through Steps 2 and 3 and can tick or clear rows first, and that the
Configurator adds each chosen property's dependencies as the list opens.
897b4b67d, sharing cannot be undone from the dialog, and how a visitorstarts again.
src/identifiers/pmp/sharing.mdsaid how an answer movesto the group's store and never said that nothing in the dialog moves it
back. "Where the Answer Is Held" now says the platform reads this site's
own storage first and asks the cloud only where this site holds nothing,
that a successful share removes the copy on this site, and that a later
change goes to the shared store while sharing is on and is kept here
instead only where the cloud refuses the write. A short paragraph follows
saying the second card is offered only to a visitor who has not already
shared, with the two steps for undoing it, being clearing the group's
third party cookie and answering Only this site when the question
comes round again.
src/identifiers/pmp/preferences.mdgains one linewhere it says where an answer is kept and one after the snippet that
removes the two local keys, because that snippet asks the visitor again
only where the answer still lives on this site. The maintainer comment on
the three second wait no longer names a repository or a path inside one,
because this repository is public.
4f9d74554, every resource key link points at the ready made list.src/identifiers/fodid.md,src/identifiers/fodid/pmp.md, the platform'sconfiguration, index, integration and IsGdpr pages, and the Prebid
integration page now open
https://configure.51degrees.com/YldpCKbW instead of the Configurator's
front page, each followed by a pointer to "Starting from a shared list".
How the three were checked.
GET https://configure.51degrees.com/props/YldpCKbWreturns 64 names,read at 14:05 today. Every property the Prebid page and the platform
pages name was confirmed present in that response, being the six 51Did
properties,
device.PlatformName,device.PlatformVersion,device.BrowserName,device.BrowserVersion,derived.HumanConfidence,device.ThirdPartyCookiesEnabled,device.ThirdPartyCookiesEnabledJavaScript, the ten IP intelligence namesthe module reads and the ten location values.
curl -sIL -o /dev/null -w '%{http_code}', as doeshttps://configure.51degrees.com, which the Prebid page still names inprose.
off any list.
convert51DegreesIpToOrtb2writesdevice.ipanddevice.ipv6fromip.ipandip.ipv6with the comment that neither isgated on confidence, and it writes the
device.geo.*fields only wherelocationconfidenceishighormedium. NodeepSetValueordeepSetNotEmptyValuecall in the module readsbrowsername,browserversionorhumanconfidence, which is why the paragraph saysthe module does not put them on the bid request.
ip.LocationConfidencedependency example is the deployed catalogue'sown answer.
GET https://configure.51degrees.com/api/data/propertiesgives that property one dependency,
location.JavaScript, and 53 otherproperties in the same catalogue depend on the same collector.
the platform's own source on the branch that carries this work, being the
read order, the write path and its fallback, the removal of the local copy
once a share succeeds, the record of a refusal, and the eligibility test
on the second card.
utm-lint.ps1 -RepoRoot . -Campaign documentationreports clean, and thekey scan reports
keys found in the commits to push: 0.One claim in the body above was corrected in this edit. The consent signals
section said "The head is now
004f3ffd3", which these three commits makefalse, so it now says that was the head after those five.