NAME

Punk::Observe::Tenant - the tenant seam, kept honest

SYNOPSIS

use Punk::Observe::Tenant;

my $r = Punk::Observe::Tenant::resolve('acme', undef);
die $r->{reason} unless $r->{ok};

DESCRIPTION

Every path this distribution builds is rooted at the store root and every segment header carries a tenant id, so the engine is single-tenant in policy and never in path.

This module does not resolve tenants for a hosted deployment. It keeps the seam from rotting in the meantime, which is what decides whether that deployment is configuration or a rewrite.

The default is a constant

The self-hosted shape is the whole shape: one tenant, named by configuration, resolved by returning it. The hosted shape is the same code with a callback.

If the constant case went through a different path than the callback case, everything the seam was built for would be spent.

A tenant id is never taken from anything a client sends

Not a header, not a query parameter, not a path segment.

That is not a check performed here - it is the absence of a function that would do it. The resolver callback takes an opaque userdata and no request, and there is nothing in this distribution that reads a tenant out of one.

Host code is not trusted

A resolver is a callback the host supplies, and whatever it returns is validated against the same character class as a configured constant before a byte of it reaches a path. An over-long answer is refused, never truncated: shortening an invalid id into a valid one is the same class of mistake as accepting it.

FUNCTIONS

check

my $r = Punk::Observe::Tenant::check($id);

Returns { ok, code, reason }. A tenant id is [A-Za-z0-9_-]{1,64}.

The reason is specific, because the person debugging a resolver wrote the resolver and "invalid tenant" is not a bug report.

resolve

my $r = Punk::Observe::Tenant::resolve($fixed, $coderef_or_undef);

Resolves through the seam and validates the result. Returns { ok, code, reason, tenant }. A bad configured constant is refused at configuration time and says so in at, so a typo is a boot failure rather than a runtime one.

hash

my $h = Punk::Observe::Tenant::hash($id);

The hash a segment header carries.

owns

my $bool = Punk::Observe::Tenant::owns($header_hash, $id);

Whether a segment belongs to this tenant. Checked when the file is opened: a mis-filed segment found at open is an error, one found after it has been read is a disclosure that already happened.

SEE ALSO

Punk::Observe, Punk::Plugin::Observe