Skip to content

Deriving

A derived value's label is the join of its parents': the least thing at or above all of them. Join only moves up, so an ordinary derivation cannot weaken a label. That is a theorem, not a rule anyone applies.

Derivation<Invoice, Last4> cardLast4 =
    charter.derivation("invoice.card.last4", INVOICE, LAST4,
        invoice -> new Last4(last4(invoice.cardToken())),
        d -> d.accepting(ctx -> ceiling(ctx, ENDORSED, CARDHOLDER))
              .lowering(joined -> joined.with(SENSITIVITY, PERSONAL))
              .availableTo(ctx -> ctx.has("approves-disputes", "true")));

Three things are settled at declaration and cannot change afterwards, which is what makes the manifest a complete answer:

  • accepting — what this derivation may read. Required.
  • lowering — whether it weakens the label, and how. Optional; its absence is the common case.
  • availableTo — whether it is offered to this access at all. Optional.

One parent or many

Fold is the same operation over a list. Same ceiling, same lowering, same lineage, same audit line — only the arity differs.

Fold<Note, Report> summarise = charter.fold("notes.summarise", NOTE, REPORT, notes -> ..., d -> ...);
Derived<Report> report = summarise.fold(List.of(acmeNote, globexNote));

The join does the quiet job: fold one ordinary note with one containing a home address and the result is PERSONAL, so it reaches the quarantined model and not the vendor's. And it does the loud one: fold two tenants' notes and the tenant axis joins to a mixture that is below no ceiling, so the report exists, remembers both parents honestly, and reaches nobody.

Declassification

Some operations genuinely reduce what a value is. Truncating a card to its last four digits is a real reduction in sensitivity; tying a customer's claim to the mailbox it arrived from is a real increase in trust. Such an operation declares lowering(...), and may weaken only what it declares:

.lowering(joined -> joined.with(INTEGRITY, ENDORSED))

The result must be at or below the join of the parents. It must also still answer every required axis — a relabel that stops mentioning one would be at or below everything, including the label it came from, so atOrBelow cannot see that mistake and a separate check does.

Every declassifying operation appears in the manifest under can WEAKEN a label. That list is the control, because no algebra can tell you whether a check is strong enough: an endorsement that merely confirms a record exists looks exactly like one that ties it to the person who asked. That judgement is human, so the list exists to put it in front of a human.

Derivations that decline

A lookup finds nothing; a check fails. Use checking, whose function returns an Optional and which also sees the access:

Derivation<Mail, Invoice> confirm =
    charter.checking("mail.confirmedInvoice", MAIL, INVOICE,
        (mail, ctx) -> invoices.findFor(mail, ctx),        // Optional<Invoice>
        d -> d.accepting(...).lowering(joined -> joined.with(INTEGRITY, ENDORSED)));

This is the shape that promotes untrusted input. What elevates a claim is agreement with something already trusted — never the fact that it arrived neatly.

Reading happens once, and late

A derivation checks every label before it reads any plaintext. Types, ceilings and the join are all settled from metadata; only then are the permitted values read, in one batch, and handed to the application's function.

If that function then fails — throws, or answers with nothing — the read already happened, so it is recorded as a refusal rather than allowed to escape as an exception. A crash must not be quieter than a decline, and it must not pass for one either: a function that throws is refused as FAILED, one that returns nothing as DECLINED.

Every derivation makes a new value

There is no deduplication and no "deterministic" flag. A derivation function sees the access context, so reusing a result keyed on its parents alone would hand a second caller the first caller's answer without the function running — across tenants. Saving a row is not worth a rule with an exception in it.