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/https without 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_ref resolves;

  • schema_version must be a JSON number whose value is 1 (1, 1.0 and 1e0 alike, 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

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.