Skip to content

Hold 0069's destination set and its redirect rule as values (#69) - #287

Merged
iderex merged 2 commits into
mainfrom
the-set-a-destination-is-in-and-the-redirect-69
Sep 2, 2026
Merged

Hold 0069's destination set and its redirect rule as values (#69)#287
iderex merged 2 commits into
mainfrom
the-set-a-destination-is-in-and-the-redirect-69

Conversation

@iderex

@iderex iderex commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

The issue this belongs to

Refs #69. It reaches part of that issue and its fourth condition is not met, so
no closing keyword is written here on purpose: the last two landings on this
board wrote "does not close #N" and GitHub read the keyword out of the sentence
with the negation discarded, on #107 and again on #29.

What changed

docs/decisions/0069-every-host-the-core-may-contact.md decides three things and
none of them was anywhere in src/. The set of destinations is exactly the
origins of the servers the operator configured. A redirect is followed only where
it stays inside the origin the request was sent to. A name is resolved only for a
host in the set, because a lookup is itself a request carrying the name.

src/server/destinations.rs holds all three as values. Destinations is the
register, empty until somebody configures a server. Destinations::admit
compares a whole origin against it and hands back an AdmittedOrigin, which has
no other constructor. Destinations::follow decides a redirect against the
origin the request went to. WhatARedirectDoes::Refused carries the failure,
built at 0037's mapping point rather than by a caller.

What was there before was the origin type and nothing that used it for this.
Every use of Origin outside the module defining it is 0027's connection
accounting, which counts requests against an origin rather than deciding whether
one may be reached:

