NAME
Langertha::Raider::Provider::Change - Internal classification of what changed between an accepted and a newly fetched provider manifest
VERSION
version 0.503
SYNOPSIS
# Internal to Langertha-Raider -- no API promise.
my $class = 'Langertha::Raider::Provider::Change';
my $got = $class->classify(
old => $accepted_manifest, old_origin => 'https://provider.example',
new => $fetched_manifest, new_origin => $report->{final_url},
);
# { needs_consent => 1,
# changes => [ { kind => 'endpoint_origin', consent => 1, endpoint => 'chat',
# description => "endpoint 'chat': origin ... -> ..." }, ... ],
# old_fingerprint => '3f2a...', new_fingerprint => '9b1c...' }
my $hex = $class->fingerprint($manifest, $origin);
DESCRIPTION
Internal module. Its interface may change without notice.
A provider manifest (ADR 0007) may change after the user accepted it. Not every change is a new trust decision: a model list may change without renegotiating anything, while a change of origin or auth mechanism must be accepted again (docs/RAIDER-REDESIGN-HANDOFF.md 11.2). This class tells the two apart. It is pure logic: it stores nothing and asks nobody; keeping an accepted manifest and asking again is the provider binding's job.
The consent view ("consent_view") is the part of a manifest a consent covers. Every change to it needs renewed consent, every other change does not -- so needs_consent of "classify" is true exactly when the two "fingerprint"s differ. The view holds:
origin -- the origin the manifest was fetched from. Another origin is another party, whatever the document says.
issuer -- compared as the exact string, as OpenID Connect compares issuers: it is the identity the provider claims.
provider_id -- the name the provider was accepted under and is shown by. A manifest cannot rename itself under an existing consent.
every endpoint by its id, with the origin of its
base_url(where requests and the credential go), its dialect (how raider talks to it) and the type of the auth entry itsauth_refnames (which credential it asks for;undeffor none). Every endpoint counts, not only the ones a model uses today: a model list change needs no consent and can put any endpoint into use. An endpoint added or removed is a change to the view. Removing one narrows trust, but the accepted binding no longer describes the provider, so it is asked again as well -- this keeps "needs consent" and "fingerprint changed" the same statement.
Changes that need no consent, reported so nothing changes silently:
the path of an endpoint's
base_urlwithin the same origin (endpoint_path) -- the origin is the trust boundary, not the path;the id of the auth entry an endpoint names when its type stays (
endpoint_auth_ref) -- the id is a label, the mechanism is what counts;auth entries no endpoint of the new manifest names (
auth_added,auth_removed,auth_type) -- they reach no endpoint; one that does is reported as the endpoint'sendpoint_auth;models added or removed, also from one endpoint to another (
model_added,model_removed) -- a model entry is its id on its endpoint;a model's declared capabilities (
model_capabilities) -- a capability is what the provider claims, never what raider authorises (ADR 0007: the four states stay separate; ADR 0005: permissions never come from a manifest). It changes what raider sends to an origin already accepted, not where anything goes;extensions-- inert in manifest v1. Once extensions carry references to MCP servers or packs, installing, loading and running them needs local policy of its own (ADR 0007).
consent_view
my $view = $class->consent_view($manifest, $origin);
The part of $manifest (a Langertha::Manifest) a consent covers, as plain data (see "DESCRIPTION"). $origin is the origin -- or any URL of it, such as the final URL of the fetch -- the manifest was fetched from. Croaks when $manifest is not a manifest or $origin names no host.
fingerprint
my $hex = $class->fingerprint($manifest, $origin);
The SHA-256 (hex) of the canonical JSON of "consent_view" -- the canonical form of "digest_arguments" in Langertha::Raider::Approval: keys sorted at every depth, UTF-8. The same for the same view whatever the key order of the manifest document, the order of its endpoints, or any change that needs no consent.
classify
my $got = $class->classify(
old => $accepted, old_origin => $accepted_origin,
new => $fetched, new_origin => $fetched_origin,
);
Compares an accepted manifest with a newly fetched one, each with the origin it was fetched from. Resolves to a hash:
changes-- one hash per change, in a stable order (origin, issuer, provider_id, endpoints by id, auth entries by id, models by endpoint and id, extensions):kind,consent(1or0),description(one line for a person) and, where it applies,endpoint,authormodelnaming what changed. Empty when nothing changed.needs_consent--1when any change needs renewed consent, else0.old_fingerprint,new_fingerprint-- the "fingerprint"s; they differ exactly whenneeds_consentis1.
Kinds that need consent: origin, issuer, provider_id, endpoint_added, endpoint_removed, endpoint_origin, endpoint_dialect, endpoint_auth. Kinds that do not: endpoint_path, endpoint_auth_ref, auth_added, auth_removed, auth_type, model_added, model_removed, model_capabilities, extensions. Croaks on a missing or invalid argument.
SEE ALSO
Langertha::Raider::Provider::Activation --
raider --provider, which accepts nothing beyond one runLangertha::Raider::Provider::Fetch -- where a manifest and its origin come from
Langertha::Manifest -- the manifest's schema and validator (core)
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/Getty/langertha-raider/issues.
IRC
Join #langertha on irc.perl.org or message Getty directly.
CONTRIBUTING
Contributions are welcome! Please fork the repository and submit a pull request.
AUTHOR
Torsten Raudssus <torsten@raudssus.de> https://raudssus.de/
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.