NAME

Langertha::Knarr::Authorizer - Role for who may use a Knarr, and with which model

VERSION

version 1.103

SYNOPSIS

package My::Authorizer;
use Moose;
with 'Langertha::Knarr::Authorizer';

sub identify {
  my ( $self, $http_req, $action ) = @_;
  return { identity => {} } if $http_req->header('X-Trusted');
  return { status => 401, message => 'unauthorized' };
}
sub authorize      { return }                    # every model
sub visible_models { $_[2] }                     # every listed model
sub forwards_client_key { 1 }

__PACKAGE__->meta->make_immutable;

my $knarr = Langertha::Knarr->new(
  handler    => $handler,
  authorizer => My::Authorizer->new,
);

DESCRIPTION

Decides who may use a Langertha::Knarr, in two phases:

1. Identify -- who is asking. Runs once the route is known, before the body is read: a client that cannot identify itself learns no 400, no 404 and no model name. A request that presents Knarr's admin key to a route that is not an admin route never gets here: Langertha::Knarr answers it with 401 first, whatever the authorizer.
2. Authorize -- may this identity do this action with this model. Runs once the action and the model are known: after the chat request was parsed, with the model of a POST /api/show body. A request that names no model gets the identity's own model first, when it has one ("apply_grant" in Langertha::Knarr::Request).

Every refusal is answered by Knarr in the client protocol's error shape ("ERRORS" in Langertha::Knarr), on the native server and under Langertha::Knarr::PSGI alike.

The identity is a plain HashRef. It goes to the handler chain as "grant" in Langertha::Knarr::Request. It must never carry a secret: no key, no token, no hash of one.

Two authorizers ship with Knarr: Langertha::Knarr::Authorizer::StaticKey ("auth_token" in Langertha::Knarr, the default) and Langertha::Knarr::Authorizer::Token (one short-lived token per model, checked against a Langertha::Knarr::TokenStore).

identify_f

my $result = await $authorizer->identify_f( $http_req, $action );

Returns a Future that is done with the result of the identify phase and never fails:

{ identity => \%identity }                       # go on
{ status => 401, message => 'unauthorized' }     # refused

The native server uses it, so a check that has to ask something (a database) does not hold the event loop. The default calls "identify".

identify

my $result = $authorizer->identify( $http_req, $action );

Required. The same result, synchronously (Langertha::Knarr::PSGI, where the request may block). $http_req answers ->header($name); $action is the route's action (chat, models, a2a_card, ...).

authorize

my ( $status, $message ) = $authorizer->authorize( $identity, $action, $model );

Required. Empty list when the identity may do $action with $model (undef when the action has none), else the status and the client's message of the refusal.

visible_models

my $models = $authorizer->visible_models( $identity, \@models );

Required. The models a model list (/v1/models, /api/tags, ACP /agents, /api/show) shows this identity, from the list the handler or router gives, as hashes with an id (the shape "format_models_response" in Langertha::Knarr::Protocol takes).

forwards_client_key

Required. True when the client's own key may reach an upstream: then the raw passthrough runs and a passthrough handler forwards the client's Authorization and x-api-key (Knarr's proxy key taken out). False: the raw passthrough never runs, and both headers are dropped from every header set Knarr forwards.

start

$authorizer->start($loop);

Called by Langertha::Knarr once a process starts serving, with the IO::Async::Loop it serves on: by "start" in Langertha::Knarr with one worker, in every forked worker otherwise -- never in the prefork supervisor, whose connections every worker would share. An authorizer that needs a connection opens it here, without blocking, so the first request does not wait for it. It may be called again in the same process (an admin push hands the same authorizer over again) and must then do nothing. The default does nothing. Langertha::Knarr::PSGI does not call it.

SUPPORT

Issues

Please report bugs and feature requests on GitHub at https://github.com/Getty/langertha-knarr/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.