Architecture
BeanGuard splits responsibilities across four modules: the server issues and stores licences, the admin panel and shop are its REST API clients, and beanguard-client verifies the licence inside your application.
Four modules
| Module | What it does |
|---|---|
beanguard-server | REST API + PostgreSQL. The only place where the RSA private key used to sign licences exists. |
beanguard-admin | Admin panel (SPA) — licences, users, orders, shop products, branding, cryptographic keys. Talks to the server via REST API. |
beanguard-shop | Public storefront for end customers — purchasing and activating licences. Uses only the server's open/ endpoints, no login required. |
beanguard-client | A library you add to your application. Fetches the licence from the server, decrypts it, and provides annotations to enforce its content. |
The admin panel and shop are thin Spring Boot wrappers serving a pre-built React frontend — all business logic (licences, products, orders) lives in beanguard-server.
How a licence is issued
- A licence is created manually in the admin panel, or automatically — by accepting a shop order or a self-service demo licence request — with limits and features encoded as key/value pairs in the
claimsfield. - The server signs the licence content with its RSA private key and encrypts the result with an AES-256-GCM key.
- The client — your application, via
beanguard-client— fetches the encrypted licence from the server using the licence key and secret.
Double encryption
BeanGuard licences are doubly-encrypted JWTs:
- Inner — a JWS signed with an RSA key (
RS256). Guarantees integrity: without the private key, no one can forge or modify the licence content. - Outer — a JWE encrypted with an AES-256-GCM key. Hides the licence content from anyone without the secret.
The keys (LICENCE_PUBLIC_KEY, LICENCE_PRIVATE_KEY, LICENCE_SECRET_KEY) and LICENCE_ISSUER are stored in the parameter table on the server. The RSA private key never leaves it — even if the public key and AES secret leak, at most an already-issued licence can be replayed, never a new one created.
Custom licence fields (limits, feature flags) go into the JSONB claims
column — the @RequiresLicenceLimit and @RequiresLicenceFeature
annotations described in Licensing annotations
operate on them.
How the client verifies a licence
beanguard-client fetches the licence on application startup and refreshes it every hour. It decrypts it using the RSA public key and AES secret supplied by your BeanGuardConfiguration implementation — see Client integration.
