Repository navigation
Release candidate - #181
Draft
jwrosewell wants to merge 99 commits into
Draft
jwrosewell wants to merge 99 commits into
jwrosewell wants to merge 99 commits into
Conversation
The rust repository is now 51degrees-rust (51Degrees/51degrees-rust#206). The list of packages that implement the 51Did specification links the new name.
Keeps the 51degrees-java link from #64 next to it.
…ies (#71) Every link to pipeline-dotnet, device-detection-dotnet, device-detection-dotnet-examples, ip-intelligence-dotnet, location-dotnet and caching-dotnet now points at the same file or folder in 51degrees-dotnet, and the 51Did package list names 51degrees-dotnet, 51degrees-python and 51degrees-php. Line numbers that had drifted from the code their links name point at that code again. The link to an issue in pipeline-dotnet stays, as issues stayed there.
Neither the .NET nor the Java packages have the option, so the Configuration options section of the JSON builder now reads None, as the Sequence element's does.
A value converted from Azimuth or Declination is given rounded to 3 decimal places as a number, a string or a WKT point, because the locations are rough and more places only lengthen the strings and WKT shapes. A package may offer a setting for a different number, with 3 as the default.
A package ignores white space before and after the text and refuses any other character outside the alphabet, white space inside included, as RFC 4648 section 3.3 requires when the referring specification says nothing else.
* Refuse a 51Did base64 text that mixes the two alphabets A text uses one alphabet, so a package refuses one holding + or / beside - or _. RFC 4648 section 5 says the URL-safe encoding should not be regarded as the same as base64. * Rest the one alphabet rule on what base64 libraries do Base64 libraries write one alphabet or the other and never a mix, and most of their decoders refuse a mixed text. The paragraph now says so first, keeps RFC 4648 section 5 as agreement, and has a package that maps one alphabet onto the other check for a mix before mapping.
The Evidence and thread safety pages said Evidence never changes once created, while the Sequence Element page said the reference implementations write the session id and sequence back to Evidence. The .NET and Java implementations agree that a Flow Element adds values through the Flow Data during processing, replacing any value held under the same key, with no check against the accepted Evidence. The pages now say what they do, and the Sequence Element adds both values to Evidence as part of its process rather than as a deviation.
…xt (#93) The pipeline configuration page says a list of strings in a configuration file is a comma-separated string, which is also the only form .NET reads. The Derived Property Element example gave Scripts and ScriptFiles as JSON arrays, and the Translation Engine example gave Sources as JSON arrays, one with a trailing comma that made the example invalid JSON. Each is now a comma-separated string, with a sentence saying so beside each example.
) The accepted evidence section now says that the prefix of a key is everything before its first dot, and that id.email, id.salt, request-query and request-path are never shared, in any mode, as the .NET packages already do. It names the HTTP header that carries cookies as Cookie, which RFC 6265 section 5.4 defines, where it said "cookies", which matches no header, so the raw header no longer undoes the cookie rule. It also gives the session. rule every package already applies.
No package reads Sources or Translations from a configuration file. A translation is a pair of values and AddSource has a two value overload, so the configuration example now sets only the single value options and the page says sources and translations are added in code, or by a builder made for one translation. The example also spells MissingTranslationBehavior as the builder method does.
The row said the native default applies and then that the option is not enabled by default. The C library's default configuration sets it to true, and the .NET builder leaves it at that native default, so the row and the UserAgents match metric now say it is on by default.
…owing it (#96) * Hold the Hash engine's concurrency at what its caches allow The native code refuses a cache whose parts hold fewer entries than there are parts, so a profile with a cache read from the file allows at most the whole square root of its smallest cache's capacity. The Concurrency row now says the default is held at that number and a larger named value is refused when the Engine is built. * Say what Concurrency does in the native code, for both engines Concurrency is the number of file handles in the pool as well as the number of parts each cache is split into. An operation that finds no free handle fails with INSUFFICIENT_HANDLES, and a cache part smaller than the number of parts stops the Engine building with CORRUPT_DATA, so a profile that reads through a cache serves at most the whole square root of its smallest cache's capacity at once. The default is the native 10, not the processor count. The device detection and IP Intelligence rows now say this, replacing the earlier wording on this branch. * Wait for a free file handle rather than fail James Rosewell decided the file handle pool waits for a handle to be released instead of failing with INSUFFICIENT_HANDLES. Both Concurrency rows now say an operation waits, so a low Concurrency slows operations that read from the file rather than failing them. * Split caches so any Concurrency builds, defaulting to the processor count James Rosewell decided how Concurrency works in the native code. Concurrency is the expected number of operations at once and defaults to the processor count. It is the number of file handles, and an operation waits for a free one. Each cache is split into Concurrency parts, or fewer so that every part holds at least Concurrency items, and a cache smaller than Concurrency grows to fit, so no value stops the Engine building. * Cap the file handles at 64 A process can only have so many files open, 512 through fopen by default on Windows, shared by every engine. James Rosewell chose a fixed maximum, so the pool holds Concurrency handles up to 64 and operations beyond that wait for a free one.
The Usage Sharing Element accepted every header except Cookie, which made the Pipeline accept every header, so the ETag and Vary of the client-side endpoints covered If-None-Match and every other header. It now accepts no Evidence and shares what it finds in the Flow Data and in the web request, which the web integration adds as server.http-request.
…e sharing reads from the web request
Thread safety asks only for Evidence that is safe to read from several threads, and the .NET Evidence is a plain dictionary written with no lock, so a member of a parallel group cannot add Evidence while the other members read it.
…configuration file (#100) * Let a package set Translation Engine sources and translations from a configuration file The page said a configuration file cannot set sources or translations, on the understanding that no package read them from one. Go, Node and Python all do in released versions, so removing the route would break customer configuration files. A package MAY now offer it, choosing the form for the two pairs, a translation and a source given as file contents, using the names it uses in code and documenting the form with an example. * Leave the file form of sources and translations to pipeline configuration The paragraph let each package choose how sources and translations are written in a configuration file. That works against pipeline configuration, which asks for configuration files that are consistent between languages and for the same names in the file as in code. The paragraph now keeps the MAY, what the two pairs hold, and the route in code for a package that does not offer the MAY, so the existing pipeline configuration rules govern the form.
…n-premise engines (#102) * Say that PHP always uses MaxPerformance, for both on-premise engines PHP serves requests from worker processes forked from the process that built the Engine, and a fork copies the open file handles with one shared read position, so a profile that reads from the file corrupts reads. PHP device detection already enforces MaxPerformance for this reason and PHP IP Intelligence is moving to the same rule. The Performance Profile row on both on-premise pages now gives Balanced as the default except in PHP, with a footnote saying why. * Limit PHP's MaxPerformance rule to the Engine built at start-up Only an Engine built before PHP forks its workers shares file handles between processes. An Engine built after the fork, or in a process that never forks, opens its own handles, so it may use any profile. * Have the PHP footnote's last sentence cover any other Engine The footnote's first sentence says the Engine built when PHP starts always uses MaxPerformance. Its last sentence let an Engine in a process that never forks use any profile, and a start-up Engine in the PHP command line, which never forks, fitted both sentences. The last sentence now covers any other Engine, such as one built after the fork or in a process that never forks, so the start-up Engine always uses MaxPerformance.
…sponse (#53) * Keep the ETag the same in every process, and send none on a failed response The ETag of the client-side endpoints is a hash of the Evidence, and a browser revalidates with the one it kept. The web integration feature now says the same Evidence gives the same ETag in every process running the same build, so a restarted server or another one behind a load balancer can match it, and that a response to a request whose processing recorded errors has no-cache and no ETag, so a browser does not keep the faulty response once processing works again. * Keep a failed client-side response out of shared caches A response to a request whose processing recorded errors carried bare no-cache, which lets a shared cache store a response made for one visitor (RFC 9111 sections 3 and 5.2.2.7), where every other response from the JSON and JavaScript endpoints is private. Make it private, no-cache. * Answer a matching If-None-Match with 304 or 412 as RFC 9110 says RFC 9110 section 13.1.2 gives a 304 to a GET or HEAD request whose If-None-Match matches and a 412 to any other method, so the rule that a matching request is answered while processing is failing now says the same, and agrees with the 412 for a POST to the JSON endpoint. Say that a failed response differs from the table in two rows, link the Flow Data errors collection, and name what the paragraph on stale values is about. * Say a matching request gets the same status code when processing records errors The 304 sent while processing records errors carries private, no-cache and no ETag, so the whole answer is not the same as at other times, only its status code. When processing throws there is no 304 at all, so the rule now names the case where processing records errors, and says the header matches the ETag rather than "it".
* Give each missing Property message as the .NET packages write it The missing Properties table differed from the messages in the .NET packages' Messages.resx, and had no row for an unknown reason or for a failed request to the remote server. Bring each message into line with .NET, name each reason as MissingPropertyReason does, and say how the reason is decided, as MissingPropertyService and MissingPropertyServiceCloud do. * Apply the typed accessor step only to a Property the metadata does not list A Property the metadata listed, with the Engine's own data file tier and as available, reached step 3.3 and could be given an upgrade message naming the tier the customer already has. The .NET packages cannot produce that case, because their on-premise Engines find no typed accessor and a Cloud Engine with a listed Property stops at step 3.2. Also say that the type name in the upgrade message differs between languages. * Give each missing Property one reason that does not mislead Decided at review on 5 November 2026. The typed accessor step applies to On-premise Engines only and counts inherited accessors. A Cloud Engine without its Property metadata gives CloudRequestFailed when its request for the property list failed, and Unknown otherwise, and one that read its Resource Key's product list and found no entry for its element has loaded metadata with no Properties. The upgrade message leaves out the sentence naming the data tiers when they are not known. Names are compared ignoring case, as the Element Data compares them when a value is read. * Say that .NET is to change to match the missing Property rules The page called the .NET packages the reference implementation, but Messages.resx has no text for the second row, and .NET differs from the section in the five places decided at review. Say which rows .NET's text gives and what .NET is to change. Make row 6's parameters easier to read.
* Check the callback URL before the JavaScript builder writes it The protocol and host of the callback URL can come from Evidence, and the template writes the URL inside a single-quoted JavaScript string. Step 6 now points to a new Callback URL section with the rules every implementation applies to the scheme, the host, the port, letter case and percent-encoding. A URL that fails them is left out and logged once. * Allow only ASCII in a callback URL's host name A Host header's syntax allows only ASCII, and RFC 3986 asks for an internationalized name in its IDNA form, so a host name label holds only ASCII letters, digits, hyphens and underscores, and a configured internationalized host is given as A-labels. This removes the need for a Unicode letter test, whose meaning differs between languages. Step 6 now says what one slash between host and endpoint means, and that a part that is empty or only whitespace is not set. * Allow only http and https as a callback URL's protocol The script uses the callback URL only with fetch and XMLHttpRequest, and the Fetch Standard's scheme fetch gives a network error for every scheme but about, blob, data, file, http and https, of which only http and https reach a server. A callback with any other protocol, such as ftp, gave a script whose update always failed, with nothing logged on the server. Refuse it as rule 6 says, and refuse a configured protocol that is neither when the element is built, as the .NET and Rust builders already do. * Compare a callback URL's protocol ignoring ASCII case only Comparing ignoring case in general lets a letter outside ASCII match one inside it. Java's String.equalsIgnoreCase treats the long s (U+017F) as s, so httpſ would pass as https and then fail in the browser. Compare ignoring ASCII case only, as the Rust check's eq_ignore_ascii_case does. * Refuse a zone identifier, user information, and ? or # in the callback host RFC 9844 obsoletes RFC 6874, so RFC 3986's IPv6 literal has no zone identifier again, and the URL Standard's IPv6 parser fails on a %. The Fetch Standard's Request constructor throws a TypeError for a URL with credentials, and a ? or # in the host value put the endpoint into the query or fragment. Refuse all three. Say that every host the rules accept is one RFC 3986 allows. Define white space as the Unicode White_Space characters. Say that a configured protocol of only white space is refused, while an empty one means none is configured. Log a failed URL the first time for each instance of the element. * Refuse a callback host that ends in a number unless it is an IPv4 address The URL Standard's host parser reads a name whose last label, ignoring a final dot, is all digits or 0x followed by hex digits as an IPv4 address, and fails if it is not one, so a browser cannot parse a callback holding example.123 or 1.2.3.999. Such a name must now be an IPv4 address in RFC 3986's IPv4address form. That also refuses 127.1, 0x7f.0.0.1, 01.2.3.4 and 1.2.3.4., which the URL Standard reads as addresses, so a callback host has one written form. Rule 3 now says why all user information is refused. fetch refuses credentials, but XMLHttpRequest, which the script uses otherwise, accepts them, and an empty user name is not credentials. Rule 6 now keeps the user information out of the logged error, so a configured password is never logged. * Leave everything before the host value's last @ out of the logged error Rule 6 left out of the log only what came before the authority's last @, but a password can hold a / that is not encoded, so the configured host user:pa/ss@example.com has the authority user:pa, which holds no @, and the log carried the password. An authority ending at ? or # fails the same way for user:pa?ss@example.com. The error now shows *** in place of everything in the host value before its last @, which can leave part of a refused host out of the log, as exa mple.com/a@b is logged as https://***@b/51dpipeline/json. Rule 4 said each part of an IPv4 address has no leading zero, which could be read as refusing 0.0.0.0. It now says each dec-octet has no zero before another digit, with examples. * Refuse an @ anywhere in the callback host value A password can hold a / that is not encoded, so the configured host user:12/ss@example.com has the authority user:12, which holds no @ and passed rules 4 and 5 as the host user and the port 12. Every script served then carried the password and called back to the host user. Rule 3 now refuses an @ anywhere in the host value, so example.com/a@b is refused too. Rule 6's example names rule 3 as one that refuses it. Rule 4 said each dec-octet has no zero before another digit, which by its words refuses 101. It now says a dec-octet starts with 0 only when it is 0 alone. RFC 3986 section 7.4 is now cited only for 127.1, 0x7f.0.0.1 and 01.2.3.4, as RFC 3986 reads 1.2.3.4. as a registered name.
* Define the Hashed Email match key exactly The match key is the SHA-256 of the salt length as one byte, the salt, then the lowercased address part of the email as UTF-8. The address part is the addr-spec of an RFC 5322 mailbox, so a display name, angle brackets and comments do not change the key. * Say exactly which bytes the Hashed Email match key hashes Existing keys depend on how the email is read and lowercased, which the draft did not fix exactly. .NET's MailAddress refuses some obsolete forms and three current ones, keeps the quote characters and backslashes of a quoted local part, and puts the address in Normalization Form C, and ToLowerInvariant uses the Unicode simple lowercase mapping except for U+0130. Say each of these, define the white space that is ignored, and add a worked example with a quoted local part. * Refuse a Hashed Email that is not a current-syntax RFC 5322 mailbox The issuer must now refuse every email that is not a mailbox in RFC 5322 section 3's syntax without the obs- rules, such as user name@example.com, a list of addresses or the obsolete john . smith@example.com, where before it could accept one when it could read an address part from it. RFC 5322's syntax is ASCII only, so an email holding a character outside ASCII is refused too. The issuer may still refuse the three current forms that hold a line break, a quoted-pair of a space or a tab, or a display name holding a quoted-string and another word. * Accept a Hashed Email mailbox that holds UTF-8, as RFC 6532 allows A mailbox is now one in RFC 5322 section 3's current syntax as RFC 6532 section 3.2 extends it, where VCHAR, ctext, atext, qtext and dtext each also allow any character outside ASCII written as UTF-8. So an email such as Üser@Exämple.com is accepted again rather than refused, and a worked example gives its bytes and Match Key, which do not depend on whether Ü and ä are written as one character or as a letter and a combining mark. The issuer may also refuse a domain literal holding a character outside ASCII, and U+FFFE, as .NET's MailAddress refuses both. * Remove white space around a Hashed Email before reading it as a mailbox The page said white space around the email is ignored, but not when. Since RFC 6532, U+00A0 and the other White_Space characters outside ASCII are atext, so U+00A0 followed by user@example.com is itself a mailbox whose address part keeps the U+00A0, and user@example.com followed by U+000B is not a mailbox at all. Removing the white space first gives user@example.com for both. The page now says the white space is removed before the email is read as a mailbox, and that every rule below applies to what remains. The first use of mailbox now says it is the one defined below, which RFC 6532 extends.
…s sent (#131) * Honor Requested Properties beside a Resource Key when a License Key is sent James Rosewell decided on 10 November 2026 that with both keys the remote server uses the Resource Key's licence and the License Key given, and the response holds the Resource Key's Properties plus the Requested Properties the License Key grants. The live service already answers that way. A Resource Key with Requested Properties and no License Key is still refused, as the list is ignored. * Say exactly what Requested Properties do beside a Resource Key with no License Key The list is not simply ignored. A requested Property the Resource Key carries changes nothing, and one it lacks comes back null with the paid feature nullreason, so the list adds no data. Records the five measured cases, and why the server answers so: it merges the Resource Key's licence and Properties with the request's before checking entitlement. * Accept Requested Properties with a Resource Key and no License Key James Rosewell corrected the table on 10 November 2026. The requested Properties are added to those the Resource Key carries, one no licence grants is answered as null with the paid feature nullreason, and a name that is not a Property is not returned, so the combination is valid and no Engine refuses it.
…#132) James Rosewell decided on 10 November 2026 that the keys are always shared. A Pipeline that authenticates holds them as fiftyone.resource-key and fiftyone.license-key, and the filter shares both whatever its configuration, because usage is counted against them.
…ies not returned section and the configuration table, and correct the reading of the 21 September measurement (#133)
…ue (#105) * Say how a Device Detection Engine gives each data file property's value Adds a Property values section to the on-premise Device Detection page. It says where a data file Property's value comes from, what a List, a string, a Boolean and a number give, and how JavascriptHardwareProfile values are wrapped. It also says the match metric Properties come after the data file Properties, in the order of their table. Adds a paragraph to value-types.md saying how a typed accessor reads a Boolean or number stored as a string. Gives the cloud example response a string null reason for javascripthardwareprofile, as json-builder.md asks. * Number the Property value rules, say the Profile holds the values, and use the native no-value message The Property values paragraph chained conditions with "Otherwise", each referring to a different earlier one, and is now five numbered rules. The value types text says the Profile, not the result, holds more than one value. The cloud example's null reason is now the C library's message for a Property no result holds a value for (hash.c, NO_RESULT_FOR_PROPERTY), rather than wording that no implementation produces. * Show the cloud example's null reason as the cloud returns it No code path gives the message the example used. The live cloud service answers javascripthardwareprofile with null and javascripthardwareprofilenullreason with null for a phone and a desktop whose Profile holds no value, so the example keeps that. * Limit the match metric order to the Property metadata A JSON object is an unordered collection of name/value pairs (RFC 8259 section 1), and Element Data is a map in several languages, so the order of the table applies to the Engine's Property metadata, which every language keeps as a list. * Show the null reason the C++ results give once they report a Profile holding no value The C++ results gave no reason when the matched Profile holds no value for a Property, so the cloud answered null, which the JSON Builder page does not allow. The C++ fix reports NO_RESULT_FOR_PROPERTY with its message, so the example shows that message.
* Say what the endpoints answer to a matching If-None-Match A GET or HEAD gets a 304 and any other method a 412, as RFC 9110 section 13.1.2 requires, and a 304 carries the headers a 200 would, as section 15.4.5 requires. * State the 304 or 412 rule once, and give the 304 headers after it rc now states the 304 or 412 rule, with the RFC 9110 section 13.1.2 citation, in the paragraph on a matching If-None-Match while processing records errors. This branch's own statement of the same rule is removed, and the earlier sentence that said the endpoint can just return a 304 now says only that the content is not sent again and that the status code depends on the method, as described below. The paragraph on the headers a 304 carries, from RFC 9110 section 15.4.5, now follows the rule it depends on.
…sing (#136) * Say that the endpoints answer a matching If-None-Match without processing * Make comparing If-None-Match before processing a MUST, as RFC 9110 requires * Say a matching request would record errors, as it is not processed
…ameters (#84) The .NET and Java JavaScript builders leave query.session-id and query.sequence out of the parameters passed to the template, because the template adds both to each callback itself. The specification said to take every query. Evidence value.
…#82) The .NET and Java JSON builders add their own Element Data to the Flow Data before building the JSON, and leave it out with an exclusion list of set-headers, cloud-response and json-builder. The specification's list named the usage sharing element, which adds no Element Data, and did not name the JSON builder itself.
* Log once when a derived property's source Element runs too late The specification said nothing about the error the .NET packages log when a request carries no Element Data at all under a key a script reads, which happens when the Element supplying that key runs after the derived property Element. It also said that nothing is shared between requests, which rules out the flag .NET keeps so that the error is logged once per script. Describe the error, and allow one flag per script, set once without a lock. * Name both causes of missing source Element Data, and add the one-time error to the late Element paragraph A supplier that ran first but failed before adding its Element Data also leaves no Element Data under the key, because exception-handling.md has every Flow Element run after one fails. The paragraph on an Element placed too late now also says the Element logs the error once.
…es needs no decoder (#108) * Say an Engine whose native code converts the values needs no decoder The page said Device Detection Engines MUST be able to use the decoder's Element Data, and that the Element is implemented for .NET. Neither holds for an on-premise Engine built on the native code in 51degrees-cxx. - The native code turns 51D_GetHighEntropyValues evidence, from the query string or a cookie, into sec-ch-ua headers before it matches (setGetHighEntropyValuesHeader, src/dd/hash/hash.c lines 3540 to 3556, called from resultsHashFromEvidence_setSpecialHeaders at line 4313), while special evidence processing is on, which is the default (src/dd/config-dd.h line 90), all at 51degrees-cxx e866f6d3cea936587790f87967eff7863778b30e. The C++ Engine accepts both keys (src/dd/EngineDeviceDetection.cpp lines 39 and 41). - 51degrees-dotnet at 4d7152bfcb7c9bcb17d14cb512025b65a3307313, whose cxx submodule is e866f6d3, has no decoder element. Its examples say the engine consumes the values as query or cookie parameters (examples/device-detection/ExampleBase/ExampleUtils.cs lines 620 to 624). - 51degrees-java at f69da52e52104687206d1ca1c3595d2935790f34, with the same cxx submodule, has no decoder element either. The page now says an on-premise Engine whose native code converts the values itself need not read the Element's data, and that a Pipeline built around one does not need the Element. The precedence rule under Process is unchanged. Follows 51Degrees/51degrees-rust#350. * Say which one value the native code converts
* Say how each form of IP address is looked up An IPv4-mapped IPv6 address is its IPv4 address under RFC 4291 and RFC 4038, an IPv4-compatible address is deprecated, and an IPv4-embedded address under RFC 6052 stays an IPv6 address. Recording this here gives every language one rule and code comments one URL to point at. * Pad the runtime table and clear the .NET row's wording
…om the reserved names (#138) * Say that the Cloud Request Engine sends every Evidence value apart from the reserved names Processing said the Engine filters the Evidence down to the keys the server needs, and then that it adds all the values from the Flow Data Evidence. James Rosewell decided on 10 November that the Engine sends everything but the reserved names, because what the server uses changes and knowing about other Evidence is important to continued optimization. Accepted Evidence now says it does not limit what is sent. * Leave the web request view out of the cloud request web-integration.md has every web integration add a read-only view of the request under server.http-request. .NET and Java leave it out of the cloud request, as it is an object and not a value, so Processing now names it with the reserved names. * Say letter case is ignored when the web request view is left out of the cloud request * Restore the line endings that the previous commit changed in six files
* Say what happens when a data file refresh fails A refresh whose new data cannot be used fails and leaves the Engine on the data it had, the failure is logged and reported as a failure, and polling and the file watcher carry on, so a later good file is still applied. * Report every data update check, a skipped or not-needed one as not needed James Rosewell decided on 10 November that the specification follows .NET's DataUpdateService.cs, which reports each polled check, each programmatic check of a configured data file and each file watcher event to its CheckForUpdateComplete event with a success, not needed or failure status. A new section, Reporting each check, says so, and the polling, newFileAvailable and programmatic update paths point to it. * Follow .NET exactly for data update reporting and errors, and exempt one-request packages James Rosewell decided on 10 November that data-updates.md follows the .NET data update service exactly. A programmatic check for a data file with no configuration and an update from memory report nothing, a watcher event that waited for another event for the same file is reported as in progress, and a programmatic failure is passed to the caller without being logged. The polling branch of Programmatic update reports not needed. The Errors table takes .NET's messages, levels and statuses. A package whose processes last for one request, such as PHP's, has no data update service. * Add the update callback error, end the memory refresh message with a full stop, and widen In progress * Give the callback and integrity check rows exactly as .NET writes them * Say how a callback's data update error is logged and that later callbacks are skipped
…ry Property by position (#137) * Scope the read-by-name rule for an Engine created with no names to the C++ Results classes In C, each property's index is its own place when no names are given (src/common/properties.c lines 479 to 491 in 51degrees-cxx at cfad8978), so an implementation that calls C may read such an Engine by position. The C++ ResultsHash and ResultsIpi classes refuse every position without names (ResultsBase.cpp lines 481 to 484), so the MUST read by name stays for them. The native code still gives each position. Also point the Cloud Request Engine's JSON Builder link at #process, the heading json-builder.md has. * Say which data set a read by name asks for the position C resolves a position against the data set the results were populated from (51degrees-cxx cfad8978 src/dd/hash/hash.c lines 5896 to 5901, src/ipi/ipi.c line 3342), so a position taken from the current data set after a refresh can name a different Property. * Write the property index rule into Finding Properties James Rosewell decided on 10 November that a property's index is its place in the list of names the engine was built with, fixed at build. An engine configured with no names takes every property name in the data set at build as that list, every read uses the index, and a read by name uses a table built once from the build-time names, never from the data set. This replaces the earlier text that let C read an engine with no names by its data set position. The C quote, the C++ scoping and the json-builder anchor fix stay. * Say the native code keeps the first data file's names for an Engine created with no names * Say an Engine with no names has every Property of its first data file, and that getProperties can list one with no position * Say the no-names Engine's names are sorted by name, and that its metadata reports a Property with no position as not available * Say a Property only a later data file has is excluded from a no-names Engine's configuration * Link the C code at the commit that merged it into rc * Link the C code at the commit that merged it into rc
…139) * Give the exact error text for a JSON Builder with no sequence number Every implementation gives the same message when no Sequence Element has produced a sequence number, with JSON Bundler in place of JSON Builder in PHP. Where the error is wrapped, the error the caller reads carries the message. * Say that the JSON Builder error message is the whole text Nothing is added to the message, such as a documentation link. A prefix that a language's error type writes in front of every error of that type is not part of the message.
Merge main into rc after the 11 November release
…'s tests prove (#141) * Read by name where an implementation measures it as faster than by position Finding Properties in both on-premise Engine specifications said every read MUST use the position. Node.js, .NET Device Detection and PHP read by name, because each measured a read by name as faster, and the fastest approach is the one used. Each specification now says every read uses the position except where the implementation's own measurement shows a read by name is faster, that the implementation records the measurement, and lists the implementations that read by name with a link to each measurement. The rest of the section is unchanged. * Say which reads go by name, and link the issues that follow them up The exception now covers a read that the implementation's own measurement shows is faster by name, rather than a whole implementation. .NET Device Detection lists only its TryGet reads as measured faster by name, and says its Value<T> and list reads also go by name though a Value<T> read measured slower by name, with the issue that moves them to the position. The Node.js and PHP items say their measurement was taken with the Device Detection Engine, and link the issues that time the IP Intelligence Engine on its own. * Link the Node.js job rather than the run, and say why it is red Both on-premise pages linked the Node.js measurement as run 42995016804, whose conclusion is failure, so a reader who opened it saw a red run and no figure. Each page now links job 130437032538 in that run, which holds the figure, in the same form the .NET item already uses. That job is itself red, so each page now says why in one sentence. The only two tests that failed in it are timing assertions, and a read by position being slower is what made them fail, which is the result the item records. * Make the read by position a SHOULD, with the exception tests prove James decided that the rule is a SHOULD and that an exception stands where tests prove it. So both on-premise Engine specifications now say every read SHOULD use the position, and an implementation MAY read by name where its own tests prove a read by name is faster. The proof has to be a measurement a test or a continuous integration run takes and records, and the implementation records that run in the change that makes the read go by name. The rest of the first item is unchanged, so a typed accessor still resolves its position once, and the table of names to positions is still built once and MUST NOT be built again from a later data set. The three implementations that read by name are listed as before, each with its measurement and the issues that follow it up. The .NET entry now says plainly that its Value<T> reads, which no test proves faster by name, are outside the exception. RFC 2119 terms are upper case, as stylistic-conventions.md asks. * Promise a run only where there is one The list says each read by name is given with the run that proves it, but the .NET Value<T> reads are listed, go by name and have no such run, which the entry itself says four lines later. The sentence now reads "and, where there is one, the run that proves it", which holds for every entry. The same sentence is in both pages and moves together.
autoUpdate is the master switch for polling alone. The file system watcher runs on its own setting, whether autoUpdate is on or off, because watching a file downloads nothing. A License Key is needed only for the two mechanisms that download, polling and the update on start-up, so an application with no License Key still has its watcher. The master switch section, Automatic update from File, General config, both start-up processes and the Defaults section now say so.
* Say why the On-premise Engines do not support a results cache Footnote 3 of both On-premise Engine specifications said only that the reference implementations do not allow a cache because of the complexity it introduces. It now names the complexity, which is coordinating the lifetime and thread safety of a result with the underlying C code that owns the memory the result reads from, says a future version will address it, and tells callers who need a cache to maintain their own with the cache implementations the Pipeline provides, storing the values they read rather than the result. * Say what a results cache measured as worth on the On-premise Engines Adds the measured benefit to the footnote that explains why a results cache is not supported, so readers can judge whether their traffic would gain from maintaining their own. The numbers come from the IP Intelligence On-Premise Engine in MaxPerformance with an Enterprise data file. * Leave the results cache measurements out of footnote 3 The figures were measured on 26 September, before the C library changed when graphs are walked on 2 November, and nobody has measured since, so whether they still hold is not known. The footnote keeps why a results cache is not supported, that a future version will address it, and how callers can keep their own cache of the values they read. * Run the pull request checks against rc, now the base * Leave out the promise of a future version, which nothing tracks
…xample naming the script's inputs (#147) Two pages of the specification say less than the languages do for the releases on Monday 16 November, or say it wrongly.
…nes when all Evidence is shared (#155) The [Usage Sharing Element](https://github.com/51Degrees/specifications/blob/706de1f0cb3edd560eb7fd7ef3bb19b93a4883dc/pipeline-specification/pipeline-elements/usage-sharing-element.md) differs from the rule the cloud service follows in two places.
…sage (#156) Each 51Degrees package is now released at its own version, but the usage sharing message carries one version, the `Version` element ([line 162](https://github.com/51Degrees/specifications/blob/706de1f0cb3edd560eb7fd7ef3bb19b93a4883dc/pipeline-specification/pipeline-elements/usage-sharing-element.md#L162), described at [lines 189 and 190](https://github.com/51Degrees/specifications/blob/706de1f0cb3edd560eb7fd7ef3bb19b93a4883dc/pipeline-specification/pipeline-elements/usage-sharing-element.md#L189-L190) as "The version of the Pipeline that is being used to generate the xml"). A message therefore cannot say which release of each package is running, and how far a release has spread cannot be measured.
…w to get one (#157) [Cloud Device Detection](https://github.com/51Degrees/specifications/blob/706de1f0cb3edd560eb7fd7ef3bb19b93a4883dc/device-detection-specification/required-examples.md#L127-L138) says the cloud examples use a Resource Key, and [Execution](https://github.com/51Degrees/specifications/blob/706de1f0cb3edd560eb7fd7ef3bb19b93a4883dc/device-detection-specification/required-examples.md#L20-L23) says the tests run each example with no arguments or settings. Nothing says what a cloud example does when it runs with no Resource Key, which is how it runs from a fresh clone and how its test runs it.
[sequence-element.md lines 39 to 44](https://github.com/51Degrees/specifications/blob/706de1f0cb3edd560eb7fd7ef3bb19b93a4883dc/pipeline-specification/pipeline-elements/sequence-element.md#L39-L44) says "A `query.sequence` value that is not a whole number, or a string made only of the digits 0 to 9 with no sign or spaces, from 0 up to 2,147,483,646 is not present". It does not say what a whole number is when Evidence can hold several kinds of number, so the languages read a floating-point 2.0 differently. Some count it as 2 and others treat it as absent.
) [Getting Started Console](https://github.com/51Degrees/specifications/blob/706de1f0cb3edd560eb7fd7ef3bb19b93a4883dc/device-detection-specification/required-examples.md#L225-L227) says "Examples MUST be provided that illustrate basic usage of the API for both Cloud and On-Prem, using fluent builder and options file configuration, when executed as a console (command line) application". That can be read as asking for four examples, one for each of Cloud and On-Prem with each way of configuring, or as met when the console examples show both ways between them.
…#146) [Session tracking](https://github.com/51Degrees/specifications/blob/c8128ca4fa68c6348d76ddb2889bb5087de1aa5f/pipeline-specification/pipeline-elements/usage-sharing-element.md#session-tracking) in the usage sharing element said that "certain Evidence values MUST NOT be included" in the hash used to find repeat Evidence, and gave the sequence number as an example. It now says five things.
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.
Releases what has collected on
rcsince the last release, #134, which merged on 11 November 2026 at 15:07 UTC. The release head is dbaf099c, and every link to the text below is fixed at that commit.This release comes five days after #134, inside the week, by James Rosewell's decision of 13 November 2026 that every repository releases on Monday 16 November.
One rule on
rcis open, so this does not merge as it stands.rcholds the Package elements, a version for every 51Degrees package in each usage sharing message (#156, usage-sharing-element.md lines 208 to 216). James Rosewell is looking at that rule again because of the size the elements add to every message. #180 takes the elements back out ofrcand waits for his word. This release merges once #180 has merged, or once he has said the rule stands as #156 wrote it.The packages released on 16 November link the specification on
mainfrom their code comments and documentation, and follow the rules below, so this merges intomainbefore any language's release.Conformance with the specification
These are the rules every implementation now has to meet. Each language's release of 16 November says which it meets.
Usage sharing
51D_one, even when all Evidence is shared. "It MUST also return false, whatever the configuration and even when all Evidence is shared, for any key whose prefix iscookie.and whose field does not start with51D_." (usage-sharing-element.md lines 65 to 69, #155). The session cookie follows, as it "MUST NOT be shared unless its name starts with51D_, as no other cookie is" (lines 98 and 99).query.session-idandquery.sequence, MUST NOT be included in the generation of this hash code, whatever the other options say, and MUST still be written to the record." Both keys are matched ignoring letter case (lines 318 to 332, #146).The Sequence Element
query.sequencevalue counts only when it is held in one of the language's integer types, or is a string made only of the digits 0 to 9 with no sign or spaces, and only from 0 up to 2,147,483,646", and "A floating-point value is not present even when it has no fractional part, so 2.0 is absent where 2 is not." (sequence-element.md lines 40 to 47, #158).Data updates
mainmadeautoUpdatethe switch for the watcher too, so a deployment with its own data file and no License Key could not have the Engine refresh when a new file was put in place.On-premise Engines
mainsaid every read MUST use the position. Both pages list the reads that go by name in Node.js, .NET and PHP, each with its change and its run.Lazy loading
TimeSpanin .NET ortime.Durationin Go, it MAY take that type instead" (lazy-loading.md lines 23 to 28, #147). The page now names Go beside .NET and Java as implementing lazy loading.Examples
Before you upgrade
A specification has no package and no version to upgrade. These are the changes a user of a package meets once that package follows the rules above, and each language's release says whether it does.
51D_ones and those in the included list, as before, turn share all query string parameters off.51D_cookies are shared, whatever the settings. Track session now works the same whether or not all Evidence is shared.query.sequenceis ignored (rule 5). An application that adds the sequence to Evidence itself gives it as an integer or as digits.autoUpdateoff and with no License Key (rule 6). This reverses the earlier text, under which turningautoUpdateoff also stopped the watcher. An application that wants no watcher turns the watcher's own setting off.New features
None. A specification gains rules, which are listed above.
Performance and test coverage
Other changes to the text
HumanConfidencescript reads, and lists its four values,High,Medium,LowandUnknown(#147).CI and housekeeping
mainwas merged back intorcafter the last release (#140).Not in this release
rc. It merges with the same change in the other repositories, and #179 is about the same two messages.Issues this closes
Closes #144.
The other 27 open issues stay open.
rc. It was raised on 15 November and is not inrcat this head.Build
The specifications have no build. Every check ran on the release head dbaf099 and passed.
rcwithdryrunset, run 43756102753, green in Get Pull Requests, Configure, Build and Test on Ubuntu (which runs the link lint against the merged result) and Complete. Compare Performance is skipped, as the workflow setsskip-compare-performance.If #180 merges into
rc, the head moves and these checks run again on the new head before this merges.This repository publishes no package and tags no version, so there is no Publish dry run, no version to move and no timing against
main.