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 its auth_ref names (which credential it asks for; undef for 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_url within 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's endpoint_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).

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:

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

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.