NAME

Langertha::Skeid::Registry - Signed Skeid-to-Skeid capacity snapshots: encoding, signing, verification, mapping

VERSION

version 0.003

SYNOPSIS

# downstream: what GET /skeid/registry/snapshot sends
my ($body, $signature) = Langertha::Skeid::Registry->signed_snapshot($skeid);

# fronting tier: what CapacityProbe::Registry does with it
Langertha::Skeid::Registry->verify($body, $signature, $secret) or die 'bad signature';
my $reading = Langertha::Skeid::Registry->reading_from_snapshot($snapshot, tags => ['local']);

DESCRIPTION

The two halves of the registry (ADR 0017) share one module, so the bytes one side signs are the bytes the other side checks and the mapping to a capacity reading is written down once.

The signature is sha256= plus the hex HMAC-SHA256 of the exact response body, in the "SIGNATURE_HEADER". generated_at and ttl sit inside the signed body, so a snapshot's age cannot be changed without breaking it.

SIGNATURE_HEADER

X-Skeid-Registry-Signature.

SCHEMA_VERSION

The snapshot schema this code writes and accepts: 1.

MIN_SECRET_BYTES

The shortest signing secret either side accepts: 32 bytes, the HMAC-SHA256 output size. A downstream with a shorter one fails its config load; a fronting probe with one reports missing_secret.

encode

my $body = Langertha::Skeid::Registry->encode($snapshot);

Canonical JSON bytes (sorted keys).

sign

my $signature = Langertha::Skeid::Registry->sign($body, $secret);   # 'sha256=...'

sha256= plus the hex HMAC-SHA256 of $body under $secret.

signed_snapshot

my ($body, $signature) = Langertha::Skeid::Registry->signed_snapshot($skeid);

"registry_snapshot" in Langertha::Skeid, encoded and signed with the Skeid's registry_secret. Dies without a secret: a snapshot is never published unsigned.

verify

my $ok = Langertha::Skeid::Registry->verify($body, $signature_header, $secret);

True when the header is the signature of these exact bytes under this secret. The comparison takes the same time wherever the first difference is, so a caller cannot find the signature one character at a time.

reading_from_snapshot

my $reading = Langertha::Skeid::Registry->reading_from_snapshot($snapshot, tags => \@tags);
# { used => 5, limit => 16 }  or  { used => undef, limit => undef }  (no ceiling)

Turns a verified snapshot into the one capacity reading admission consumes for the node that stands for this downstream. Over the downstream nodes that are healthy and carry every tag in tags:

  • free per node is max_conns - inflight, at most limit - used of the node's own capacity reading when it has one with a limit, and 0 while its backoff is pending. Never below 0, never above the node's limit.

  • limit is the sum of the nodes' limits (max_conns, or the reading's limit for a node without one), used is limit minus the summed free.

  • A node with no ceiling at all (max_conns 0 and no limited reading) makes the whole downstream unbounded: the reading has no limit and does not narrow admission.

  • No selected healthy node: reported as full (used 1 of limit 1). That downstream would answer 503, so routing should go elsewhere.

used is the downstream's own number. It is never added to what this process counts in inflight: the requests this process sent are already in it (ADR 0017).

SEE ALSO

"registry_snapshot" in Langertha::Skeid and "registry_enabled" in Langertha::Skeid (publishing), Langertha::Skeid::CapacityProbe::Registry (reading), Langertha::Skeid::Secret

SUPPORT

Issues

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