Memory Store
This page explains how memory-backed stores work in rix/auth.
The examples use the public Rix facade:
#include <rix.hpp>and Auth through:
rix.authMemory stores keep users and sessions inside the current process.
They are useful for examples, tests, local development, and small temporary tools.
They are not durable.
When the process exits, the stored users and sessions are lost.
Basic idea
The simplest memory-backed Auth service is:
auto auth = rix.auth.memory();This creates:
MemoryUserStore
MemorySessionStore
Auth serviceand returns a managed auth object that owns the stores safely.
You can immediately register users, login, authenticate sessions, refresh sessions, and logout.
Complete example
Create a file:
mkdir -p ~/rix-auth-memory-store
cd ~/rix-auth-memory-store
touch memory.cppAdd:
#include <rix.hpp>
int main()
{
rix.debug.print("== rix/auth memory store ==");
auto auth = rix.auth.memory();
auto registered = auth.register_user({"ada@example.com", "correct-password"});
if (registered.failed())
{
const auto &error = registered.error();
rix.debug.eprint(
"auth error:",
rix.auth.error.to_string(error),
error.message()
);
return 1;
}
auto login = auth.login({"ada@example.com", "correct-password"});
if (login.failed())
{
const auto &error = login.error();
rix.debug.eprint(
"auth error:",
rix.auth.error.to_string(error),
error.message()
);
return 1;
}
rix.debug.print("registered:", registered.value().email());
rix.debug.print("user id:", registered.value().id());
rix.debug.print("session id:", login.value().session.id());
auto session = auth.authenticate_session(login.value().session.id());
if (session.failed())
{
const auto &error = session.error();
rix.debug.eprint(
"auth error:",
rix.auth.error.to_string(error),
error.message()
);
return 1;
}
rix.debug.print("OK:", "session authenticated");
rix.debug.print("session user id:", session.value().user_id());
return 0;
}Run it:
vix run memory.cppIf Rix is not available yet for single-file usage:
vix install -g rix/rix
vix run memory.cppExpected output
The output should look like this:
== rix/auth memory store ==
registered: ada@example.com
user id: user_...
session id: session_...
OK: session authenticated
session user id: user_...The exact user id and session id will be different.
When to use memory auth
Use memory auth for:
examples
tests
local experiments
temporary tools
learningMemory auth is the fastest way to try rix/auth.
It does not need a database.
It does not need schema setup.
It is easy to reset because all data lives in memory.
When not to use memory auth
Do not use memory auth when:
users must survive restarts
sessions must survive restarts
the app runs in production
the app runs across multiple processes
the app needs durable account stateFor real applications, prefer database auth:
auto config = rix.auth.config.production();
auto auth = rix.auth.database(db, config);Memory auth with configuration
Memory auth can receive a custom configuration:
auto config = rix.auth.config.development();
config.set_min_password_length(10);
config.set_session_ttl_seconds(60 * 60);
config.set_token_ttl_seconds(60 * 15);
config.set_issuer("memory-example");
auto auth = rix.auth.memory(config);The configuration applies to:
password validation
password hashing
session lifetime
token lifetime
token issuer
email verification behavior
inactive user rejectionMemory users
A memory user store keeps users in memory.
The facade can create one directly:
auto users = rix.auth.stores.memory_users();This returns an owned user store.
Most applications do not need to create the store directly.
Prefer:
auto auth = rix.auth.memory();Use direct store creation only when you need custom store wiring.
Memory sessions
A memory session store keeps sessions in memory.
The facade can create one directly:
auto sessions = rix.auth.stores.memory_sessions();This returns an owned session store.
Most applications do not need to create the store directly.
Prefer:
auto auth = rix.auth.memory();Managed memory auth
rix.auth.memory() returns a managed auth service.
That means the returned object owns its user store and session store.
auto auth = rix.auth.memory();This is safe because the stores stay alive as long as the auth object stays alive.
You can use the returned object like the normal Auth API:
auth.register_user(...)
auth.login(...)
auth.authenticate_session(...)
auth.refresh_session(...)
auth.logout(...)
auth.logout_user(...)
auth.issue_token(...)Custom managed memory stores
If you want to create stores yourself but still let the returned auth service own them, use managed.
auto users = rix.auth.stores.memory_users();
auto sessions = rix.auth.stores.memory_sessions();
auto managed = rix.auth.managed(
std::move(users),
std::move(sessions)
);Check the result:
if (managed.failed())
{
const auto &error = managed.error();
rix.debug.eprint(
"auth error:",
rix.auth.error.to_string(error),
error.message()
);
return 1;
}
auto auth = managed.move_value();Use this when you need custom store construction but still want safe ownership.
Managed memory auth with config
You can pass configuration to managed:
auto config = rix.auth.config.development();
config.set_issuer("managed-memory-example");
auto users = rix.auth.stores.memory_users();
auto sessions = rix.auth.stores.memory_sessions();
auto managed = rix.auth.managed(
std::move(users),
std::move(sessions),
config
);The configuration is validated before the managed auth object is returned.
If the configuration is invalid, managed returns an error.
Caller-owned memory stores
Advanced code can create an auth service from caller-owned stores:
auto users = rix.auth.stores.memory_users();
auto sessions = rix.auth.stores.memory_sessions();
auto auth = rix.auth.create(*users, *sessions);In this case, the caller owns the stores.
The stores must stay alive for as long as auth is used.
This is advanced usage.
For normal application code, prefer:
auto auth = rix.auth.memory();or:
auto managed = rix.auth.managed(
rix.auth.stores.memory_users(),
rix.auth.stores.memory_sessions()
);Store lifetime rule
This is safe:
auto auth = rix.auth.memory();because the auth object owns its stores.
This is also safe:
auto managed = rix.auth.managed(
rix.auth.stores.memory_users(),
rix.auth.stores.memory_sessions()
);because the managed auth object owns the stores.
This requires care:
auto users = rix.auth.stores.memory_users();
auto sessions = rix.auth.stores.memory_sessions();
auto auth = rix.auth.create(*users, *sessions);because the stores are owned outside the auth object.
Do not destroy the stores while the auth service is still using them.
Inspect memory stores
A managed auth object exposes the owned stores:
auto auth = rix.auth.memory();
auto &users = auth.users();
auto &sessions = auth.sessions();This is useful for tests and advanced diagnostics.
Example:
auto auth = rix.auth.memory();
auto registered = auth.register_user({
"ada@example.com",
"correct-password"
});
auto all_users = auth.users().all();
if (all_users.ok())
{
rix.debug.print("users:", all_users.value().size());
}Find users in memory
The user store exposes lookup methods:
auth.users().find_by_id(user_id)
auth.users().find_by_email(email)
auth.users().exists_by_id(user_id)
auth.users().exists_by_email(email)
auth.users().all()Example:
auto found = auth.users().find_by_email("ada@example.com");
if (found.ok() && found.value().has_value())
{
rix.debug.print("found:", found.value()->email());
}These methods are mainly useful for tests, admin tools, and custom integrations.
Normal application code should prefer the high-level Auth API.
Find sessions in memory
The session store exposes lookup methods:
auth.sessions().find_by_id(session_id)
auth.sessions().find_by_user_id(user_id)
auth.sessions().exists_by_id(session_id)
auth.sessions().all()Example:
auto found = auth.sessions().find_by_user_id(user_id);
if (found.ok())
{
rix.debug.print("sessions:", found.value().size());
}These methods are useful for tests, cleanup tools, and admin-style features.
Normal application code should prefer:
auth.authenticate_session(session_id)Clear memory stores
Memory stores expose clear().
For users:
auto &users = auth.users();
users.clear();For sessions:
auto &sessions = auth.sessions();
sessions.clear();Clearing a memory store removes its in-memory data.
This is useful in tests.
Use it carefully in application code.
Memory store size
Memory stores expose size helpers.
For users:
auto count = auth.users().size();
bool empty = auth.users().empty();For sessions:
auto count = auth.sessions().size();
bool empty = auth.sessions().empty();These are useful for tests and diagnostics.
Duplicate users
Memory user store rejects duplicate users.
This can happen when:
the same user id already exists
the same email already existsExample:
auto first = auth.register_user({
"ada@example.com",
"correct-password"
});
auto second = auth.register_user({
"ada@example.com",
"correct-password"
});The second registration should fail with a user-already-exists error.
Duplicate sessions
Memory session store rejects duplicate session ids.
Normally, application code does not create session ids directly.
Auth generates session ids during login.
Duplicate session errors are mostly relevant for tests and custom store implementations.
Session revocation in memory
Logout revokes a session in the memory session store:
auto status = auth.logout(session_id);After logout:
auto session = auth.authenticate_session(session_id);should fail because the session is revoked.
Logout all sessions for a user:
auto status = auth.logout_user(user_id);This revokes every session attached to the user.
Thread safety
The memory stores are designed to be thread-safe for individual store operations.
This means individual calls such as create, update, find, revoke, and remove are protected internally.
For larger multi-step workflows, the application should still treat Auth operations as the public coordination layer.
Use database-backed stores for real multi-process or durable deployments.
Memory store limitations
Memory stores do not provide:
durability
cross-process sharing
database migrations
long-term persistence
production storage guaranteesThey are intentionally simple.
Use them to learn, test, and prototype.
Use database stores for real applications.
Security notes
Memory auth still uses password hashing and the configured policy.
But the storage is not durable.
Do not use memory auth as the permanent storage for real user accounts.
Do not log:
plain-text passwords
password hashes
session ids
raw token valuesUse production configuration and database auth for real deployments.
What you should remember
Create memory auth:
auto auth = rix.auth.memory();Create memory auth with config:
auto config = rix.auth.config.development();
auto auth = rix.auth.memory(config);Create memory stores manually:
auto users = rix.auth.stores.memory_users();
auto sessions = rix.auth.stores.memory_sessions();Prefer managed ownership:
auto auth = rix.auth.memory();Use caller-owned stores only when you understand the lifetime rule:
auto auth = rix.auth.create(*users, *sessions);Memory auth is for examples, tests, and local experiments.
Database auth is for durable applications.
Next step
Learn database-backed stores.
Next: Database Store