Elixir SDK for the fast, file-backed, scalable JSON token engine
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
def deps do
[
{:crudjt, "~> 1.0.0"}
]
endHow 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 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
Generate a new secret key (terminal)
export CRUDJT_SECRET_KEY=$(openssl rand -base64 48)Start master (elixir)
CRUDJT.Config.start_master(
secret_key: System.fetch_env!("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 nil 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
CRUDJT.Config.connect_to_master(
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
silence_read = 10
token = CRUDJT.create(data, ttl, silence_read)
# token == "HBmKFXoXgJ46mCqer1WXyQ"# To disable token expiration or read limits, pass `nil`
token = CRUDJT.create(
%{"user_id" => 42, "role" => 11},
nil, # disable TTL
nil # disable read limit
)R
result = CRUDJT.read("HBmKFXoXgJ46mCqer1WXyQ")
# result == %{"metadata" => %{"ttl" => 101001, "silence_read" => 9}, "data" => %{"user_id" => 42, "role" => 11}}# When expired or not found token
result = CRUDJT.read("HBmKFXoXgJ46mCqer1WXyQ")
# result == nilU
data = %{"user_id" => 42, "role" => 8}
# `nil` disables limits
ttl = 600
silence_reaad = 100
result = CRUDJT.update("HBmKFXoXgJ46mCqer1WXyQ", data, ttl, silence_read)
# result == true# When expired or not found token
result = CRUDJT.update("HBmKFXoXgJ46mCqer1WXyQ", %{"user_id" => 42, "role" => 8})
# result == falseD
result = CRUDJT.delete("HBmKFXoXgJ46mCqer1WXyQ")
# result == true# When expired or not found token
result = CRUDJT.delete("HBmKFXoXgJ46mCqer1WXyQ")
# result == falsePerformance
40 000 requests up to 256 bytes — median over 10 runs
macOS 15.7.4, ARM64 (Apple M1)
Elixir 1.19.5
In-process benchmark; Redis accessed via localhost TCP
| Function | CRUDJT (Elixir) | JWT (Elixir) | redis-session-store (Ruby, Rails 8.0.2.1) |
|---|---|---|---|
| C | 0.279 second | 0.353 second | 2.909 seconds |
| R | 0.037 second | 0.972 second | 4.436 seconds |
| U | 0.382 second | X | 2.124 seconds |
| D | 0.192 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.start_master(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
- Elixir version: tested with 1.17.3 | Erlang/OTP >= 27
- 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: 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!