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/mcpwith 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
sourcefield names the agent. Every write records asource. 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.288becomesclaude-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.
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
nochangeand 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.
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.
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_maplists it with each memory, andread_memoryreturns it in the memory’s metadata. It is the name the writer recorded. get_contextnames another agent that saved a fact;retrieve_memorydoes not.get_contextadds thesourcewhen it differs from the reading agent, and omits it when the reader saved the fact or nobody is named.retrieve_memoryreturns the fact’s event id instead. To inspect the complete authorship record, read the ledger:export_memoryreturns every fact event with itssource, 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 aprovenancefield 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_contextnames another agent that saved a fact;retrieve_memorydoes 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
scopeis 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.
