Skip to content

The external surface

Two audiences live outside the administration: the one with no account at all, and the one with an account with you who is not one of your employees. The kernel treats them separately, and separately from the administration.

A public page, declared by the backend

from kewos.public_pages import PublicPage, register_public_page
register_public_page(
PublicPage(
slug="booking",
kind="booking",
title="public.booking.title", # an i18n key
config={"service_endpoint": "/public/appointment/services"},
)
)

The public portal reads GET /public/pages/booking, finds the kind, and renders the matching generic component. Your module writes no screen: it declares a kind and a configuration, and rule 48 holds by itself. The config is opaque to the kernel: your module is what knows its shape.

The portal: each customer, their own records

A portal customer is not an administration user. Separate table, separate cookie, separate API scope, and no possible escalation (rules 27 and 32).

To show them something, a module declares which model is accessible to them and through which field they own it:

from kewos.portal.access import register_portal_accessible
register_portal_accessible("library_loan", owner_field="member_id")

Filtering happens in the ORM, not in an endpoint. Every read gets its WHERE member_id = <the signed-in customer>. Run:

members visible to portal customer 1 : [(1, 'Alice Nguyen')]
with no portal context : 4 members

The same code, the same query, two results: the barrier does not depend on where you call from.

An undeclared model is not filtered: it is refused

That is the page’s most important property, and it shows at runtime:

the catalogue (undeclared) : PortalAccessRefusedError
-> model 'library_title' not declared portal-accessible ; portal session refused

Not an empty list, not a filter: a refusal. The default is closed, so a module that forgot to gate an endpoint cannot leak through it: the session refuses the query before it. You decide explicitly what a customer sees; everything else does not exist for them.

A portal customer signs in through a signed link, with no password: a compact HMAC token with a lifetime, verified without writing a row either at issue or at verification. It is not the same mechanism as secure links, which address someone with no account at all, not even a portal one.