Ceilings¶
A ceiling is what a reader is entitled to see. A label is what a value is. Reading is permitted when the label is at or below the ceiling, on every axis the label speaks to.
Ceiling ceiling = Ceiling.of(TENANT, Constraint.atMost("acme"))
.with(INTEGRITY, Constraint.any())
.with(SENSITIVITY, Constraint.atMost(PERSONAL));
Two constraints, and the difference matters:
| constraint | means |
|---|---|
Constraint.atMost(v) |
this axis may be v or anything below it |
Constraint.any() |
this axis may be anything — except a mixture |
any() still refuses a mixture. A reader who is broad about tenants is not thereby entitled to
something belonging to two of them at once.
Silence is not permission¶
A label that speaks to an axis the ceiling never constrained is refused.
Ceiling ceiling = Ceiling.of(TENANT, Constraint.atMost("acme"));
// a value labelled with SENSITIVITY as well is refused: this ceiling never said anything about it
This is the fail-closed direction, and it is what makes adding an axis safe. Declare a fourth axis and every existing ceiling stops admitting values that speak to it, until somebody says what it should allow. The alternative — treating an unmentioned axis as "anything" — means a new axis is invisible to every gate on the day it ships.
Say any() when you mean it. Breadth is a decision worth writing down.
Ceilings usually depend on who is asking¶
A portal's ceiling is a function of the access, not a constant:
charter.sink(
"support-ui",
ctx -> Ceiling.of(TENANT, Constraint.atMost(ctx.get("tenant").orElseThrow()))
.with(SENSITIVITY, Constraint.atMost(ORDINARY)),
INVOICE);
The tenant comes from ambient context, never from anything a caller passes. If a call site could override what the edge established, any code holding a portal could name itself whichever tenant it liked, and the gate would agree — which is not a policy system but a formality.
A ceiling that cannot decide has not said yes¶
Ceilings are application code, and application code fails. A ceiling that throws, or that returns
null — a ctx.get("tenant").map(...).orElse(null) on a request that had no tenant — is treated
as a refusal, and the refusal is recorded.
There is deliberately no way to express "this operation accepts anything". A derivation or a query
that never calls accepting(...) is refused at configuration time, so a ceiling is always present
and always consulted.
Ceilings are not authority to act¶
A ceiling answers may this value be seen here. It does not answer should this happen. A capability to refund a card is not a statement about disclosure, and occlude has nothing to say about the amount. Pair it with whatever authorises actions in your system — see What Occlude Does Not Do.