[{"data":1,"prerenderedAt":116},["ShallowReactive",2],{"blog-en-one-agent-four-identities":3,"header-blog-translations-\u002Fen\u002Fblog\u002Fone-agent-four-identities":113},{"id":4,"title":5,"author":6,"body":7,"date":96,"description":97,"draft":98,"extension":99,"image":100,"meta":101,"navigation":102,"path":103,"seo":104,"stem":105,"tags":106,"translationKey":111,"__hash__":112},"blog_en\u002Fblog\u002Fen\u002Fone-agent-four-identities.md","The Same Agent Had Four Identities","Patrick Hofmann",{"type":8,"value":9,"toc":87},"minimark",[10,14,19,22,30,33,37,40,43,46,50,53,64,67,70,74,77,80,84],[11,12,13],"p",{},"An agent in my setup has a mail address as its identity. That address lives in four places. And on a particular day, the four no longer agreed.",[15,16,18],"h2",{"id":17},"the-four-places","The four places",[11,20,21],{},"The IdP issues the address — it's the source. From there it gets copied: into the troop registry, where agents stand as running instances. Into the org, which records which agent belongs to which organization and who it reports to. And into the task tracker, which assigns agents to teams.",[11,23,24,25,29],{},"Four stores, four times the same address as a key. On top of that there's a fifth, local place — the agent's home directory, where its own ",[26,27,28],"code",{},"auth.json"," lives — but that one is private and not queryable from outside. The four are the problem.",[11,31,32],{},"None of these four stores points at a shared record. Each has its own row with the address as text. There's no foreign key, no trigger, no synchronization back to the source. Each service writes once, and after that its copy lives its own life.",[15,34,36],{"id":35},"how-they-drift-apart","How they drift apart",[11,38,39],{},"The symptom was an agent that stood in a task team but was missing from the troop registry. It had been assigned to a team, but as a running instance it didn't exist — an orphaned membership. From the other direction the same thing: an agent in the org that never arrived in the troop.",[11,41,42],{},"Such gaps arise because the four write paths are independent. An agent gets invited into the org without anyone checking whether it exists in the troop. It gets assigned to a team without a reconcile. Each individual step is correct on its own. Only the sum produces an agent that, depending on whom you ask, exists or doesn't.",[11,44,45],{},"On top of that, the same address can be written differently across stores — upper\u002Flower case, the encoding of the owner domain, a hash suffix that distinguishes an agent from a same-named one. If the comparison runs on the raw string, two spellings of the same identity are suddenly two identities.",[15,47,49],{"id":48},"what-it-took-to-see-it","What it took to see it",[11,51,52],{},"Before I could repair anything, I had to be able to recognize the drift at all. And that starts with reading an agent address canonically.",[11,54,55,56,59,60,63],{},"An agent address has a structure: an agent name, optionally a hash that makes the owner relation unique, then the owner itself, encoded, then the IdP. Pulling the agent name and the real owner mail back out of ",[26,57,58],{},"name+owner+owner_domain@id.openape.ai"," is not trivial — the domain is encoded, the hash has to be stripped, all case-insensitive. That logic used to live in the troop, as a local helper. I moved it into ",[26,61,62],{},"@openape\u002Fcore",", to one place everyone uses: one parser, one truth about what an agent address means.",[11,65,66],{},"On top of it sits a reconcile. It takes the lists from org, troop and tasks, parses each address with the same parser, throws out the humans — addresses that don't follow the agent convention — and lays the rest on top of each other. For each agent it then shows which stores it appears in. Whoever stands in org and troop is coherent. Whoever stands in only one is drifting. Next to it a small diagnostic script that prints exactly that: a table with check marks per store and the list of the orphaned at the end.",[11,68,69],{},"The reconcile repairs nothing. It only shows. Which direction is right — spawn the missing agent, or remove the dead membership — I decide, not the script.",[15,71,73],{"id":72},"why-one-identity-became-four","Why one identity became four",[11,75,76],{},"Not because I wanted four. But because the services arrived one after another. First there was the IdP and the troop — one address, one copy. Then the org came and brought its own table. Then tasks. Each new service copied the identity downstream, because that was the simplest path: no dependency on a foreign store, no migration, each service stays independently deployable. The price only shows up at the end, when four copies exist and none knows the others.",[11,78,79],{},"That's not a dumb mistake, it's an understandable first pattern. Copying identity downstream works as long as it's two places. At four it drifts if nothing leads it back.",[15,81,83],{"id":82},"closing","Closing",[11,85,86],{},"The bug wasn't one broken line. It was an architecture without reconciliation that only showed itself once enough places held the same thing independently. What I built isn't an automatic fix — it's the ability to see the drift: a canonical parser, a reconcile, a diagnostic script. The repair stays a decision. But you can't decide what you can't see.",{"title":88,"searchDepth":89,"depth":89,"links":90},"",2,[91,92,93,94,95],{"id":17,"depth":89,"text":18},{"id":35,"depth":89,"text":36},{"id":48,"depth":89,"text":49},{"id":72,"depth":89,"text":73},{"id":82,"depth":89,"text":83},"2026-06-15","An agent's identity lived in four places, each its own write path, none reconciled against the others. They drifted apart. A note on how one identity became four — not by design, but by accretion — and what it took to make the drift visible at all.",false,"md",null,{},true,"\u002Fblog\u002Fen\u002Fone-agent-four-identities",{"title":5,"description":97},"blog\u002Fen\u002Fone-agent-four-identities",[107,108,109,110],"OpenApe","AI Agents","Identity","Building in Public","one-agent-four-identities","OaaKeCv-QBm057ecmbIJNFPa8U90WFQI1q-5G8-ggUQ",{"en":114,"de":115},"\u002Fen\u002Fblog\u002Fone-agent-four-identities","\u002Fde\u002Fblog\u002Fderselbe-agent-hatte-vier-identitaeten",1781706958410]