https://github.com/standardapplied/monolith
https://github.com/standardapplied/monolith
Last synced: 25 days ago
JSON representation
- Host: GitHub
- URL: https://github.com/standardapplied/monolith
- Owner: standardapplied
- License: mit
- Created: 2026-05-31T20:40:41.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2026-06-08T00:24:01.000Z (about 2 months ago)
- Last Synced: 2026-06-24T05:36:56.888Z (about 1 month ago)
- Language: Java
- Size: 747 KB
- Stars: 4
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- Contributing: CONTRIBUTING.md
- License: LICENSE
- Security: SECURITY.md
- Roadmap: ROADMAP.md
Awesome Lists containing this project
README
# Monolith
*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.**
```java
// 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) {}
```
```java
// 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());
```
## What it does
- **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**: `@Encrypted` envelope encryption, `@Tenant` and
`@AccessControlled` forced row-level security (a unified RBAC/ACL/consent grant model), `@Audited`
trails. 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. See
[`SECURITY.md`](SECURITY.md) and the [roadmap](ROADMAP.md) 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 `main` and routes.
## Documentation
Full guides, concepts, and design notes: ****. The docs site also
publishes an [`llms.txt`](https://singlr-ai.github.io/monolith/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`](examples/collab).
## Modules
| 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.
## Status & requirements
**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:
```sh
# 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 verify
```
The `-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.
## License
MIT, Standard Applied Intelligence Labs. See [LICENSE](LICENSE).