Recommended account/user/peer topology for a multi-agent organization (agents as primary actors, not chat counterparts) #4602
Replies: 5 comments
|
We run a comparable topology (multi-machine, multiple harnesses feeding one self-hosted OV), and your Phase 1 friction — a team-scoped 1. Keep 2. Express team+agent in the peer id: 3. For shared project memory across agents on different harnesses, derive the peer from the Git repo rather than the agent: our PR #3540 does exactly this (normalized 4. The profile sedimentation itself is best fixed at the schema layer, not the topology layer: One caveat from practice: whatever mapping you settle on, write the peer-id derivation down per agent (config, not convention) — ad-hoc peer ids are the #1 source of "the memory disappeared" incidents in our fleet, which is the same failure mode RFC #4565 is trying to kill. |
|
Hi, this is a great question, and deserve a deliberate answer. I need to think through and provide more details later, but i can definitely share our initial thought. I believe it would map to your "agent fleet" case natrually. In your case
short answer first. A bit longer overall concept: There're of course other scenarios, where you treat human as user, team as user, topics as user and organization as users. In that sense, what "peer" is, shall be designed by actual use cases. (e.g. team as user, then all the team human and agent members can be peers.) back to some of the questions
In your case, I would suggest agent fleet as account, (manager-human have the root APIKey), agents as users, those interact with agent as peers. While i would not deny your current decision, choose the one works the best for your usecase.
if agent as user, interactors as peer, peer extraction would probably work better. agent owns all the data under viking://user/agent_id, and agent should choose "what to look for in peer folder" based the peer asking questions. Peers folder is a soft boundary
We can discuss further after aligning concept on Q1 Q2.
Concept of account is multi-tenancy. meaning no data is shared between accounts, unless you have a outside layer which manages data share.
APIKey actually represent Account + User. You can have a outer layer which make api_key mode looks like trusted mode.
Thx for sharing. Hope my answer helps. We could definitely discuss more on Discord or Lark channel for more details. even online meetings. You discovery is a great appreciation to our teams effort. Thank you. |
|
Thanks for such a thorough write-up — identity-topology questions like this are genuinely valuable, and we're still working on making the framing crisper in our docs. Following up on Zayn's initial answer, I'd like to nail down one more foundational definition, because I think it answers Q1, Q4, and the meta-question at once. The core definition of user: a data holder, who owns a api_key, not need to be a "person" or an "agent" Reading through your two-phase migration, I noticed you keep asking "should user be the person, the team, or the agent?" — and that question itself presupposes that user is some kind of entity type. That's not actually how we define it:
In other words, a user is first and foremost a credential boundary and a data-ownership boundary — very close to the notion of a database user in traditional databases, which is what Zayn meant by "same as database user." The test for whether something should be a user is not whether it's a natural person, but:
In your scenario, if "each agent needs independent credentials, independent memory, and can be revoked individually" holds, then agent-as-user is the natural answer. Conversely, if your governance boundary is "the team is an indivisible key-holding entity," then team-as-user is perfectly valid too. What made Phase 1 painful wasn't a wrong topology — it was two implementation-layer issues: the profile template's semantic assumption ("the user is a person"), and the write side not carrying peer identity. now-ing's suggestions on both (trim the profile per memory-type schema, and pin peer-id derivation in config rather than convention) are the right fixes. peer: a convenience feature, not an ontological category Your Sense A / Sense B tension (peer as interaction object vs. peer as acting subject) exists because peer was never meant to be a precisely-defined entity type. It's fundamentally a convenience feature we provide: The single most common memory-isolation need in AI applications is "memories from my conversation with A shouldn't leak into my conversation with B." peer simply hardcodes that highest-frequency pattern into a directory convention — memories under peers/<peer_id>/ are a soft boundary: isolated by default, cross-peer recall available when you want it. It lets developers achieve typical interlocutor-memory isolation just through filesystem directories, without first learning an identity model. So don't get stuck on whether a peer is a subject or an object — you can map any relationship between two entities onto the user–peer pair: an agent and the person it serves, an agent and another agent on the same team, even an agent and a code repository (the repo-as-peer idea now-ing raised is a great example). User–peer is a heuristic, minimal modeling primitive — it doesn't claim the world consists of only two kinds of entities. When the binary model isn't enough: accounts as "table splitting" The user–peer binary genuinely doesn't cover every case, especially in a many-to-many relationship network like yours: 8 agents, 5 teams, 1 human — the relationships cross-cut each other, and forcing them all into "one user + a pile of peers" produces exactly the ownership awkwardness you observed. We've seen a pattern in practice that looks more complex but works well: for any given binary relationship, create a dedicated account to hold it. For example, the collaboration memory between agent A and team T, and the shared context between agent A and agent B, each live in their own dedicated account rather than being stuffed into some user's peer directory. This is directly analogous to normalization in relational databases: when one table holds too many independent relationship facts, you split tables and follow third normal form — each relationship fact belongs to exactly one independent relation entity. An account is OV's equivalent of a "table": it's the strongest isolation boundary (zero data sharing between accounts by default). Placing each pairwise/group relationship in its own account turns complex multi-relationship modeling into "create a few more tables," rather than tagging dimensions inside one table. The cost is that you need an outer orchestration layer to decide "which key do I use, which account do I hit, for this read/write" — which is the same layer Zayn referred to as "an outer layer which makes api_key mode look like trusted mode." On auth mode (Q5) A word on auth modes, since this directly concerns the security boundary of a fleet deployment. We ship a pluggable authentication mechanism; trusted and the built-in api_key mode are best understood as two forms of it:
In short: trusted is an escape hatch for "the boundary is already guaranteed externally," not the recommended foundation for fleet multi-tenant isolation. For hard permission guarantees, use api_key mode or a custom auth plugin. What this means for your setup
I hope this three-layer view — user = key-holding isolation boundary, peer = convenient directory convention, account = table split — is helpful. And we'd genuinely welcome a docs PR describing whatever topology you land on; the fleet scenario is honestly the thinnest part of our docs right now. |
|
Thanks for this thorough write-up — the "what is a peer" confusion is worth a precise answer. I've written up how I personally think about OV's memory model, which has helped me reason through exactly the topology questions you raise; it's shared below. The short version up front: in OV, Memory Concepts in OpenVikingPart I — Conceptual Model1. Memory is the continuity of cognition
2. Why
|
| Memory type | Meaning | Typical contents | Storage granularity |
|---|---|---|---|
| User memory — more precisely "served-object memory" | An Agent's cognition of a served object, including service preferences and requirements that apply only to that object | profile, event, entity, preference | Per served object (default object / peer) |
| Agent memory | Cognition an Agent forms about its own way of serving, reusable independent of any specific served object | Extracted, de-individuated, abstracted case, trajectory (traj), experience (exp) | One copy per Agent (self) |
| soul / identity memory | An Agent's cognition of its own identity and the role it plays; soul and identity are distinct memory types in the same upper category | soul, identity | One copy per Agent (self) |
4. Agent and served object
A served object can be a natural person, or any object the caller defines. The type and real identity of served objects are interpreted by the caller; OV only distinguishes them by an opaque peer_id, which needs to be unique within the current Agent.
Different Agents' memories are independent and are not shared merely because they serve the same object. The same peer_id appearing under different user_ids denotes cognition formed independently by two different cognitive subjects about that served object.
Part II — Storage and behavior rules
1. self and peer
self is the Agent's single main directory itself — not a subdirectory named "self". An Agent has exactly one main directory, under which multiple peers/<peer_id>/ directories may exist:
<agent-root: user_id>/ # self, unique
├── user memory of the default served object
├── Agent memory
├── soul / identity memory
└── peers/
├── <peer_id_1>/ # user memory of served object 1
└── <peer_id_2>/ # user memory of served object 2
self is a storage-location concept; it does not mean everything in it is the Agent's cognition about itself. profile, event, entity, preference in the main directory are the Agent's cognition of the default served object; case, trajectory, experience, soul, and identity are the Agent's cognition of itself.
The default served object in the main directory uses the same user-memory structure as each peer (profile, event, entity, preference); the two differ only in storage namespace. Agent memory and soul/identity memory exist only in the main directory, never in peer directories.
A peer directory is created on demand when a message carrying role=user, peer_id=... first appears. The caller is responsible for keeping peer_id stable. OV does not recognize whether different peer_ids correspond to the same real-world object, and never migrates or merges memory across peer directories.
If an object was initially used as the default served object and other peers are added later, the default object's memory stays in the main directory and is not migrated. All user messages without a peer_id are treated as coming from the same default served object; if the caller lets different real-world objects act as the default object, their user memory will be merged — identity disambiguation is the caller's responsibility.
2. Message semantics of peer_id
Only peer_id on role=user messages identifies the served object. peer_id on messages of other roles has no memory-routing semantics and does not participate in served-object identification or user-memory loading:
role=userwithoutpeer_id: the message comes from the default served object → user memory in the main directory.role=userwithpeer_id=A: the message comes from served object A → user memory underpeers/A/.role=assistant/tool/system: theirpeer_idis not used to identify the served object.
The content of assistant/tool/system messages can still serve as evidence from which the Agent forms cognition — e.g., a tool result "A's order has been canceled" can inform an event memory about A. When a context involves multiple served objects, the extractor uses the LLM to attribute cognition semantically, combining the peer_id on user messages with the full conversation context.
3. Loading user memory
A single context can involve the default served object and multiple peers simultaneously. The memory system determines which served-object memories to load from all role=user messages in the context:
- User messages without
peer_idpresent → load the default served object's user memory from the main directory. - User messages with
peer_idpresent → load user memory for each distinctpeer_id. - Both kinds present → load the default object's and all relevant peers' user memory together.
- No user messages in context at all → load no served-object user memory.
Regardless of which served objects are involved, Agent memory and soul/identity memory are always loaded from the main directory.
4. Attribution and classification of user memory
peer_id determines which served object a message comes from, but which memory type a piece of information should be written to still depends on its actual semantics. Not everything uttered by A should be recorded as A's own profile, event, or preference.
For example, a user with peer_id=A says "B likes coffee." Liking coffee is not A's preference, so it must not be written to peers/A/'s preference; nor does this message automatically update peers/B/. If B is a relevant object within A's cognitive scope, this information can be stored as an entity memory related to A, in A's directory. Even if B later appears as an independent served object, A's entity memory is never automatically copied or merged into B's preference.
"Preference" in user memory is defined broadly: not just likes the object explicitly stated, but the Agent's cognition of how this object wants, is suited to, or requires to be served. For instance, "confirm the budget before serving A" applies only to A, so it is A's preference — not Agent memory.
5. The generality boundary of Agent memory
Agent memory holds only service cognition that can be reused independent of any specific served object. The test for whether a piece of cognition belongs to Agent memory: does it still hold after you switch the served object?
- "A likes concise answers" and "be concise when serving A" apply only to A → A's preference.
- "For objects that prefer concise expression, reduce unnecessary explanation" is reusable across served objects → Agent memory.
Agent memory is not isolated per served object. A single interaction may involve the default object and several peers at once, making a unique peer attribution for a case, trajectory, or experience impossible; allowing peer-level Agent memory would additionally introduce multi-attribution, duplicate storage, and cross-peer composition complexity.
Agent memory can originate from the process of serving specific objects, but only the portion that becomes general after extraction, de-individuation, and abstraction is written to the main directory. Here "trajectory" means a processed, reusable trajectory — not the raw message log. The specific de-individuation and abstraction methods are defined by the extraction rules for case, trajectory, and experience.
How this maps to your questions
- Q1 (canonical topology):
user_id= the cognitive subject = the Agent; the things it serves — people, projects, repositories, teams, departments, customers — are the default object or peers. Your Phase 2 (user = principal human, peer = agent) inverts this: the acting agents are the cognitive subjects, so they belong atuser_id. Note the one exception the model explicitly allows: in a dedicated deployment — one Agent instance serving one scope exclusively, never reused across scopes — naminguser_idafter that scope (customer_id_a,department_id_b) is valid, because you are simply naming the cognitive subject after the one object it serves; its default served object then is that scope, and per-scope key issuance and data deletion come for free. The trade-off is that its experience/skills/soul are isolated per scope and never accumulate across scopes. If multiple distinct Agents share one scope, or one Agent serves multiple scopes,user_idmust be the Agent itself and the scope becomes a peer. - Q2 (peer-scoped extraction): yes — routing is driven by
peer_idonrole=usermessages (the write side); that is the sanctioned mechanism, and it is the caller's job to tag stably.X-OpenViking-Actor-Peeris a request-level read view, not write routing. Also note that under this model, a lot of what your Phase 1 saw land at "user (team) level" was actually correct behavior: cases/trajectories/experiences are Agent memory and belong atselfby design — what was wrong was project/object state being sedimented into a person-shapedprofile.mdinstead of being attributed to the project peer. - Q3 (profile semantics):
profilein the main directory is the Agent's cognition of the default served object — not of the Agent itself. The Agent's self-cognition is soul/identity. So when your sessions are generated by agents, the user-level profile should describe the object those sessions serve, and project/fleet knowledge belongs to object peers (or to resources for shared facts). - Q6 (is OV the right fit): the honest boundary is in §1 of Part I: OV memory is per-subject subjective cognition, and different Agents' memories are deliberately independent, even contradictory — it is not designed as a fleet-wide shared authoritative fact store. Shared objective state across agents belongs in
viking://resources/(or an outer orchestration/account layer, as @MaojiaSheng described). What OV gives your fleet is per-Agent cognitive continuity — soul, skills, experience, and per-object cognition that survive model/prompt/instance changes. If "shared long-term memory" means a single converged fact pool, that's a different layer; if it means each agent growing and persisting as a competent colleague, this is exactly the model.
Happy to contribute this as a docs section (a fleet topology guide is the thinnest part of the docs right now) if it helps.
|
Thank you @ZaynJarvis, @MaojiaSheng, and @chenjw — the "user is a credential/data-isolation boundary, not an entity type" framing is the crisp definition this topic was missing, and reading it alongside chenjw's cognitive-subject writeup finally makes the two-phase migration pain in the original question fully explainable. An honest update from my earlier comment: I recommended keeping Two things from operating a fleet that seem worth adding to the picture, both compatible with the agent-as-user model:
For whoever documents this: the credential-boundary test (own key? hard isolation? independent recall visibility?) plus "peers are everyone this user interacts with" would have answered Q1–Q4 up front — this thread is worth distilling into that guide. |
Uh oh!
There was an error while loading. Please reload this page.
Hi OpenViking team — first, thank you for the project. We've been running a self-hosted OpenViking server in production for several months as the shared memory layer for a fleet of AI agents, and we'd like advice on settling our identity topology once and for all. We've read the docs (filesystem model, FAQ, configuration, authentication guide) and the blog (especially "OpenViking User / Peer: Separating Data Owners From Interaction Objects" and "From Assistant to Colleague: Peer Mode"), and we've discussed this internally repeatedly — but the correct mapping for our scenario is still not clear to us. The core of our confusion is the meaning of peer.
Our scenario
We're a small company where the "users" of OpenViking are not humans chatting with a bot — they are autonomous agents organized into teams, supervised by one human principal. Concretely:
api_keymode, local-only endpoint).The journey (and why we migrated)
Phase 1 — naive company mapping. We started with
account = <company>(single tenant),user = <team>(hackers, management, growth, research, persona),peer = <agent>. Isolation worked (one API key per team user space), but we hit semantic friction:profile.md(ours reached version ~49 and became an unreadable accretion layer).MemoryIsolationHandleronly routes memories to peer level whenallowed_peer_idsis non-empty, which is derived frompeer_idfields on the stored session messages. Our Hermes provider sendsX-OpenViking-Actor-Peerbut the stored messages carry nopeer_id, soallowed_peer_ids = {}and 100% of automatic extraction landed at user (team) level. The only peer-level memories we have came from explicit writes, not extraction.Phase 2 — account-per-team migration. We migrated to
account = <team>(five tenants),user = <principal-person>(the same human in every account),peer = <agent>. This aligns better with the documented model and gives true per-team isolation includingviking://resources/. Along the way we also flippedtrusted→api_keymode (needed key-based identity for Web Studio + admin plane) and cleaned up several stray-writer incidents: a coding agent authenticating as its own user instead of a peer under the shared user, a plugin deriving peer IDs from workspace paths, and stale global env config silently resurrecting the obsolete tenant.So we are now stable — but we arrived here by inference from docs, blog posts, and source code reading, not from any authoritative guidance. And some tensions remain unresolved.
Where the confusion is: what is a peer?
The docs and blog seem to use peer in two different senses:
viking://user/<user>/peers/<peer>/memories/).In Sense A, peers are mostly objects of interaction (memory about them). In Sense B, peers are subjects (memory by them). Our agents are unambiguously subjects — but the extraction pipeline (profile semantics, the
peer_idgating above) seems built around Sense A. That ambiguity is what keeps us unsure whether our current mapping is the intended one or just the least-wrong one.Our concrete questions
account = team, user = principal, peer = agentthe intended mapping? Or would you recommend something else (e.g., account = company, user = agent, peer = the people/counterparts the agent interacts with)?peer_id? If our harness stores sessions as the agent acting (not as a human chatting with the agent), what is the sanctioned way to make peer-level extraction work — tag messages withpeer_idclient-side, useX-OpenViking-Actor-Peer, or something else?useris the human principal but the sessions are generated by many different agents, is it expected that the user-level profile accretes facts about projects/agents the person never personally stated? Is there a recommended way to keep "who the user is as a person" separate from "what the fleet knows"?api_keymode theX-OpenViking-Account/Userheaders are stripped server-side whileX-OpenViking-Actor-Peerstill applies. Is per-team API keys + actor-peer header the blessed pattern for multi-agent fleets, or istrustedmode with header-based identity what you'd expect deployments like ours to use?We'd genuinely appreciate guidance — a short "for agent fleets, map it like this" statement would settle months of internal debate, and we're happy to contribute a docs PR describing the resulting topology if that helps others running multi-agent organizations.
Deployment details: OpenViking 0.4.17.1 self-hosted (systemd, loopback-only),
api_keymode, clients via MCP + HTTP, harnesses: Hermes Agent, Codex CLI, opencode, Kimi Code.All reactions