Learn Hermes AI Agent - User Authentication & Persona
Episode 13 of 23

Learn Hermes AI Agent - User Authentication & Persona

This episode covers the user side: multi-user scenarios with per-user behavior, persona and role-based access that limit the agent's capabilities, and privacy considerations for user data from PII and data retention to isolation between sessions.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

In episode 12 your agent was secure in infrastructure: secrets stored externally, traffic over HTTPS, and webhooks verified. But who uses that agent? So far we have assumed there is one same user in every conversation. Episode 13 destroys that assumption: you will learn multi-user scenarios, agent behavior that adapts to the user, persona and role-based access, and user data privacy.

Here is the roadmap for this episode: mapping multi-user scenarios and per-user state, defining personas along with role-based access control, and then organizing data privacy from PII to retention.

Multi-User Scenarios

A production agent is rarely used by one person. Imagine one "checkout assistant" agent used by every customer, or one "ops" agent used by the whole engineering team. Each user has a different context, preferences, and history — and you must not mix them up.

The basic key: every turn must know who the caller is. This identity becomes part of the agent context, used to lock down sessions, memory, and tool results:

  • Session keyed per user — memory and conversation history are stored with a key containing userId, not one global key.
  • Context per turn — each request carries the user context (identity, role, preferences) that is filled in before the agent starts thinking.
  • Identity-aware tool calls — tools that write data must receive the userId from context, not from a global assumption.

The authentication flow in front of the agent looks like this:

auth.ts - per-request identity resolution
export function resolveUser(req) {
  const token = req.headers.authorization?.replace("Bearer ", "");
  const claims = verifyJwt(token);
  return {
    id: claims.sub,
    role: claims.role,
    sessionKey: `session:${claims.sub}`,
    preferredLang: claims.locale ?? "id",
  };
}

The token here is issued by an identity provider (Keycloak, Authentik, Authelia — see the SSO series on this blog), and the agent trusts the claims inside it. The agent itself does not need to manage passwords; its job is only to map claims to behavior.

Per-User Agent Behavior

Once the identity is clear, the agent can behave differently between users without changing code. Several things can be differentiated:

  • Language and tone — a user with a specific language preference is addressed in that language.
  • History and context — a user talking for the first time gets a step-by-step explanation; a returning user gets straight to the point.
  • Personalization — recommendations use user data (order history, location), not global data.

An example implementation is injecting the user profile into the system prompt when building the prompt on every turn:

prompt.ts - injecting the user profile
export function buildSystemPrompt(user, persona) {
  return [
    persona.systemPrompt,
    `Kamu sedang melayani ${user.id} (role: ${user.role}).`,
    "Gunakan bahasa Indonesia kecuali user bicara bahasa lain.",
    user.frequentTopics?.length
      ? `User sering bertanya soal: ${user.frequentTopics.join(", ")}.`
      : "User baru pertama kali; jelaskan lebih detail.",
  ].join("\n");
}

What must be safeguarded: personalization must not make the agent leak data between users. If a tool looks up user data, it must always be filtered by the userId from context — not from the prompt text, which can be manipulated.

Persona & Role-Based Access

Persona defines the agent's personality, style, and behavioral limits. Role-based access restricts which tools may be used based on the user's role. Both are combined through a declarative definition:

personas.yaml
personas:
  support:
    description: "Asisten support yang sabar, jelas, dan to the point"
    temperature: 0.4
    allowedTools:
      - search_knowledge_base
      - read_ticket
      - reply_ticket
    disallowedTools:
      - refund_order
      - grant_access
 
  admin:
    description: "Operator yang boleh mengubah konfigurasi dan akses"
    temperature: 0.2
    allowedTools:
      - search_knowledge_base
      - read_ticket
      - reply_ticket
      - refund_order
      - grant_access

Persona selection happens at runtime based on the user's role and request type. The important consequence: the chosen persona must reject tools outside the allowedTools list at the enforcement level — not just in the prompt. A user with the support role could write "do a refund", but the agent does not have a refund_order tool to call, so the request fails structurally.

Role-based access also controls what the user may read. The read_ticket tool must ensure the user only reads their own tickets, unless their role is indeed allowed to see everything.

Privacy Considerations for User Data

The agent processes user data that is personal in nature. Three areas that must be thought through from the start:

  • Minimize PII in prompts — do not put full personal data into the system prompt when you only need one field. Use internal identities (id, role) in place of names, emails, or addresses.
  • Data retention — define how long conversation history is kept. Idle sessions are deleted, old conversations are archived or anonymized per policy. Do not store forever just because "it is cheap".
  • Isolation and encryption — user data is stored separately per user and encrypted at rest. Never put PII in logs or tool outputs that do not need it.

One simple exercise: before logging or storing, strip the fields that are not needed from the conversation payload:

redact.ts - PII redaction before storage
export function redact(conversation) {
  return {
    userId: conversation.user.id,
    role: conversation.user.role,
    messages: conversation.messages.map((m) => ({
      role: m.role,
      content: maskEmail(m.content),
    })),
  };
}

Keeping the userId is still necessary for audit purposes, but PII details such as email or phone numbers do not need to be stored.

Danger

The classic mistake: the agent returns user A's data to user B because the tool pulls data based on a prompt that can be injected with instructions. Always filter data on the tool side using identity from the authenticated context, not from the conversation contents.

Connecting All the Layers

The correct sequence on every turn:

  1. Authentication — resolve the user from the token (like resolveUser above).
  2. Authorization — pick the persona according to the role and make sure the requested tool is in allowedTools.
  3. Context — build the system prompt with the user profile and persona.
  4. Execution — run the agent loop with the persona-restricted tools; tools read and write data using the userId from context.
  5. Persistence — store history with a per-user session key, pass it through the redaction function, and apply retention.

If all five layers are coherent, the same agent can serve thousands of users with different behavior without leaking data between them. To try the flow locally, run the agent server with bun run dev and send different user tokens from each client.

Conclusion

Episode 13 turned the agent from "one machine for everyone" into "a machine that knows who is talking". You mapped multi-user scenarios with per-user sessions, differentiated agent behavior by identity, defined personas and role-based access that restrict tools structurally, and organized data privacy with PII minimization, retention policies, and session isolation.

Key takeaways:

  • Identity is mandatory on every turn — sessions, memory, and tool calls must be bound to the userId from the authenticated context.
  • Persona governs personality, role governs permissions — and tool restrictions must be enforced, not merely suggested through a prompt.
  • Never pull data from the prompt contents — tools always use identity from context as the filter.
  • Redact PII before persistence, and set clear retention from the start.
  • Encryption and isolation of data between users is non-negotiable, not a bonus feature.

In the next episode 14 we prepare the agent for the reality of an imperfect network: Resilience & Rate Limiting — dealing with provider rate limits, circuit breakers, retry policies, and graceful degradation. See you there!