Java SDK for the fast, file-backed, scalable JSON token engine
Escape the JWT trap: predictable login, safe logout
Fast B-tree–backed token store for stateful user sessions
Provides authentication and authorization across multiple processes
Optimized for vertical scaling on a single server
implementation("com.crudjt:crudjt:1.0.0")<dependency>
<groupId>com.crudjt</groupId>
<artifactId>crudjt</artifactId>
<version>1.0.0</version>
</dependency>- One process starts the master
- All other processes connect to it
Start the CRUDJT master when your application boots
Only one process can do this for a single token storage
The master process manages sessions and coordination
All functions can also be used directly from it
For containerized deployments, see: Start CRUDJT master in Docker
export CRUDJT_SECRET_KEY=$(openssl rand -base64 48)import com.crudjt.CRUDJT;
CRUDJT.Config.startMaster(
Map.of(
"secret_key", System.getenv("CRUDJT_SECRET_KEY"),
"store_jt_path", "path/to/local/storage", // optional
"grpc_host", "127.0.0.1", // default
"grpc_port", 50051 // default
)
);Important: Use the same secret_key across all sessions. If the key changes, previously stored tokens cannot be decrypted and will return null or false
Create a docker-compose.yml file:
services:
crudjt-server:
image: crudjt/crudjt-server:latest
restart: unless-stopped
ports:
- "${CRUDJT_CLIENT_PORT:-50051}:50051"
volumes:
- "${STORE_JT:-./store_jt}:/app/store_jt"
- "${CRUDJT_SECRETS:-./crudjt_secrets}:/app/secrets"
environment:
CRUDJT_DOCKER_HOST: 0.0.0.0
CRUDJT_DOCKER_PORT: 50051Start the server:
docker-compose up -dEnsure the secrets directory contains your secret key file at ./crudjt_secrets/secret_key.txt
For configuration details and image versions, see the CRUDJT Server on Docker Hub
Use this in all other processes
Typical examples:
- multiple local processes
- background jobs
- forked processes
import com.crudjt.CRUDJT;
CRUDJT.Config.connectToMaster(
Map.of(
"grpc_host", "127.0.0.1", // default
"grpc_port", 50051 // default
)
);App boot
├─ Process A → start_master
├─ Process B → connect_to_master
└─ Process C → connect_to_master
Map<String, Object> data = Map.of("user_id", 42, "role", 11); // required
long ttl = 3600 * 24 * 30; // optional: token lifetime (seconds)
// Optional: read limit
// Each read decrements the counter
// When it reaches zero — the token is deleted
long silenceRead = 10;
String token = CRUDJT.create(data, ttl, silenceRead);
// token == "HBmKFXoXgJ46mCqer1WXyQ"// To disable token expiration or read limits, pass `null`
CRUDJT.create(Map.of("user_id", 42, "role", 11), null, null);Map<String, Object> result = CRUDJT.read("HBmKFXoXgJ46mCqer1WXyQ");
// result == {metadata={ttl=101001, silence_read=9}, data={user_id=42, role=11}}// When expired or not found token
String token = CRUDJT.read("HBmKFXoXgJ46mCqer1WXyQ");
// token == nullMap<String, Object> data = Map.of("user_id", 42, "role", 8);
// `null` disables limits
long ttl = 600;
long silenceRead = 100;
boolean result = CRUDJT.update("HBmKFXoXgJ46mCqer1WXyQ", data, ttl, silenceRead);
// result == true// When expired or not found token
boolean result = CRUDJT.update("HBmKFXoXgJ46mCqer1WXyQ", Map.of("user_id", 42, "role", 8));
// result == falseboolean result = CRUDJT.delete("HBmKFXoXgJ46mCqer1WXyQ");
// result == true// When expired or not found token
boolean result = CRUDJT.delete("HBmKFXoXgJ46mCqer1WXyQ");
// result == false40 000 requests up to 256 bytes — median over 10 runs
macOS 15.7.4, ARM64 (Apple M1)
Java 17.0.18
In-process benchmark; Redis accessed via localhost TCP
| Function | CRUDJT (Java) | JWT (Java) | redis-session-store (Ruby, Rails 8.0.2.1) |
|---|---|---|---|
| C | 0.263 second | 0.241 second ⭐ | 2.909 seconds |
| R | 0.011 second |
0.972 second | 4.436 seconds |
| U | 0.383 second |
X | 2.124 seconds |
| D | 0.177 second |
X | 3.984 seconds |
40 000 tokens of 256 bytes each — median over 10 creates
darwin23, APFS
48 MB
Stored tokens are placed in the file system according to the following order
- Explicitly set via
CRUDJT.Config.startMaster(Map.of("store_jt_path", "custom/path/to/file_system_db")); - Default system location
- Linux:
/var/lib/store_jt - macOS:
/usr/local/var/store_jt
- Linux:
- Project root directory (fallback)
- CRUDJT automatically removing expired tokens after start and every 24 hours without blocking the main thread
- Storage automatically fsyncs every 500ms, meanwhile tokens are available from cache
For multi-process scenarios, CRUDJT uses gRPC over an insecure local port for same-host communication only. It is not intended for inter-machine or internet-facing usage
The library has the following limits and requirements
- Java version: tested with 17.0.16
- Supported platforms: Linux, macOS (x86_64 / arm64)
- Maximum json size per token: 256 bytes
secret_keyformat: must be Base64secret_keysize: must be 32, 48, or 64 bytes
- Custom integrations / new features / collaboration: [email protected]
- Library support & bug reports: open an issue
CRUDJT is released under the MIT License
💘 Shoot your g . ? Love me out via Patreon Sponsors!