This repository contains the first version of the CRT server-side security core. The focus so far is the domain-level system that issues capability tokens, validates them, and enforces authorization decisions in one consistent path.
The current design is built around three main classes:
CapabilityTokenCodec— encrypts and decrypts capability tokensNodeAuthorizationService— enforces token lifetime, node scope, rights, and ownership rulesCapabilityTokenService— the main entry point for issuing, resolving, and authorizing tokens
The goal is to keep cryptography, authentication, and authorization separated so the system stays understandable, testable, and hard to bypass.
CRT uses encrypted capability tokens to represent what a client is allowed to do for a specific node. A token contains claims such as:
- token ID (jti)
- client ID
- node ID
- rights
- issued-at time
- expiration time
These claims are encrypted and authenticated before being sent to clients. On receipt, the server decrypts the token, validates its structure, checks that it is still usable, and then enforces the requested authorization rule.
Responsible for token protection and parsing. It uses AES-GCM for authenticated encryption with a 12-byte random nonce and 128-bit authentication tag. The version and key ID are included in AAD so protocol metadata is authenticated. The codec loads a raw AES key from configuration instead of deriving a key from a human passphrase.
The codec is a cryptographic implementation detail. It proves that a token is authentic and well-formed, but authorization policy lives elsewhere.
The authorization policy layer. It checks whether claims are currently usable, whether the token belongs to the requested node, whether the required right is present, and whether object ownership matches the token scope.
This keeps access-control logic centralized, which reduces the chance that different parts of the application enforce different rules.
The default application-facing security entry point. Other parts of the system call this when they need to issue a token, resolve a token into claims, parse a bearer token, or authorize a node action.
The design goal is that normal business code should not call CapabilityTokenCodec directly unless there is a specific low-level reason to do so.
The current system is a server-side capability token model. It is not yet a true end-to-end encryption system for messaging content.
At the moment, the system handles:
- authenticated token issuance
- scoped access to a node
- permission checking by named rights
- ownership enforcement for node-owned objects
- bounded token lifetime
It does not yet handle:
- true end-to-end encrypted messaging
- client-side message secrecy from the server
- multi-device secure session sync
- group messaging cryptographic protocols
- token revocation or replay protection beyond short TTLs
The codebase distinguishes different categories of security failure with explicit exception types:
CapabilitySecurityException— base security exceptionInvalidCapabilityTokenException— malformed, unverifiable, or invalid tokenCapabilityTokenExpiredException— token is authentic but expiredCapabilityAuthorizationException— token is valid but lacks required scope or rights
This distinction makes the system easier to test now and easier to map to HTTP status codes later.
The current test suite verifies the core security path. All of these cases are passing:
- round-trip token encode/decode
- tampered token rejection
- wrong-node rejection
- missing-right rejection
- expired-token rejection
- bearer token parsing
- invalid bearer header rejection
- TTL upper-bound rejection
This matters because secure design is only useful if the failure cases are exercised as well as the happy path.
The current version loads the AES key from environment configuration using CRT_CAP_TOKEN_KEY_V1.
This is acceptable for local development and early testing, but should not be the final production posture. Best practice is to store keys separately from encrypted data and use proper key or secrets management systems such as a vault, HSM, or cloud key-management service.
Example local setup:
export CRT_CAP_TOKEN_KEY_V1="$(openssl rand -base64 32)"
mvn clean testFor production:
- store secrets outside source control
- inject them securely at deploy time
- restrict access to the smallest set of systems and operators possible
- support key rotation through token keyId
This repository follows these principles:
- Centralize authorization so rights and scope checks happen in one place
- Fail closed so invalid or tampered tokens are rejected rather than partially accepted
- Use authenticated encryption for tokens rather than plain encryption
- Keep secrets out of source code and move toward dedicated secret management
- Keep the integration path simple by treating CapabilityTokenService as the default entry point
The project has a strong v1 domain security core. The main unfinished areas are operational and architectural rather than basic cryptography:
- formal token format documentation
- key rotation procedure
- secret-management hardening for deployment
- logging policy for security failures
- HTTP integration layer
- future protocol work for a true E2E encrypted messenger
The most useful next steps are:
- freeze the current token format in a short internal protocol note
- document key rotation and secret handling for each environment
- add more negative-path tests (malformed base64, truncated payloads, unknown key IDs, future-issued tokens beyond skew tolerance)
- wire CapabilityTokenService into the future API layer without bypassing the centralized authorization path