From 31dd42d80765b6c79ced1a1e6cb28901a8314357 Mon Sep 17 00:00:00 2001 From: James Rosewell Date: Fri, 9 Oct 2026 16:13:38 +0100 Subject: [PATCH 01/15] A module can add rules to robots.txt, and core writes the file `[robots-txt] modules` names, in order, what makes the file. `refuse_all` and `allow_all` are core's own and need nothing else. Any other name is a contributor a module declared with `with_robots_txt_contributor`, and core names none of them. A contributor returns groups of records and core writes every byte. That is what lets these hold whatever a contributor returns. - With `refuse_all` selected the file is the refusal and nothing else. - A path in `always_allow` stays open to every crawler a contribution refuses the whole site to, by a group of core's own below the contributions, which are not edited. - The `Sitemap` line and the publisher's own text go around the contributions. Each contributor's answer is held in the key-value store, under a key that is a hash of the publisher, the contributor's name and the contributor's fingerprint, and the contributor is asked again when its own refresh is due. A contributor that fails has its held answer served past its age. With nothing held the file fails closed with `503`, because leaving a contributor's rules out would allow by omission what those rules refused, and a `404` or an empty file reads to a crawler as permission. A store that cannot give an entry a lifetime is written without one, so the answer is still there to serve. A contributor is asked through the module context, with the request's method, path, host and scheme and without its query or anything about who asked, because its answer is served to every crawler. `[robots-txt]` is refused when the settings load if `modules` is absent, empty or names one thing twice, if a key is not a setting, or if a block belongs to a contributor `modules` does not name. A name nothing supplies is refused when the registry is built, so the file does not fail on the first request for it. --- .../src/integrations/registry.rs | 100 +++ crates/trusted-server-core/src/lib.rs | 1 + .../trusted-server-core/src/module_context.rs | 9 + .../src/robots_txt/contributor.rs | 85 ++ .../src/robots_txt/document.rs | 461 ++++++++++ .../src/robots_txt/holding.rs | 650 ++++++++++++++ .../trusted-server-core/src/robots_txt/mod.rs | 827 ++++++++++++++++++ crates/trusted-server-core/src/settings.rs | 100 +++ 8 files changed, 2233 insertions(+) create mode 100644 crates/trusted-server-core/src/robots_txt/contributor.rs create mode 100644 crates/trusted-server-core/src/robots_txt/document.rs create mode 100644 crates/trusted-server-core/src/robots_txt/holding.rs create mode 100644 crates/trusted-server-core/src/robots_txt/mod.rs diff --git a/crates/trusted-server-core/src/integrations/registry.rs b/crates/trusted-server-core/src/integrations/registry.rs index c7758b0d39..fb37af1a1e 100644 --- a/crates/trusted-server-core/src/integrations/registry.rs +++ b/crates/trusted-server-core/src/integrations/registry.rs @@ -21,6 +21,7 @@ use crate::http_util::is_navigation_request; use crate::middleware::{Middleware, MiddlewareChain, MiddlewarePhase, PhaseEntries}; use crate::module_context::{ModuleCall, ModuleContext, ResolvedRequest}; use crate::platform::{DisabledGeo, PlatformGeo, RuntimeServices}; +use crate::robots_txt::{ALLOW_ALL, REFUSE_ALL, RobotsTxtContributor}; use crate::settings::Settings; /// What a middleware's element handler decides about one attribute of an @@ -672,6 +673,12 @@ pub struct IntegrationRegistration { /// Declaring one does not make it active, because the module is only asked /// to classify a request when `[device] module` names it. pub device_module: Option<(&'static str, Arc)>, + /// The rules this registration adds to `/robots.txt`, with the name + /// `[robots-txt] modules` selects them by. + /// + /// Declaring them does not put them in the file, because the file is only + /// built from the contributors `[robots-txt] modules` names. + pub robots_txt_contributor: Option<(&'static str, Arc)>, } impl IntegrationRegistration { @@ -701,6 +708,7 @@ impl IntegrationRegistrationBuilder { geo_module: None, ec_module: None, device_module: None, + robots_txt_contributor: None, }, } } @@ -782,6 +790,21 @@ impl IntegrationRegistrationBuilder { self } + /// Declare the rules this registration adds to `/robots.txt`, under their + /// `name`. + /// + /// They are only asked for when `[robots-txt] modules` names them. Core + /// holds what they return and writes the file, see [`crate::robots_txt`]. + #[must_use] + pub fn with_robots_txt_contributor( + mut self, + name: &'static str, + contributor: Arc, + ) -> Self { + self.registration.robots_txt_contributor = Some((name, contributor)); + self + } + /// Mark this integration's JS module for deferred loading via /// `