Opaque references to sensitive values, for Java.
Some values should not simply become text: personal and regulated data, credentials, untrusted
input from outside. Once such a value is a String in your application, nothing can tell you where
it went — a log line, an event, a prompt.
Occlude takes custody of the value and hands back an occluded reference. It travels anywhere, because holding one is not permission to read it. Turning it back into a value is the one checked operation, and it always names where the value is going.
Occluded<Mail> mail = customerMail.occlude(incoming); // the real thing stays here
vendorLlm.reveal(mail); // Denied — above that sink's ceiling
quarantinedLlm.reveal(mail); // AllowedBuilt on Denning's lattice model and on the object-capability model: authority is held, never
looked up. There is no registry and no charter.get("customer-mail"). Code can perform an
operation because something handed it the portal that performs it — so "what can this class do?" is
answered by reading its constructor parameters.
<dependency>
<groupId>org.jwcarman.occlude</groupId>
<artifactId>occlude-spring-boot-starter</artifactId>
<version>0.2.0</version>
</dependency>That is the Spring Boot path; add a Postgres driver beside it. Plain Java, the BOM, and trying it in memory are in Getting Started. Occlude's encryption and serialisation come from codec, a small library it depends on.
An application declares axes — the questions it asks about every value, such as a tenant, a sensitivity, whether it was vouched for — and every value carries a label answering them. Every place a value may go declares a ceiling, and reading is decided against both. Deriving a value joins its parents' labels, so an ordinary derivation cannot weaken one, and a value made from two tenants' data carries a mixture no ceiling admits.
DefaultCharter charter = new DefaultCharter(Axes.of(TENANT, SENSITIVITY));
Occlude<Mail> customerMail = charter.source("customer-mail", MAIL,
ctx -> Label.of(TENANT, ctx.get("tenant").orElseThrow()).with(SENSITIVITY, PERSONAL));
Reveal<Mail> supportDesk = charter.reveal("support-desk",
ctx -> Ceiling.of(TENANT, Constraint.atMost(ctx.get("tenant").orElseThrow()))
.with(SENSITIVITY, Constraint.atMost(PERSONAL)), MAIL);
charter.bind(Bindings.of(storage).withIdentity(currentAccess));A charter is where authority is declared, and each declaration hands back the one object able to perform it — a portal. Holding the portal is the only way to perform the operation; nothing can look one up. Every operation writes a line to a tamper-evident record, allowed and refused alike. Storage is Postgres, and everything it keeps is encrypted and signed.
| module | what it is |
|---|---|
occlude-api |
the portals and results application code holds |
occlude-core |
the charter, labels and ceilings, and MemoryStorage for tests |
occlude-jdbc |
encrypted, signed storage in Postgres 13+ |
occlude-spring-boot-starter |
Spring Boot wiring, with the JDBC store |
occlude-bom |
versions for all of the above |
The documentation site is the manual:
- Getting Started — a worked example, from axes to a first reveal
- Concepts — labels, ceilings, portals, deriving, the record, erasing
- Spring Boot, Storage and Operating a Store
- What Occlude does not do — the page to read first if you are deciding whether it fits
./mvnw clean verify
Java 25. The JDBC tests need Docker, for Testcontainers.