Skip to main content
A Renor workspace belongs to one owner account. Every agent that account signs in reads and writes the same memory: tell Claude Code something once and Codex can recall it. This page says what the engine does when more than one of those agents is active, and what it does not do yet.

How an agent is identified

Two different things identify a caller, and they are not the same thing.
  • The connection token identifies the workspace. An agent connects to https://mcp.renor.cloud/mcp with a connect token, minted when you sign in (OAuth) or as a key from the dashboard. The gateway resolves that token to one workspace on every request and routes there; a missing or revoked token is refused. Tokens are stored only as a hash, can be revoked, and a revocation takes effect on the next request. The token says which workspace, not which agent.
  • The source field names the agent. Every write records a source. For a named connection, the gateway supplies its authenticated connection name and refuses an attempt to name another author. Only an unnamed fallback label, taken from the write or the product named in its User-Agent (claude-code/2.1.288 becomes claude-code), is self-reported. It is shown beside the memory and stored with it. A name that is a path or a URL is reduced to the name it ends in, and the stored name is capped, so a long path is never kept whole.
The dashboard’s connection list is the same idea from the control-plane side: it records one row per client, keyed by the product in the User-Agent, keeps each client’s latest label, and bounds how many it keeps. It tells you which agents have connected, not which one wrote a given memory.
A named connection’s author is authenticated; only an unnamed fallback label is self-reported. The author is not an access control: there are no per-agent permissions, and every agent in the workspace can read and write everything the workspace holds.

When two agents write the same fact

A fact is a subject, a predicate and a value in a scope (Fjord, database, SQLite). For any one subject, predicate and scope there is exactly one current value. A new value does not sit beside the old one; it replaces it, and the old value stays in history. What happens when two agents save the same fact:
  • They do not race. Each workspace runs one engine process, and every lifecycle write (the decision, the ledger append and the index update) runs one at a time inside it. Two calls are handled in the order they arrive; one lands, then the other sees the result.
  • If the second writes the same value, the same way, nothing changes. The receipt says nochange and no new event is written.
  • If the second writes a different value, it becomes current and the first is kept as history. The engine closes the old value (stamps it with when it stopped being current) and opens the new one, in a single transaction. The ledger — the durable record the index is rebuilt from — holds both events, and the event that appears later in the ledger is the current one.
  • If the second event is older than the value it would replace, it is refused and nothing is written. The check runs before the ledger is touched, so a clock that jumped backward cannot leave a self-rejected event behind. The write returns an error; the memory is unchanged.
The same rule applies to a value saved under the same subject and predicate with different words: it is recorded as a new event that replaces the old one, and the old one stays in history.
retrieve_memory with as_of and an ISO date returns what was current at that time, so the value an earlier agent wrote is still answerable.

When agents name the same thing differently

Correction is narrow on purpose. A new value closes the current one only under the same subject, predicate and scope, spelled the same way (case and Unicode form are folded). When several agents write, the same thing often arrives under another name (Atlas project from one agent, Atlas from another) or in another scope (a correction saved without the project’s scope goes to global). Those do not replace each other; both values stay current. Renor names the possible collision instead of guessing:
  • a fact save’s receipt can carry possibleDuplicates: the other current facts related to the one just written;
  • a read can carry conflicts: groups of related current facts whose values disagree.
Two facts are related when they share a predicate and their subjects are one identity, or one name is the other with one more word before or after it. Values are compared after case and accent folding, so Sao Paulo and São Paulo are not a conflict. Nothing is merged or superseded by this; it is a signal for you or the agent to correct.

What a read shows about where a memory came from

Where a memory came from is shown in some places and not in others, and the difference matters when several agents write.
  • Notes and daily-log entries carry their source. memory_map lists it with each memory, and read_memory returns it in the memory’s metadata. It is the name the writer recorded.
  • get_context names another agent that saved a fact; retrieve_memory does not. get_context adds the source when it differs from the reading agent, and omits it when the reader saved the fact or nobody is named. retrieve_memory returns the fact’s event id instead. To inspect the complete authorship record, read the ledger: export_memory returns every fact event with its source, its sentence, its scope and its event id.
  • A flag-gated lane can add provenance to get_context. A deployment can turn on a read-honesty lane that adds a provenance field to each item — the note path or the ledger, and the last date the system recorded it as current. It is off by default, so a default deployment does not show it.
memory_map also lists recent write events, but those carry no source, and retrieve_memory with include_history returns the old value with its dates and its event id, not the agent. A fact’s complete authorship history remains in the ledger; export_memory returns it.

What is not built yet

  • Fact-read authorship differs by tool. get_context names another agent that saved a fact; retrieve_memory does not.
  • Only an unnamed fallback author is self-reported. A named connection’s author is authenticated, but there are no per-agent permissions.
  • Writes are serialized inside one engine process, not across processes. Running two writers against the same workspace at the same time is not protected; the durable cross-process guard (an OS-level writer lock with fsync barriers) is not built. The ledger is durable and the index is rebuilt from it, so a crash is recoverable, but two live processes are not coordinated.
  • A scope is a folder, not a permission. It separates projects inside one workspace; a read that names no scope sees every scope, and any agent can read or write any scope.
  • A workspace is not separated per person. It has one owner account, but the control plane also supports an invited member joining it, and every member reaches the same memory. Nothing partitions a workspace by person or by agent.
  • The connection list is bounded and self-reported. It keeps a limited number of clients per workspace and each client’s latest label; a client names itself, so the list is a convenience, not an audit log.