crudjt / crudjt-php
Fast B-tree–backed token store for stateful sessions
Fund package maintenance!
Requires
- google/protobuf: ^4.33
- grpc/grpc: ^1.74
This package is auto-updated.
Last update: 2026-05-22 14:19:39 UTC
README
PHP 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
Installation
Composer:
composer require crudjt/crudjt-php
Start CRUDJT master (once)
startMaster() runs the master in a single PHP process
For multi-process or distributed setups, start the master in
another supported runtime
and connect from PHP using connectToMaster()
or jump to Start CRUDJT master in Docker
Start CRUDJT master
Start the CRUDJT master when your application boots
Only one process can do this for a single token storage
The master is responsible for session state
All functions can also be used directly from it
Generate a new secret key (terminal)
export CRUDJT_SECRET_KEY=$(openssl rand -base64 48)
Start master (php)
<?php require __DIR__ . '/vendor/autoload.php'; use CRUDJT\CRUDJT; \CRUDJT\Config::startMaster([ 'secret_key' => getenv('CRUDJT_SECRET_KEY'), 'store_jt_path' => 'path/to/local/storage' ]);
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
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: 50051
Start the server:
docker-compose up -d
Ensure 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
<?php require __DIR__ . '/vendor/autoload.php'; 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
$data = ['user_id' => 42, 'role' => 11]; // required $ttl = 3600 * 24 * 30; // optional: token lifetime (seconds) // Optional: read limit // Each read decrements the counter // When it reaches zero — the token is deleted $silenceRead = 10; $token = CRUDJT::create($data, $ttl, $silenceRead); // $token == string(22) "HBmKFXoXgJ46mCqer1WXyQ"
// To disable token expiration or read limits, pass `null` $token = CRUDJT::create( ['user_id' => 42, 'role' => 11], null, // disable TTL null // disable read limit );
R
$result = CRUDJT.read('HBmKFXoXgJ46mCqer1WXyQ'); // result == array(2) { ["metadata"]=> array(2) { ["ttl"]=> int(101001) ["silence_read"]=> int(9) } ["data"]=> array(2) { ["user_id"]=> int(42) ["role"]=> int(11) } }
// When expired or not found token $result = CRUDJT.read('HBmKFXoXgJ46mCqer1WXyQ'); // result == NULL
U
$data = ['user_id' => 42, 'role' => 8]; // `null` disables limits $ttl = 600; $silenceRead = 100; $result = CRUDJT::update("HBmKFXoXgJ46mCqer1WXyQ", ['user_id' => 42, 'role' => 8]); // $result == bool(true) # array(1) { ["data"]=> array(2) { ["user_id"]=> int(42) ["role"]=> int(8) } }
// When expired or not found token $result = CRUDJT::update("HBmKFXoXgJ46mCqer1WXyQ", $data, $ttl, $silenceRead); // $result == bool(false)
D
$result = CRUDJT::delete("HBmKFXoXgJ46mCqer1WXyQ"); // $result == bool(true)
// when expired or not found token $result = CRUDJT::delete("HBmKFXoXgJ46mCqer1WXyQ"); // $result == NULL
Performance
40 000 requests up to 256 bytes — median over 10 runs
macOS 15.7.4, ARM64 (Apple M1)
PHP 8.4.19
In-process benchmark; Redis accessed via localhost TCP
| Function | CRUDJT (PHP) | JWT (PHP) | redis-session-store (Ruby, Rails 8.0.2.1) |
|---|---|---|---|
| C | 0.234 second |
0.292 second | 2.909 seconds |
| R | 0.018 second |
0.341 second | 4.436 seconds |
| U | 0.364 second |
X | 2.124 seconds |
| D | 0.173 second |
X | 3.984 seconds |
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
- PHP version: tested with 8.2.30
- Supported platforms: Linux, macOS (x86_64 / arm64). Windows (experimental, 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: support@crudjt.com
- Library support & bug reports: open an issue
Lincense
CRUDJT is released under the MIT License
💘 Shoot your g . ? Love me out via Patreon Sponsors!