NAME
Langertha::Manifest - Provider manifest (/.well-known/langertha.json) value object, parser and validator
VERSION
version 0.503
SYNOPSIS
use Langertha::Manifest;
# Parse (a JSON string or an already-decoded hashref) -- validated or croaks
my $manifest = Langertha::Manifest->from_json($json_text);
my $manifest = Langertha::Manifest->from_hash(\%data);
for my $model ( @{ $manifest->models } ) {
my $endpoint = $manifest->endpoint( $model->endpoint_ref );
next unless $endpoint->is_known_dialect;
printf "%s via %s at %s (tools: %s)\n", $model->id, $endpoint->dialect,
$endpoint->base_url, $model->supports('tools_native') ? 'yes' : 'no';
}
# Serialize
my $hashref = $manifest->to_hash;
my $json = $manifest->to_json; # canonical, byte-stable
# Build one from a configured engine
use Langertha::Manifest::Builder;
my $manifest = Langertha::Manifest::Builder->from_engine($engine);
DESCRIPTION
The data model of the provider manifest served at /.well-known/langertha.json: a declarative description of what a provider exposes — endpoints with their wire dialect, auth mechanisms, model ids and declared capabilities. Langertha core owns the schema, these value objects, the parser/validator and the Langertha::Manifest::Builder; fetching, trust, aliases and secret binding belong to the client (langertha-raider), publishing to the servers (langertha-knarr, langertha-skeid). Core does no network I/O here.
Schema version 1 carries exactly: schema_version (1), kind (langertha-provider), provider_id, issuer, endpoints, auth, models and extensions. Validation is strict:
a field outside the schema is rejected — at the top level and in every endpoint, auth and model entry;
a command-, code-, secret- or prompt-shaped field (
command,exec,engine_class,api_key,secret_path,env,system_prompt,mcp_servers,tools, …) is rejected explicitly with a message saying why — a manifest never carries those;URLs are printable-ASCII
http/httpswithout userinfo, query or fragment (best effort: a secret embedded in the path itself cannot be detected);ids and model ids carry no control or format characters (a client prints them);
ids are unique and every
auth_ref/endpoint_refresolves;schema_versionmust be a JSON number whose value is1(1,1.0and1e0alike, on every JSON backend; the string"1"is not); any other version is rejected before anything else is checked.
extensions is inert: it must be an object of plain JSON data, and it is kept and serialized exactly as given, never validated further or interpreted by core.
Values, unlike structure, are open: an unknown dialect, auth type or capability name is accepted. is_known_dialect / is_known_type tell a client whether it has an adapter; a client treats a capability it does not know as absent.
A manifest states what the provider claims. It is not a probe result and grants no local permission: a model claiming tools_native does not authorise running local tools.
provider_id
Stable provider slug, [a-z0-9][a-z0-9._-]* (max 128).
issuer
Origin of the publisher, an http/https URL.
endpoints
ArrayRef of Langertha::Manifest::Endpoint; at least one.
auth
ArrayRef of Langertha::Manifest::Auth; may be empty.
models
ArrayRef of Langertha::Manifest::Model; may be empty (a filtered manifest can legitimately list none).
extensions
HashRef, inert: never validated beyond "plain JSON data" and never interpreted by core; serialized exactly as given. It is deep-copied on construction and every read returns a fresh copy, so mutating the input or the returned structure does not change the manifest. A blessed object (other than a JSON boolean) or a code reference in it is rejected.
schema_version
Always 1: the only schema version this Langertha reads and writes.
kind
Always langertha-provider.
from_json
my $manifest = Langertha::Manifest->from_json($json_bytes);
Decodes UTF-8 JSON text and hands it to "from_hash". Croaks on invalid JSON and on every validation failure.
from_hash
my $manifest = Langertha::Manifest->from_hash(\%data);
Validates a decoded manifest document and returns the object. Croaks with Langertha::Manifest: <path>: <reason> on the first violation.
endpoint
my $endpoint = $manifest->endpoint('chat');
The endpoint with that id, or undef.
auth_entry
my $auth = $manifest->auth_entry( $endpoint->auth_ref );
The auth entry with that id, or undef.
models_for_endpoint
my @models = $manifest->models_for_endpoint('chat');
The model entries served on that endpoint.
to_hash
The manifest as a plain Perl data structure, ready for any JSON encoder. Capability values are JSON booleans; extensions is returned as given.
to_json
UTF-8 JSON with sorted keys (canonical), so the output is byte-stable: to_json is a fixed point after one roundtrip (from_json($m->to_json)->to_json eq $m->to_json). Input that omitted optional sections comes back with their defaults (auth, models, extensions, capabilities) filled in, so the first serialization of such input is not byte-identical to it.
SEE ALSO
Langertha::Manifest::Builder - Builds a manifest from configured engines
Langertha::Manifest::Endpoint, Langertha::Manifest::Auth, Langertha::Manifest::Model - The entries
Langertha::Role::Capabilities - The capability vocabulary
SUPPORT
Issues
Please report bugs and feature requests on GitHub at https://github.com/Getty/langertha/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 <getty@cpan.org>
COPYRIGHT AND LICENSE
This software is copyright (c) 2026 by Torsten Raudssus https://raudssus.de/.
This is free software; you can redistribute it and/or modify it under the same terms as the Perl 5 programming language system itself.