Skip to content

How Ihawu Works

Ihawu has a small, deliberate model. Four ideas cover almost everything.

A resource is a type you want masked, marked with @IhawuResource("name"). The name is the key policies resolve against. Types without the annotation serialize unchanged.

A policy maps a resource and a caller’s role(s) to a list of field policies. Resolution is a pure lookup: given the caller’s IhawuPrincipal and the resource name, Ihawu asks a ResourcePolicyResolver for the field policies that apply, and enforces exactly those. Policies can come from static configuration or a dynamic source (database, OPA) behind a simple provider SPI.

Each field policy carries a masking strategy:

  • HIDE removes the field from the output entirely.
  • REDACT replaces the value with a placeholder (e.g. ***-**-****).

When multiple roles apply to a field, the stricter strategy wins.

Ihawu draws a careful line between two kinds of “nothing matches”:

  • Missing identity — no verified principal on the call. Ihawu fails closed and emits {}. No identity, no data.
  • Missing policy — a principal is present, but no rule restricts a field. Ihawu fails open for that field: masking is a denylist, so unconfigured fields are public by design.

This asymmetry — fail closed on missing identity, fail open on missing policy — keeps Ihawu safe without turning it into an allowlist that would require a rule for every field just to see any data.

Ihawu enforces the decisions your policy source returns; it never evaluates authorization conditions itself. That separation is what lets it stay a small, auditable layer at the serialization boundary rather than a second, competing authorization system.