Persistence
Security state — audit events, login attempts, sessions, role
assignments, tokens — needs somewhere to live. jSentinel 00.70 models
persistence as a set of small store SPIs above any concrete storage
technology. security-core ships in-memory defaults; an optional
Eclipse-Store module is the first durable reference; a contract testkit
keeps every adapter honest.
security-core → 11 store SPIs + InMemory*Store defaults
security-persistence-eclipsestore → Eclipse-Store reference impl (opt-in)
security-persistence-testkit → contract test suites (test scope)security-core carries no Eclipse-Store dependency. The durable
backend is strictly opt-in.
The 11 store SPIs
Every record is hash-only or single-use where appropriate, every key is tenant-scoped:
| Store | Record / key | Notes |
|---|---|---|
AuditEventStore | AuditEnvelope | backing for StoreBackedSecurityAuditService |
SessionStore | SessionRecord keyed on SessionId | findAll() powers the admin session view |
LoginAttemptStore | LoginAttemptKey(username, clientAddress) | brute-force state |
RoleAssignmentStore | RoleAssignmentKey(tenant, subjectId) → Set<RoleName> | role persistence |
BootstrapStateStore | BootstrapState per tenant | first-run state |
RememberMeTokenStore | RememberMeTokenRecord | hash-only |
PasswordResetTokenStore | PasswordResetTokenRecord | hash-only, single-use |
EmailVerificationTokenStore | EmailVerificationTokenRecord | hash-only, single-use |
ApiKeyStore | ApiKeyRecord | hash-only, scoped |
RefreshTokenStore | RefreshTokenRecord | hash-only, rotating via markReplaced(...) |
RateLimitStore | event timestamps under RateLimitKey(tenant, scope) | sliding window |
Store-backed services
Wire a store and the matching service swaps its in-memory behaviour for durable storage — no change to the rest of your application:
StoreBackedSecurityAuditService → AuditEventStore
StoreBackedLoginAttemptPolicy → LoginAttemptStore
StoreBackedSubjectSessionRegistry → SessionStore (+ optional SecurityVersionStore)
StoreBackedRoleAuthorizationService → RoleAssignmentStore
StoreBackedRememberMeService → RememberMeTokenStore (+ PasswordHasher)
StoreBackedBootstrapStateService → BootstrapStateStoreAll are tenant-scoped and swallow store failures on the audit / notification path so a flaky store can never block the security flow.
Eclipse Store reference implementation
security-persistence-eclipsestore implements every store SPI on top of
org.eclipse.store:storage-embedded:4.1.0. To switch a whole
application from in-memory to durable storage, add the module as a
runtime dependency — registration is via META-INF/services/, no code
change in the consuming application:
<dependency>
<groupId>com.svenruppert</groupId>
<artifactId>security-persistence-eclipsestore</artifactId>
<version>00.70.00</version>
</dependency>The Eclipse-Store root class stays an adapter-internal implementation
detail — there is no public API type called SecurityRoot, and the
core never sees Eclipse Store.
Contract testkit
security-persistence-testkit ships a @Test default contract interface
per store. Any adapter (the in-memory defaults, the Eclipse-Store impl,
or your own Redis / JDBC backend) implements the contract to be vetted
against the library’s persistence semantics — the same 95+ tests run
against every implementation:
public interface LoginAttemptStoreContract {
LoginAttemptStore store();
@Test
default void savedStateCanBeLoaded() {
LoginAttemptKey key = new LoginAttemptKey(
TenantId.DEFAULT, "alice", "127.0.0.1");
LoginAttemptState state = ...;
store().save(key, state);
assertEquals(Optional.of(state), store().find(key));
}
}Bring your own backend
The store SPIs are deliberately small and domain-shaped so that
Redis / JDBC / event-stream / IAM-backed implementations are drop-in
replacements. Cluster mode is intentionally not a built-in — it is
exactly what these SPIs enable you to add. Implement the SPI, pass the
contract suite, register via META-INF/services/, done.
@ExperimentalSecurityApi in
00.70 while the record shapes settle.