Skip to content

An adapter with no geo provider fails closed on Edge Cookie creation and on the auction #1134

Description

@jwrosewell

On an adapter with no geo provider the jurisdiction is always Unknown, and Unknown fails closed on both Edge Cookie creation and the server-side auction. So neither of those can run, and there is no configuration that lets an operator say what to assume.

Both halves are documented on their own, and the Axum adapter's own source puts them together in a comment (crates/trusted-server-adapter-axum/src/app.rs:156-159, which says a no-op geo leaves the jurisdiction Unknown and fails the auction consent gate closed). We could not find an operator-facing page that does.

The chain, at 0f8b44dc0

The Axum adapter's geo lookup always returns None (crates/trusted-server-adapter-axum/src/platform.rs:189-193), which docs/guide/architecture.md:59 states as a limitation. Spin injects NullGeo (crates/trusted-server-adapter-spin/src/platform.rs:747), whose lookup returns Ok(None) (:766-772), and that wiring sits outside every #[cfg], so it applies to the real runtime as well. Spin is described as production-capable at architecture.md:68 and its limitations do not mention geo.

With no geo, detect_jurisdiction returns Unknown (consent/jurisdiction.rs:48-51, and its own test no_geo_returns_unknown).

Unknown then fails closed twice:

// consent/mod.rs:590-591
        // Fail-closed: block EC creation as a precaution.
        jurisdiction::Jurisdiction::Unknown => false,
// consent/mod.rs:341-350
    let requires_tcf_purpose1 = ctx.gdpr_applies
        || matches!(
            ctx.jurisdiction,
            jurisdiction::Jurisdiction::Gdpr | jurisdiction::Jurisdiction::Unknown
        );
    if !requires_tcf_purpose1 {
        return true;
    }
    effective_tcf(ctx).is_some_and(|tcf| tcf.has_purpose_consent(1))
}

Observed on an Axum deployment across fifteen publisher sites, every request logged should_run_auction=false with consent_allows_auction=false, until a TCF consent cookie was supplied, after which nine slots filled on one page. So on one of these adapters the server-side auction never runs unless a TCF consent string granting purpose 1 reaches it, which in practice means a consent management platform in front of it.

Neither fail-closed rule is wrong. edge-cookies.md:119 and :125 state the Edge Cookie one as the design, and the auction gate's own doc comment says only a known non-GDPR jurisdiction with no EU TCF signal is freely allowed. The gap is that nothing tells an operator that choosing an adapter without a geo source means neither will run.

The question

Should there be a way for an operator to declare the jurisdiction to assume when no geo source is available?

We ask because we have built one in our own pull requests and would rather you saw the shape before it arrives than after.

In #1045 the assumed jurisdiction is the jurisdiction: on the top node of the rules tree in the compiled-in permission policy file, beside the permission baseline for the same visitor, so one file states the policy for both. detect_jurisdiction returns that value when there is no geo instead of Unknown, and it is carried into the consent context that the auction gate reads, so both subsystems see the same declaration. A deployment that runs an Edge Cookie provider without a geo provider must also set [geo] assume_single_jurisdiction = true, checked at startup, so serving one jurisdiction is an explicit operator decision rather than an accident of the defaults. With the shipped policy file's jurisdiction: gdpr at the top the auction gate still requires TCF purpose 1, by the deployer's own declaration, and a deployer whose file names a non-regulated jurisdiction there gets the auction without it. #779 and #781 are the issues that specified it.

So the design question is whether that shape, a declared assumed jurisdiction read in one place by both subsystems and an explicit acknowledgment that it is being relied on, is the one you want. If it is not, we would rather change ours than land it and argue.

Scope notes

The Spin half is read from source and not run, because on main the Spin adapter answers every request except the health probe with a 503 (#1101), so the condition cannot be observed there yet.

No branch is offered here beyond the pull request already open.


Written with AI assistance. The chain above was checked against the source at 0f8b44dc0, the description of our own change against the head of #1045, and the auction behavior was observed on an Axum deployment. Worth a human review before acting on it.

Activity

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