Skip to content
Flag of Europe
Made in the European Union · Independently built · Released under EUPL 1.2
Persistence

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:

StoreRecord / keyNotes
AuditEventStoreAuditEnvelopebacking for StoreBackedSecurityAuditService
SessionStoreSessionRecord keyed on SessionIdfindAll() powers the admin session view
LoginAttemptStoreLoginAttemptKey(username, clientAddress)brute-force state
RoleAssignmentStoreRoleAssignmentKey(tenant, subjectId)Set<RoleName>role persistence
BootstrapStateStoreBootstrapState per tenantfirst-run state
RememberMeTokenStoreRememberMeTokenRecordhash-only
PasswordResetTokenStorePasswordResetTokenRecordhash-only, single-use
EmailVerificationTokenStoreEmailVerificationTokenRecordhash-only, single-use
ApiKeyStoreApiKeyRecordhash-only, scoped
RefreshTokenStoreRefreshTokenRecordhash-only, rotating via markReplaced(...)
RateLimitStoreevent 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     → BootstrapStateStore

All 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.

The persistence-store SPIs are marked @ExperimentalSecurityApi in 00.70 while the record shapes settle.