How Ihawu Works
Ihawu has a small, deliberate model. Four ideas cover almost everything.
Resources
Section titled “Resources”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.
Policies
Section titled “Policies”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.
Strategies
Section titled “Strategies”Each field policy carries a masking strategy:
HIDEremoves the field from the output entirely.REDACTreplaces the value with a placeholder (e.g.***-**-****).
When multiple roles apply to a field, the stricter strategy wins.
Fail-closed vs. fail-open
Section titled “Fail-closed vs. fail-open”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.
Enforcement point, not decision point
Section titled “Enforcement point, not decision point”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.