Listrar registry

Security

This page describes controls that exist in the product today. Where something is a deployment configuration rather than an implemented control, it says so.

Tenant isolation, in two independent layers

Every business query is scoped to the caller's tenant at the data-access layer, so cross-tenant access cannot happen by omission. Underneath that, PostgreSQL row-level security is applied per request through session variables, giving a database-enforced backstop that does not depend on application code being correct. Both layers must fail for data to cross a tenant boundary.

The public rail

Anonymous visitors reach product passports through a client that sets no tenant, which narrows every read to the public-read policies: published products and their relations. The published-only filter in application code remains, but it is no longer the only thing between a visitor and another tenant's drafts.

Capability-based authorization

Permissions are expressed as capabilities, not role-name comparisons, and one registry drives the dashboard navigation, the page guards and the server-side assertions — so they cannot drift apart. Deny by default: anything not explicitly granted is refused. Changing a record's registry status and recording a verification are separate capabilities from editing the catalogue, because they are different acts.

Audit trail

Security-relevant events — sign-in and sign-out, every product create and update, every registry status change, every verification created or revoked — are written to an append-only log. Append-only is enforced in code, not by convention: the update and delete paths are not available on that table.

API authentication

API keys are stored only as hashes, never in plaintext. Each key carries explicit scopes, and a route refuses a key that lacks the scope it requires. Requests are counted against a per-key monthly quota by tier. Inputs are validated at every boundary before reaching the database.

Where data is hosted

The deployment pins an EU region for hosting and the database, and the project's compliance documentation records EU data residency as its design posture. This is a configuration choice recorded in the repository, not an independent certification, and this page does not claim otherwise.