git grep -nE '(^|[^A-Za-z_])Origin([^A-Za-z_]|$)' HEAD -- src/ | grep -v 'src/server/address.rs' | grep -v 'src/server/destinations.rs'
HEAD:src/lib.rs:156:    any_thread::<server::address::Origin>();
HEAD:src/server/transport.rs:48:use crate::server::address::Origin;
HEAD:src/server/transport.rs:259:    against: Vec<(Origin, usize)>,
HEAD:src/server/transport.rs:298:    pub fn start(&mut self, origin: &Origin) -> Result<(), Waits> {
HEAD:src/server/transport.rs:326:    pub fn end(&mut self, origin: &Origin) {
HEAD:src/server/transport.rs:338:    pub fn against(&self, origin: &Origin) -> usize {
HEAD:src/server/transport.rs:395:    kept: Vec<(Origin, C, SteadyInstant)>,
HEAD:src/server/transport.rs:412:    pub fn keep(&mut self, origin: Origin, connection: C, now: SteadyInstant) {
HEAD:src/server/transport.rs:428:    pub fn take(&mut self, origin: &Origin, now: SteadyInstant) -> Option<C> {
HEAD:src/server/transport.rs:529:    use crate::server::address::{BaseAddress, Origin};
HEAD:src/server/transport.rs:545:    fn origin(typed: &str) -> Origin {

What failure it prevents

The one 0069 was written before the code in order to stop, and it has not
happened here because there is no transport yet. Following a redirect anywhere is
the default in every HTTP client anybody would reach for, it is one line of
configuration, it is invisible in every test against a fake server that does not
redirect, and the first time it matters is an operator whose server redirects
artwork to a content network. At that point the core has been sending an address
and a user agent to a host the operator never chose for as long as that
deployment has existed, and on a self-hosted server the address frequently says
where the person lives.

The second is the resolution order. Checking the host after resolving it reads as
equivalent and is not, and the difference is visible only to somebody watching the
network rather than reading the code. AdmittedOrigin is where that order is
written down: the host a resolver may be given comes off a value the comparison
produced.

What I decided that 0069 left open

A redirect is compared against the origin the request went to and never against
the whole set.
0069 follows a redirect only where it stays inside the origin
that was already in the set, and refuses one going anywhere else. Read exactly,
that refuses a configured server redirecting to a SECOND configured server. Read
conveniently, it admits it. I took the exact reading, because what the convenient
one admits is one server deciding that another answers for it, and
docs/decisions/0101-what-the-core-trusts.md places that decision with the
operator: the person is trusted for which servers the core may talk to, and a
server is not trusted to move a request onto one of them.

The offset a refused redirect reports is the start of the location. 0004 fixes
the payload as the site, what was expected, and an offset, and forbids the bytes
there. A refused redirect consumed none of what it was handed, so the offset is
the start rather than a position inside something partly parsed. It is a named
constant carrying that reason rather than a bare zero.

Evidence

Read at the commit being pushed.

git rev-parse HEAD
965e1a35fbad4069b7519fe346c6f70af833f20a

git show HEAD:src/server/destinations.rs | grep -n '^pub const \|^pub struct \|^pub enum \|    pub fn \|    pub const fn '
58:pub const A_REFUSED_REDIRECT_CONSUMED_NOTHING: usize = 0;
74:pub struct AdmittedOrigin {
81:    pub const fn origin(&self) -> &Origin {
92:    pub fn host(&self) -> &str {
105:pub enum WhatConfiguringDid {
120:pub enum WhatARedirectDoes {
139:pub struct Destinations {
158:    pub const fn new() -> Self {
177:    pub fn configured(&self, origin: Origin) -> WhatConfiguringDid {
191:    pub fn forgotten(&self, origin: &Origin) -> bool {
203:    pub fn rows(&self) -> usize {
212:    pub fn all(&self) -> Vec<Origin> {
229:    pub fn admit(&self, origin: &Origin) -> Option<AdmittedOrigin> {
256:    pub fn follow(&self, sent_to: &AdmittedOrigin, location: &Origin) -> WhatARedirectDoes {

cargo test --locked --lib server::destinations
test result: ok. 14 passed; 0 failed; 0 ignored; 0 measured; 501 filtered out; finished in 0.00s

cargo build --locked --all-targets
Finished `dev` profile [unoptimized + debuginfo] target(s) in 2.52s

cargo test --locked | grep -c '^test result: ok'
9

The gate legs that run without a network on this machine, each printing its own
verdict line:

bash .github/format/format.sh check | tail -1
Every tracked source file above is written the way the formatter would write it.

bash .github/invariants/invariants.sh check | tail -1
Every rule above was applied to its subject and refused nothing.

bash .github/doc-paths/doc-paths.sh check | tail -1
Every path these documents name resolves against the tracked set.

bash .github/lint/lint.sh check | tail -1
Every lint the groups above carry was refused, outside the register printed with it.

bash .github/excluded-targets/excluded-targets.sh check | tail -1
Every target the ordinary command leaves out compiles.

What a guard here refuses, and the proof it bites

Six deliberate violations, each a one-change neighbour of what landed, each run
with cargo test --locked --lib server::destinations. The green run is the one
under Evidence above: 14 passed, 0 failed.

Comparing hosts rather than whole origins, which is the alternative 0069
prices by name. .find(|held| *held == origin) became
.find(|held| held.host() == origin.host()):

test result: FAILED. 12 passed; 2 failed; 0 ignored; 0 measured; 501 filtered out
the_scheme_is_part_of_the_comparison ... FAILED
a_port_that_was_typed_and_a_port_that_was_not_are_two_origins ... FAILED

Following a redirect to anything in the set, which is the convenient reading
of 0069's sentence. An if let Some(admitted) = self.admit(location) arm was put
in front of the comparison:

test result: FAILED. 13 passed; 1 failed; 0 ignored; 0 measured; 501 filtered out
a_redirect_to_a_second_configured_server_is_refused_too ... FAILED

The redirect rule comparing hosts. location == sent_to.origin() became
location.host() == sent_to.origin().host():

test result: FAILED. 12 passed; 2 failed; 0 ignored; 0 measured; 501 filtered out
a_redirect_downgrading_the_scheme_on_the_same_host_is_refused ... FAILED
a_redirect_to_another_port_on_the_same_host_is_refused ... FAILED

Forgetting one server emptying the set, which is the shortest implementation
of removal. held.retain(|kept| kept != origin) became held.clear():

test result: FAILED. 13 passed; 1 failed; 0 ignored; 0 measured; 501 filtered out
forgetting_one_server_leaves_every_other_row_standing ... FAILED

A second sign-in against one server adding a second row. The contains arm
was deleted:

test result: FAILED. 13 passed; 1 failed; 0 ignored; 0 measured; 501 filtered out
what_was_typed_is_compared_after_the_parse_rather_than_before_it ... FAILED

An unconfigured core reading as unrestricted rather than closed, which is the
mistake 0068 and #70 are about. An early if self.rows() == 0 arm returning the
origin was added:

test result: FAILED. 13 passed; 1 failed; 0 ignored; 0 measured; 501 filtered out
a_set_nobody_configured_admits_nobody ... FAILED

What this does not cover

It refuses nothing that is happening. The refusal 0069 wants is a request not
being sent, and nothing in this crate sends one. What is here answers correctly
about an origin and produces the failure value a caller would hand back. Whether
any caller asks is #27 for the transport and #70 for the test.

Nothing consumes an AdmittedOrigin. The property that a connect written
against Origin is one that skipped the comparison is available rather than held:
it starts holding on the day something connects. Origin still hands its host to
anybody who has one, and this change does not narrow that, because that parse is
0028's rather than 0069's.

0069 says the refused location is named in the payload, and the landed failure
type cannot carry it.
This is a disagreement between two records that I met by
building and did not resolve:

git show HEAD:docs/decisions/0069-every-host-the-core-may-contact.md | tr '\n' ' ' | tr -s ' ' | grep -o 'its payload shape carries[^.]*\.'
its payload shape carries what was being read and where reading stopped, which is where the refused location is named.

git show HEAD:src/failure/mod.rs | sed -n '552,563p'
    AnswerNotUnderstood {
        /// What the core was reading, from the declared set.
        site: ReadingSite,
        /// What it expected there.
        expected: Expected,
        /// Where in the answer it stopped, as an offset. Never the bytes at that
        /// offset: 0004 forbids carrying the answer, because an answer holds
        /// library contents and may hold a token.
        stopped_at: usize,
        /// The token this module builds and nothing else can.
        constructed: Constructed,
    },

The payload carries an offset and forbids the bytes at it, and a Location header
is part of the answer, so the location cannot be named there at all. A client is
told that a cross-origin redirect was refused and is not told where to. Nothing
here changes either record, and no issue is opened for it: it is written in this
body and named on #69.

No Scope: line exists on #69, so the hygiene check prints that the path
comparison was not made rather than making it.

The targets leg did not run here. It compiles the library for 0113's seven
triples and this machine has no cross-compilation set up for them; the gate is
where that verdict comes from.

The thread-detector leg did not run here. It uses a nightly compiler this
machine does not carry.

Nothing is measured. No number about what this comparison costs is in this
body, and #65 is where a measured one would come from.

The empty set is proven against an empty register and not against a running
core.
There is no core to start, so nothing shows that a core comes up with the
set empty. What is shown is that the only constructor produces an empty one and
that the only way a row arrives is a caller supplying an origin.

Who has read it

Nobody other than me. There was no second reader available for it, and the
evidence above stands in place of one.

0069 decides that the set of destinations is exactly the origins of the
servers the operator configured, that a redirect is followed only where it
stays inside the origin the request was sent to, and that a name is resolved
only for a host in the set. None of the three was anywhere in the tree: every
use of Origin outside the module defining it was 0027's connection accounting,
which counts requests against an origin rather than deciding whether one may be
reached at all.

src/server/destinations.rs holds the set, the comparison and the redirect rule.
A set nobody configured admits nobody, which is 0068's sentence and what makes
the check in #70 unambiguous. The comparison is over the whole origin, so a
scheme downgrade and a second port on one host are two origins rather than one
host. A redirect is compared against the origin the request went to and never
against the whole set, so one configured server cannot move a request onto
another one: 0101 places that decision with the operator, and a server asking
is not the operator answering. The refusal is built at 0037's mapping point and
carries ReadingSite::CrossOriginRedirectRefused, which is the value that keeps
0004's reversal measurement three populations rather than one count.

The failure this prevents is the one 0069 was written before the code to stop:
following a redirect anywhere is one line of configuration on an HTTP client,
it is invisible in every test against a fake server that does not redirect, and
the first time it matters is an operator whose server redirects artwork to a
content network, at which point an address that frequently says where somebody
lives has been going to a third party for as long as that deployment existed.

AdmittedOrigin is produced by the comparison and by nothing else, so a later
connect written against Origin is one that skipped it. Nothing consumes one
today, because nothing here connects, resolves or requests.

What #69 still needs is its fourth condition, which is the check in #70, and
that check does not exist. #70's own last reading says what it waits on is the
comparison being written rather than an issue; this is that comparison.

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
src/server/ is an area in .github/coverage/pinned-surface, and the rule that
makes that register hold is that every tracked .rs file under an area is on the
surface and the run refuses one that is not listed. The module the previous
commit added was not, and the check said so rather than measuring it:

    src/server/destinations.rs is on the pinned surface and is not listed in
    .github/coverage/pinned-surface.

The row carries the reason on the same line as the path, which is what that
register asks for: what a defect there does is send an address and a device
identity to a host the operator never chose, or admit a host on a core nothing
has been configured on, where 0068 says the set is empty.

Signed-off-by: Nils Lehnen <30603423+iderex@users.noreply.github.com>
@iderex
iderex merged commit 2df6319 into main Sep 2, 2026
27 checks passed
@iderex
iderex deleted the the-set-a-destination-is-in-and-the-redirect-69 branch September 2, 2026 17:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant