Skip to content

Settings without a consumer and consumers without settings #1403

Open

Description

@jwrosewell

Many features need two things in a deployment before they do anything. One is a settings section. The other is the consumer that acts on it, being a middleware, a module, a request filter, a page request preparer or a part of the adapter the deployment runs on. When one is present without the other, Trusted Server refuses to start, logs a warning, or runs as though nothing were wrong, and which of the three happens depends on the feature. Each feature that checks at all has written its own check, the checks run at different times and say different things, and several features have no check.

Code references are at 3aaf720c0, the head of the draft stack, which is the cookies by path change (#1400). Startup means an adapter loading the settings and building its state. Push means ts config validate, ts config diff and ts config push, which run the same checks. In the Code column below, core/ stands for crates/trusted-server-core/src/.

One case, the 51Degrees client hints

The client hints change (#1386, row 44 of the stack) adds the middleware 51degrees.client_hints, which writes the Delegate-CH tag and the client hint headers on the pages a [[serve]] entry names it for. The middleware needs [shared.51degrees], which holds the cloud endpoint and the license key. Whenever that table is present, the crate registers two things, a request filter that asks the cloud service which hints the browser should send, and the middleware that writes the answer into the page (lib.rs lines 110 to 132).

The settings without the consumer. With [shared.51degrees] and no [[serve]] entry naming the middleware, push accepts the document and startup logs one warning, that the middleware "changes no page" (registry.rs lines 1724 to 1731). The request filter is not tied to the entry, and core runs every registered filter (registry.rs lines 2640 to 2674), so on Fastly the filter still asks the service on every page request (lib.rs lines 352 to 381). A deployment that has not placed the middleware therefore asks the service, through its cache, about every page request, and no page gets the tag.

The consumer without the settings. With the [[serve]] entry and no [shared.51degrees], the crate does not run, so nothing supplies the middleware. Startup refuses the entry as naming "51degrees.client_hints, which no module that runs supplies" (registry.rs lines 1669 to 1685). The message never says that [shared.51degrees] is missing, because the note naming absent shared tables (lines 1162 to 1178) is added to a refused section selection and not to a refused entry. Push accepts the same document, because push checks only the shape of each entry (settings.rs lines 4776 to 4791) and the crate's own validation stops early when the table is absent (lib.rs lines 142 to 150).

Both present, on an adapter without request filters. Only the Fastly adapter runs request filters (Fastly app.rs lines 836 to 867). The Axum, Cloudflare and Spin adapters call no request filter, so there the middleware runs on every page an entry names, finds no hints left for it (lib.rs lines 264 to 268 and 286 to 298) and writes nothing, with no log line.

The cases

Feature Settings section Consumer Today with the settings missing Today with the consumer missing Code
51Degrees client hints (#1386) [shared.51degrees] 51degrees.client_hints in a [[serve]] entry, and its request filter Refused at startup, with no word of the missing table. Accepted at push. One warning at startup. On Fastly every page request still asks the service. middleware/51degrees lib.rs 110-132, 352-381, core/integrations/registry.rs 1669-1685, 1724-1731
Request filters (DataDome protection, 51Degrees client hints) enable_protection = true in [bot-protection.datadome], or [shared.51degrees] The adapter's request filter step, which only the Fastly adapter has No filter is registered, as intended On Axum, Cloudflare and Spin, accepted with no log. DataDome checks no request, and the client hints middleware writes nothing. bot-protection/datadome lib.rs 1148-1150, Fastly app.rs 836-867
DataDome page tag (#1351) client_side_key in [bot-protection.datadome] bot-protection.datadome.tag in a [[serve]] entry Accepted at push and at startup with no log, and the tag writes nothing. Startup checks only the tag's URL. One warning at startup. Accepted at push. bot-protection/datadome lib.rs 509-511, 1016-1026, core/integrations/registry.rs 1724-1731
InMobi consent prompt (#1359) [cmp] module = "inmobi" with [cmp.inmobi] cmp.inmobi in an entry for text/html Refused at startup, with a note that no section selects cmp.inmobi. Accepted at push. Refused at push and at startup by the module's own check, which counts any text/html entry whatever its path. cmp/inmobi lib.rs 141-158, core/integrations/registry.rs 1118-1134
ads.txt sellers (#1361) [ads-txt] module = "iabtl" with [ads-txt.iabtl] ads-txt.iabtl in a [[fetch]] entry for text/plain Refused at startup. Accepted at push. Refused at push and at startup by the module's own check, which counts any text/plain entry whatever its path. ads-txt/iabtl lib.rs 152-167
robots.txt (#1366) [robots-txt] robots_txt in a [[fetch]] entry for text/plain Refused at startup. Accepted at push. Refused at push and at startup by core's own check, which requires path = "/robots.txt" exactly. core/settings.rs 4414-4462, core/integrations/registry.rs 2375-2393
Middleware settings (#1379) [middleware.<name>] A running module that supplies <name> The middleware runs on its defaults, as intended Refused at startup. Accepted at push. core/integrations/registry.rs 1702-1723
Shared vendor tables (#1367) [shared.<vendor>] A linked builder that reads the table The builder does not run, and a selection naming its module is refused with a note naming the table Refused at startup. Accepted at push. core/integrations/registry.rs 2044-2069, 2138-2150
Section selections (#1348) A section's module, such as [cmp] module A linked module of that name The module is not built, as intended Refused at startup. Not checked at push, deliberately, because the tool may not link the module. core/integrations/registry.rs 1774-1825, 2163-2168
Loading cache tables (#1373) [cache.<name>] A linked module that declares the cache On a stock build, the one loading cache module the build links, as intended. A build that links none, or several, loads through no cache, with no log Refused at push and at startup core/integrations/mod.rs 1008-1025, 1083-1104, core/platform/loading_cache.rs 240-242, 269-272
Auction analytics (#1357, #1389) [analytics] module = "tinybird" The adapter's telemetry sink, which only the Fastly adapter reads No events are sent, as intended On Axum, Cloudflare and Spin, resolved and checked at startup, and nothing is sent, with no log core/integrations/registry.rs 1505-1540, Fastly app.rs 389-392
Asset routes (#1125) [[proxy.asset_routes]] The adapter's asset route dispatch, which only the Fastly adapter has No asset routes, as intended On Axum, Cloudflare and Spin, checked at push and at startup and never dispatched, with no log Fastly app.rs 1191-1194, 1310-1313
Origin certificate check (#1132) [proxy] certificate_check = false The adapter's origin client, which only the Fastly adapter configures from it Certificates are checked, as intended On Axum the client ignores it, and on Cloudflare and Spin it changes only a backend's name, with no log. Fastly warns. Fastly backend.rs 492-496, Axum platform.rs 321-333, Cloudflare platform.rs 70-78, Spin platform.rs 92-100
Host geo lookup [geo] module = "platform" The adapter's own geo lookup No geo lookup, as intended On Axum and Spin, accepted with no log, and every request gets no location core/integrations/registry.rs 1480-1482, Axum platform.rs 210-218, Spin platform.rs 809-817
Host device module [device] module = "platform" The adapter's own device module, which only the Fastly adapter builds No host device module, as intended On Axum, Cloudflare and Spin, accepted with no log, and no host device module runs core/integrations/registry.rs 1323, Fastly lib.rs 1256-1263, Fastly Cargo.toml 65, device/fastly lib.rs 69
Edge Cookie cluster settings (#1097) cluster_trust_threshold and cluster_recheck_secs in [ec] None in any crate Defaults apply, and nothing reads them Accepted with no log core/settings.rs 783-795

Three more things stand out across the table.

  1. The first check that fails stops the start, and most checks stop at the first gap they find, so a document with two gaps of different kinds is fixed one failed start at a time.
  2. Push builds no registry, so every check that lives in the registry runs at startup only. A document that push accepts can still take a service down when it next starts.
  3. Each placement check was written for one feature. InMobi and the ads.txt sellers count any entry for their media type, whereas robots.txt requires an exact path.

The common startup model

One model in core, in four points.

  1. Every consumer declares the settings it needs. A builder already declares the shared table it reads, its secret settings, the caches it loads through and the middleware it supplies with its phase and media type, as the 51Degrees builder does (lib.rs lines 94 to 100). The builder would also declare, for each middleware, module, request filter and page request preparer it supplies, the settings that consumer needs and what places it, being a section selecting it or an entry naming it for a media type and path. A request filter or preparer that only feeds one middleware is declared as part of that middleware, so it runs only where an entry names the middleware. A consumer that runs on defaults declares its settings optional, so leaving them out is not a gap.
  2. Every settings section has a consumer. Core's own sections declare their consumers in the same way, for example [robots-txt] and its middleware, so a section with no consumer that is linked, selected and placed is a gap.
  3. Each adapter declares the parts it has, such as running request filters, dispatching asset routes, reading the telemetry sink, honoring certificate_check, a host geo lookup and a host device module. A section or a consumer that needs a part the adapter lacks is then a gap on that adapter, reported by name, rather than accepted and ignored.
  4. Startup, ts config validate and ts config push give one report listing every gap. Before any builder builds, core goes through the declarations once and collects every gap into one report, in which each line names the section, the consumer, the missing half and what to add. Each line refuses or warns, as the design document sets out, and startup refuses when any line refuses. Push runs the same check against the builders the tool links and the parts of the adapter the document is pushed for, so a document with a gap that refuses is stopped before it is written to the config store.

The declaration types and the check live in core and name no vendor, standard or platform. Each vendor crate declares its own consumers in its own folder, and each adapter declares its own parts.

What it would replace

Related issues

Of the same shape:

The issues whose changes added checks of this shape, each written for its own feature, are a vendor's shared table (#1284), a module's own loading cache (#1320), a middleware's own settings table (#1331) and the 51Degrees client hints (#1333). The stack as a whole is listed in #777.

A design document for this model follows in the pull request that holds every design document of the stack (#1342).

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions