Skip to content

fodid refuses a URL-safe 51Did that the other five packages accept #40

Description

@jwrosewell

The problem

A page that puts a 51Did in a link writes it in the URL-safe base64 alphabet, with - and _ in place of + and /. Five of the six 51Did packages accept that form on the way in and normalise it before reading. The Rust crate does not, so the same identifier reads in .NET, Java, Node, Python and PHP and fails in Rust.

FodId::from_base64 in fodid/src/fodid.rs hands the string straight to Owid::from_base64, and the OWID reader's engine in owid-rust/src/owid.rs is built on alphabet::STANDARD, with padding indifferent but the alphabet fixed. - and _ are not in that alphabet, so the read fails with an invalid base64 status.

Evidence

Run in a worktree of the feature/usage-accessor branch on 6 September 2026, taking an envelope the test fixture had just signed, swapping the two characters, and reading both forms:

PROBE standard_ok=true url_safe_ok=false differed=true

The probe was removed afterwards and is not committed.

For comparison, .NET has FodId.NormaliseBase64 and applies it inside TryParse before the OWID reader sees the string, and its documentation says the URL-safe form is how a page puts a 51Did in a link. Node, Python and PHP each carry the same normalising helper.

What to do

Normalise the alphabet in FodId::from_base64 before the OWID reader sees the string, the way the other packages do, and cover it with a test that reads the URL-safe form of an identifier the standard form already reads. The choice worth making deliberately is whether the fix belongs in fodid or in owid-rust, since the OWID reader is where the alphabet is decided, and the OWID specification is a SWAN community document rather than ours.

Related, and worth deciding at the same time: the static base64 helpers differ across the six packages, which is recorded in the "One inconsistency, noted but separate" section of issue 39 and its five siblings. Rust has neither helper, which is why this gap exists at all.

The surface every package is expected to expose is at Package surface, and the base64 forms an identifier is exchanged in are in Identifier layout.

Notes

Written with AI assistance on James Rosewell's instruction and needs human review. Nothing has been changed for this issue.

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