crudjt
v1.0.1
Published
Fast B-tree–backed token store for stateful sessions
Maintainers
Readme
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
Installation
NPM or (Yarn)
npm install crudjtHow to use
- One process starts the master
- All other processes connect to it
Start CRUDJT master (once)
Start the CRUDJT master when your application boots
Only one process should do this
The master is responsible for session state and coordination
All functions can also be used directly from it
For containerized deployments, see: Start CRUDJT master in Docker
Generate a new secret key (terminal)
export CRUDJT_SECRET_KEY=$(openssl rand -base64 48)Start master (JavaScript)
const CRUDJT = require('crudjt');
await CRUDJT.Config.startMaster({
secret_key: process.env.CRUDJT_SECRET_KEY,
store_jt_path: 'path/to/local/storage', // optional
grpc_host: '127.0.0.1', // default
grpc_port: 50051 // default
});
// Use await CRUDJT.Config.shutdownServer() to shut down the gRPC server and let
// the process exitImportant: Use the same secret_key across all sessions. If the key changes, previously stored tokens cannot be decrypted and will return null or false
Start CRUDJT master in Docker
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
Connect to an existing CRUDJT master
Use this in all other processes
Typical examples:
- multiple local processes
- background jobs
- forked processes
const CRUDJT = require('crudjt');
CRUDJT.Config.connectToMaster({
grpc_host: '127.0.0.1', // default
grpc_port: 50051 // default
});Process layout
App boot
├─ Process A → start_master
├─ Process B → connect_to_master
└─ Process C → connect_to_master
C
const data = { user_id: 42, role: 11 }; // required
const ttl = 3600 * 24 * 30; // optional: token lifetime (seconds)
// Optional: read limit
// Each read decrements the counter
// When it reaches zero — the token is deleted
const silenceRead = 10;
const token = await CRUDJT.create(data, ttl, silenceRead);
// token === 'HBmKFXoXgJ46mCqer1WXyQ'// To disable token expiration or read limits, pass `null`
const token = await CRUDJT.create(
{ user_id: 42, role: 11 },
null, // disable TTL
null // disable read limit
);R
const result = await CRUDJT.read('HBmKFXoXgJ46mCqer1WXyQ');
// result === { metadata: { ttl: 101001, silence_read: 9 }, data: { user_id: 42, role: 11 } }// When expired or not found token
const result = await CRUDJT.read('HBmKFXoXgJ46mCqer1WXyQ');
// result === nullU
const data = { user_id: 42, role: 8 };
// `null` disables limits
const ttl = 600;
const silenceRead = 100;
const result = await CRUDJT.update('HBmKFXoXgJ46mCqer1WXyQ', data, ttl, silenceRead);
// result === true// When expired or not found token
const result = await CRUDJT.update('HBmKFXoXgJ46mCqer1WXyQ', { user_id: 42, role: 8 });
// result === falseD
const result = await CRUDJT.delete('HBmKFXoXgJ46mCqer1WXyQ');
// result === true// When expired or not found token
const result = await CRUDJT.delete('HBmKFXoXgJ46mCqer1WXyQ');
// result === falsePerformance
40 000 requests up to 256 bytes — median over 10 runs
macOS 15.7.4, ARM64 (Apple M1)
JavaScript 20.20.0
In-process benchmark; Redis accessed via localhost TCP
| Function | CRUDJT (JavaScript) | JWT (JavaScript) | redis-session-store (Ruby, Rails 8.0.2.1) |
|----------|-------|------|------|
| C | 0.372 second | 16.537 seconds | 2.909 seconds |
| R | 0.010 second | 16.678 seconds | 4.436 seconds |
| U | 0.684 second | X | 2.124 seconds |
| D | 0.200 second | X | 3.984 seconds |
Benchmarks shown here are from a previous version of CRUDJT. For current performance metrics, see the GitHub repository
Storage (File-backed)
Disk footprint
40 000 tokens of 256 bytes each — median over 10 creates
darwin23, APFS
48 MB
Path Lookup Order
Stored tokens are placed in the file system according to the following order
- Explicitly set via
CRUDJT.Config.startMaster({store_jt_path: 'custom/path/to/file_system_db'}); - Default system location
- Linux:
/var/lib/store_jt - macOS:
/usr/local/var/store_jt - Windows:
C:\Program Files\store_jt
- Linux:
- Project root directory (fallback)
Storage Characteristics
- 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
Multi-process Coordination
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
Limits
The library has the following limits and requirements
- Node version: >= 18.0.0
- Supported platforms: Linux, macOS, Windows (x86_64 / arm64)
- Maximum json size per token: 256 bytes
secret_keyformat: must be Base64secret_keysize: must be 32, 48, or 64 bytes
Contact & Support
- Custom integrations / new features / collaboration: [email protected]
- Library support & bug reports: open an issue
License
CRUDJT is released under the MIT License
