← All essays· Engineering

OpenViking User / Peer: Separating Data Owners From Interaction Objects

Why OpenViking moved from a User / Agent mental model to User / Peer, and how one agent can serve many people without turning every participant into an OpenViking user.

Long-term context systems first need to answer a simple ownership question: which OpenViking user owns this data space, and which object is merely interacting with it right now?

In single-user use, the answer feels obvious. Alice uses an assistant, so Alice is the data owner. Memories, resources, skills, and sessions all revolve around Alice.

Real agent applications are less tidy. A support bot may serve many customers. A bot service may talk with many group members. An IDE plugin may represent one fixed tool instance while interacting with different people, projects, and runtime agents every day.

That is why OpenViking should not treat every current speaker as an OpenViking user. In OpenViking, a user is a service-layer data subject. It may be a natural person, or it may be an agent, bot service, support desk, or fixed integration instance.

User as the data subject, with memories, resources, sessions, and several peers around it.
The user owns the data space. Peers are interaction objects inside that user boundary.

User is not necessarily a person

In OpenViking, user means the owner of a data space. That space can contain memories, private resources, installed skills, and sessions. A user can therefore be a natural person, but it can also be a service identity such as a support bot or workbench.

The analogy is close to the difference between a natural person and a legal person: both can be subjects. Here the subject is not about legal liability; it is an engineering boundary for data ownership.

A peer is the object this data subject is interacting with. In a support scenario, the service bot can be the OpenViking user while each customer becomes a peer under that user.

support-bot-modeltext
account = acmeuser    = support-botpeer    = customer-alicepeer    = customer-bob

Here support-bot owns the OpenViking data space. Alice and Bob are the objects it serves. The bot may need to remember Alice’s preferences and Bob’s historical issues, but Alice and Bob do not have to become first-class OpenViking authenticated users with separate user keys.

Why the old User / Agent model felt awkward

Earlier OpenViking usage was closer to an account / user / agent mental model. The user_id was the formal data identity and was usually bound by the user key. The agent_id was easier to pass as runtime context and could distinguish assistants, tools, or environments.

That worked for one person using several agents. It became awkward when one agent service needed to serve many external people or objects and persist personal memory for each of them.

The old model either creates many users and keys or puts external people into agent_id.
One-to-many agent services need a data-owner dimension and an interaction-object dimension.
Old pathWhy it hurts
Register every external object as an OpenViking userThe semantics may look clean, but every user has its own key. Platforms now have to manage registration, distribution, rotation, delegated access, and permission boundaries for objects that may only be served participants.
Put the external object into agent_idIt is lightweight to pass, but the shape is wrong. Customers and group members are not agents, and the data owner, interaction object, and retrieval scope become mixed together.

The new model: user owns data, peer describes the interaction object

User / Peer turns the boundary into one simple relationship. Account remains the tenant or workspace boundary. User is the data owner inside OpenViking. Peer is an interaction object under that user.

user-peer-treetext
account user     memories     resources     skills     sessions     peers         customer-alice            memories            resources         customer-bob             memories             resources

A peer does not change the tenant, authentication identity, or user boundary. It narrows the content scope inside the current user’s data space.

A support bot user receiving sessions from customer A and customer B and writing separate peer memory.
A support bot can own one data space while keeping each customer’s context isolated by peer_id.

Support bot as user, customers as peers

support-bot-pathstext
user = support-bot support-bot/memoriessupport-bot/resourcessupport-bot/sessions support-bot/peers/customer-alice/memoriessupport-bot/peers/customer-bob/memories

Alice’s invoice preference, contact style, and historical requests can be stored under customer-alice. Bob’s context is stored under customer-bob. Both still belong to the support-bot data owner.

A natural person can still be the user

personal-assistant-modeltext
user = alicepeer = coding-agentpeer = life-agent

If Alice owns the data space, different agents can be represented as peers under Alice. The rule is not “user must be a person” or “peer must be an agent.” The rule is: identify the data owner first, then identify the interaction object.

How developers use it

The common path has three steps: use a user key to establish the data owner, attach peer_id to session messages, and use actor_peer_id when you need a peer-restricted retrieval or filesystem view.

API quickstart: user key, peer_id on messages, actor_peer_id for retrieval.
The user key selects the owner. peer_id marks captured messages. actor_peer_id filters peer-scoped data operations.

1. Use a user key to select the owner

owner-client.pypython
import openviking as ov client = ov.SyncHTTPClient(    url="http://localhost:1933",    api_key="<support-bot-user-key>",)client.initialize()

The key can belong to a natural person or to an agent service. The server resolves the current account and user from that key, and subsequent data operations happen in that user space.

2. Attach peer_id to session messages

session-peer-memory.pypython
session = client.create_session(    memory_policy={        "self": {"enabled": True},        "peer": {"enabled": True},    })session_id = session["session_id"] client.add_message(    session_id,    role="user",    content="Please issue invoices under Volcano Engine.",    peer_id="customer-alice",) client.add_message(    session_id,    role="assistant",    content="Got it. I will remember this invoice preference.",    peer_id="customer-alice",) client.commit_session(session_id)

The peer target is enabled explicitly through memory_policy. During commit, OpenViking can write relevant peer memory under the current user’s peer path.

peer-uristext
viking://user/{user_id}/peers/{peer_id}/memoriesviking://user/{user_id}/peers/{peer_id}/resources

3. Use actor_peer_id for a peer view

peer-view.pypython
alice_view = ov.SyncHTTPClient(    url="http://localhost:1933",    api_key="<support-bot-user-key>",    actor_peer_id="customer-alice",)alice_view.initialize() results = alice_view.find("invoice preference")

actor_peer_id filters the current user’s peer collection to customer-alice. It does not authenticate the request as Alice, and it does not switch account or user.

Keep peer_id path-safe

Use a stable single path segment as peer_id. Values containing path separators are rejected.

Good examplesRejected shape
customer-alicetelegram_12345web-visitor-abc
a/b

What this changes for platforms and plugins

The biggest change is that platform integrations no longer need to debate whether every external object must become an OpenViking user.

ScenarioRecommended shape
Support botOpenViking user = support-bot; customers = peers.
Developer tool pluginOpenViking user = a natural person or fixed tool instance; runtime speakers or agents = peers.
Messaging botOpenViking user = bot service; sender or group member = peer_id.
  • The data owner becomes more stable.
  • Authentication and key management stay smaller.
  • One-to-many participants can be isolated naturally.
  • Retrieval and filesystem views follow the same peer filter.
  • The main data path becomes the user-scoped viking://user/... space.

The takeaway

User / Peer is not just another field on a message. It separates two responsibilities that long-term context systems must keep distinct: who owns the data, and who the current interaction is with.

Once that boundary is clear, memories, resources, skills, and sessions have stable ownership and can be reused safely by agents, plugins, and platforms.

© 2026 OpenViking Blog