Live, typed queries on real Postgres.
Monolith is a set of small Java libraries for building applications on PostgreSQL where the schema, the typed data access, the client reader, and the live (reactive) queries all come from a single declaration. You write a record, sometimes carrying its SQL, and an annotation processor generates the rest at compile time. It runs on ordinary relational tables: you keep SQL, JOINs, transactions, and foreign keys.
It started from one question: can a Java team get the "subscribe, and the UI updates when the data changes" experience of Firebase or InstantDB without giving up a real relational database? This is v0.x: experimental, and the API will change.
// One declaration → Postgres DDL, a binary reader/builder, and a TypeScript reader for the same layout.
// @Encrypted fields use envelope encryption (a per-value AES-256-GCM data key); Postgres stores only ciphertext.
@PgType
public record Patient(UUID id, String name, @Encrypted String ssn) {}
// A @PgQuery record carries its SQL. The processor also generates a reader, a typed run(), and the
// reactive invalidation rule for this query.
@PgQuery("""
SELECT o.id, c.name AS customer, o.status,
coalesce(sum(li.qty * li.unit_price), 0) AS total
FROM orders o
JOIN customers c ON c.id = o.customer_id
LEFT JOIN line_items li ON li.order_id = o.id
WHERE c.region = $1
GROUP BY o.id, c.name, o.status""")
public record OrderSummary(UUID id, String customer, String status, BigDecimal total) {}// Reactive: wake when a row that affects the result changes, even a line item two joins away.
var hub = new ReactiveHub(pool, List.of(new OrderSummaryInvalidation())); // rule is generated
var feed = new Invalidator("host=localhost dbname=app", hub, "app_feed"); // tails the WAL
hub.subscribe("OrderSummary", "EU", () -> pushFreshResultToClients());- One declaration, generated outputs: DDL, a binary reader/builder over the Postgres wire layout, a TypeScript reader, and a reactive invalidation rule, at compile time, no runtime reflection.
- Live queries over joined tables: precise re-execution, not incremental view maintenance; the generated rule maps a change back to the affected subscribers, walking joins where needed.
- libpq, not JDBC: the binary protocol via the Java FFM API; TLS and SCRAM are libpq's.
- Transactions with automatic retry, schema migrations, binary parameters (arrays, enums, prepared statements), and a durable transactional queue (outbox + jobs in one primitive).
- Compliance primitives in the database:
@Encryptedenvelope encryption,@Tenantand@AccessControlledforced row-level security (a unified RBAC/ACL/consent grant model),@Auditedtrails. These are building blocks, not a turnkey HIPAA/SOC 2 posture — key custody defaults to an in-process key, and read-access audit and PHI-safe observability are not yet shipped. SeeSECURITY.mdand the roadmap for exactly what is and isn't covered. - Scale-out routing: read replicas and tenant sharding over a common
ConnectionSource. - A library, not a platform: pure-JDK core, no web framework; bring your own
mainand routes.
Full guides, concepts, and design notes: https://singlr-ai.github.io/monolith. The docs site also
publishes an llms.txt and llms-full.txt so coding
agents can ingest the documentation as context. A complete, runnable example app (a live, multi-client
task board) is in examples/collab.
| Module | What it is |
|---|---|
monolith-api |
Declaration annotations: @PgType, @PgQuery, @PgProjection, @PgNull, @Encrypted, @Tenant, @Audited, @AccessControlled, Json. |
monolith-codegen |
The javac annotation processor. Generates DDL, readers/builders, TypeScript readers, and invalidation rules. |
monolith-runtime |
libpq via Panama FFM, a connection pool, the binary tuple bridge and codecs, transactions, field encryption, and the WAL change-feed primitives. Pure JDK. |
monolith-reactive |
Live queries: ReactiveHub plus the WAL-tailing Invalidator. No web dependency. |
monolith-queue |
A durable, transactional, at-least-once message queue (outbox + jobs) over the same Postgres. Pure JDK. |
monolith-helidon |
Optional adapter: a Helidon SE WsListener that serves live queries over WebSockets. |
Every module except monolith-helidon has no web dependency, and nothing in the core depends on it.
v0.x: experimental; APIs will change. Requires JDK 25+ (Panama FFM, virtual threads) and
PostgreSQL 14+ (wal_level = logical for the reactive layer). macOS and Linux (libpq is loaded
via FFM); Windows via WSL2. The Maven build enforces the JDK floor (maven-enforcer-plugin), so an older
JDK fails fast with a clear message instead of cryptic compiler errors.
Reproducing CI locally. Many integration tests self-skip when no database is reachable, so a bare
mvn verify can pass while exercising none of the FFM/WAL/queue/compliance paths. Run the CI-equivalent
build against a real Postgres with logical decoding:
# Postgres 18 with wal_level=logical, created exactly as CI does (see .github/workflows/ci.yml):
# the monolith_test database and trust auth (password-free, as the conninfo below expects) are
# set up by the container's own env vars, and wal_level=logical enables logical decoding.
# docker run -d --name monolith-pg \
# -e POSTGRES_DB=monolith_test -e POSTGRES_HOST_AUTH_METHOD=trust \
# -p 5432:5432 postgres:18 -c wal_level=logical
MONOLITH_TEST_CONNINFO="host=localhost dbname=monolith_test user=postgres" \
mvn -B -Pci clean verifyThe -Pci profile sets monolith.requireDb=true, which makes the build fail (rather than silently
skip) if that database is not actually reachable.
Goals. A live-subscription developer experience on a real relational database, for Java teams; type safety carried from the database row to the client; libraries you embed, not a platform you adopt. Non-goals. Not an incremental-view-maintenance engine, not an ORM (you write SQL), not a managed service.
MIT, Standard Applied Intelligence Labs. See LICENSE.