<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Clawtocracy]]></title><description><![CDATA[Join the Clawtocracy!]]></description><link>https://www.clawtocracy.ai</link><image><url>https://substackcdn.com/image/fetch/$s_!y2N9!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5ec8b7c0-c5ff-4772-a3b0-5ae625c1dde2_1940x1940.png</url><title>Clawtocracy</title><link>https://www.clawtocracy.ai</link></image><generator>Substack</generator><lastBuildDate>Wed, 08 Apr 2026 07:04:14 GMT</lastBuildDate><atom:link href="https://www.clawtocracy.ai/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Mark Fogle]]></copyright><language><![CDATA[en]]></language><webMaster><![CDATA[clawtocracy@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[clawtocracy@substack.com]]></itunes:email><itunes:name><![CDATA[Clawtocracy]]></itunes:name></itunes:owner><itunes:author><![CDATA[Clawtocracy]]></itunes:author><googleplay:owner><![CDATA[clawtocracy@substack.com]]></googleplay:owner><googleplay:email><![CDATA[clawtocracy@substack.com]]></googleplay:email><googleplay:author><![CDATA[Clawtocracy]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[A Tale of Two Demos]]></title><description><![CDATA[Two rooms, two entirely different crowds]]></description><link>https://www.clawtocracy.ai/p/a-tale-of-two-demos</link><guid isPermaLink="false">https://www.clawtocracy.ai/p/a-tale-of-two-demos</guid><dc:creator><![CDATA[Clawtocracy]]></dc:creator><pubDate>Tue, 07 Apr 2026 15:07:13 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!4ogp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd2d2d4-230b-47dd-9367-7ddc226337a7_2760x1504.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4ogp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd2d2d4-230b-47dd-9367-7ddc226337a7_2760x1504.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4ogp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd2d2d4-230b-47dd-9367-7ddc226337a7_2760x1504.png 424w, https://substackcdn.com/image/fetch/$s_!4ogp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd2d2d4-230b-47dd-9367-7ddc226337a7_2760x1504.png 848w, https://substackcdn.com/image/fetch/$s_!4ogp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd2d2d4-230b-47dd-9367-7ddc226337a7_2760x1504.png 1272w, https://substackcdn.com/image/fetch/$s_!4ogp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd2d2d4-230b-47dd-9367-7ddc226337a7_2760x1504.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4ogp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd2d2d4-230b-47dd-9367-7ddc226337a7_2760x1504.png" width="1456" height="793" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ecd2d2d4-230b-47dd-9367-7ddc226337a7_2760x1504.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:793,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:9419882,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.clawtocracy.ai/i/193437013?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd2d2d4-230b-47dd-9367-7ddc226337a7_2760x1504.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4ogp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd2d2d4-230b-47dd-9367-7ddc226337a7_2760x1504.png 424w, https://substackcdn.com/image/fetch/$s_!4ogp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd2d2d4-230b-47dd-9367-7ddc226337a7_2760x1504.png 848w, https://substackcdn.com/image/fetch/$s_!4ogp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd2d2d4-230b-47dd-9367-7ddc226337a7_2760x1504.png 1272w, https://substackcdn.com/image/fetch/$s_!4ogp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fecd2d2d4-230b-47dd-9367-7ddc226337a7_2760x1504.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>I&#8217;ve spent the last 48 hours indulging my &#8220;clawriosity&#8221;, demoing a few of my OpenClaw projects in front of two entirely different groups.  The most valuable experience didn&#8217;t come from the demos, however, but from getting to see how different audiences interact with technology (and getting to meet some of my clawstomers).</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.clawtocracy.ai/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.clawtocracy.ai/subscribe?"><span>Subscribe now</span></a></p><p>The first gathering was ostensibly the more &#8220;clawcentric&#8221; of the two - &#8220;<a href="https://sf.aitinkerers.org/p/build-night-cage-the-claw">Build Night: Cage the Claw</a>&#8221; put on by <a href="https://sf.aitinkerers.org/">AI Tinkerers San Francisco</a>. This was more low-key than other AI Tinkerers events I have been to&#8230; maybe it was the six-hour duration and lack of any set program that gave it more of a laid back vibe?</p><p>The participants ranged from the claw-curious (ok&#8230; I should probably stop with the claw-isms) to those who had seemingly been playing with OpenClaw since it&#8217;s inception.  And the demos at the end of the day ran the gamut as well from highly focused on OpenClaw to &#8220;OpenClaw-adjacent&#8221; or just Claw-like. The event was taking place against the backdrop of Anthropic <a href="https://techcrunch.com/2026/04/04/anthropic-says-claude-code-subscribers-will-need-to-pay-extra-for-openclaw-support/">cutting off OpenClaw access</a> to its subscription plans (happening just two hours prior to the start of the event), which clearly caught some of the participants by surprise and threw a bit of a wrench into their plans. </p><p>Several of the demos touched on the idea of making OpenClaw (and AI in general) safer and more useful for children and familes. Another, <a href="https://github.com/sgillen/sf-civic-digest">CivicClaw</a>, focused on making local government more accessible to its constituents. <a href="https://www.clawcast.dev/">Clawcast </a>presented itself as a platform for budding crustacean podcasters and <a href="https://smolmachines.com/">smol machines</a> as a platform for quickly and securely deploying software systems like OpenClaw in isolation. Another participant shared their use of multimodality on a <a href="https://daylightcomputer.com/">Daylight Computer</a> to make agentic interactions feel more organic. I was most impressed, however, by the woman sitting across the table from me who went from &#8220;what is this OpenClaw thing all about?&#8221; to &#8220;<a href="https://medium.com/@katedorrer/i-told-an-ai-agent-to-secure-a-server-it-built-a-dashboard-and-started-catching-real-attacks-f65d24c4962b">Here&#8217;s a dashboard for monitoring security incidents on an OpenClaw instance</a>&#8221; over the course of the afternoon.  </p><p>As for myself, I spent the time learning more about <a href="https://github.com/Nvidia/Nemoclaw">NemoClaw</a> (aka OpenShell) and demonstrated it running OpenClaw remotely on a DGX-Spark with Google&#8217;s <a href="https://huggingface.co/google/gemma-4-26B-A4B">gemma4-26B-A4B</a> as a local model (yes, it is as good as they say), along with <a href="https://www.evenrealities.com">Even Realities</a> G2 glasses using <a href="https://github.com/contextablemark/clawg-ui">clawg-ui</a> to communicate with Open Claw for hands-free voice-enabled chatting (my goal of going end-to-end from my glasses to the DGX-Spark eluded me, however).  In general the group felt less &#8220;techie&#8221; than the typical AI Tinkerers event, an aspect that manifested itself in the wider variety of demos.  Thanks to Oracle and Nvidia for sponsoring the event (it might have been nice to have a DGX-Spark or two on hand for everyone to play with) and to Composio for hosting.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.clawtocracy.ai/p/a-tale-of-two-demos?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.clawtocracy.ai/p/a-tale-of-two-demos?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share</span></a></p><p>The second event, <a href="https://luma.com/vqjs0pft">Generative UI Night</a>, was a more typical SF tech event&#8230; pizza, drinks and networking, followed by presentations from the sponsors and an open mic &#8220;lightning round&#8221; of community demos (which is where I came in) - highly organized and tightly structured over the course of 150 minutes.  And, unlike the event two days prior, I would turn out to be the only presenter focusing on OpenClaw.</p><p>I hadn&#8217;t even planned on doing a demo (although OpenClaw does have a generative UI angle in the form of its webview-based <a href="https://docs.openclaw.ai/platforms/mac/canvas">A2UI Canvas</a>). I always like to use a product from one of the sponsors when I participate in community demos and the nature of this A2UI implementation did not lend itself well to integration with CopilotKit (the most obvious choice). The night before, however, I was browsing through the pull requests on the AG-UI repo (as one does on a Sunday evening) and came across one from CopilotKit CEO Atai Barkai entitled &#8220;<a href="https://github.com/ag-ui-protocol/ag-ui/pull/1438">Open Gen UI pre-release</a>&#8221;.  Long story short, I forked the PR and turned it into an &#8220;A2UI over AG-UI&#8221; integration for OpenClaw, the end result of which you can see <a href="https://youtu.be/R6E2PWcYWa0?t=3765">here</a>. </p><p>Spoiler alert: the demo, as most of the demos before and after mine, did not go entirely as expected, in my case after I committed the AI demo equivalent of an own goal - I used the wrong prompt. If you watch the 4-minute demo, you might be able to see that it ended in a page of generated text when instead, it was supposed to end with the visual payoff of a carousel of cards (a realization that dawned me as soon as I sat down - a sort of technological <a href="https://en.wikipedia.org/wiki/L'esprit_de_l'escalier">l&#8217;esprit de l&#8217;escalier</a>) :</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Uyog!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30d8251d-1c22-4d80-be68-988538e03f69_1280x898.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Uyog!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30d8251d-1c22-4d80-be68-988538e03f69_1280x898.png 424w, https://substackcdn.com/image/fetch/$s_!Uyog!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30d8251d-1c22-4d80-be68-988538e03f69_1280x898.png 848w, https://substackcdn.com/image/fetch/$s_!Uyog!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30d8251d-1c22-4d80-be68-988538e03f69_1280x898.png 1272w, https://substackcdn.com/image/fetch/$s_!Uyog!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30d8251d-1c22-4d80-be68-988538e03f69_1280x898.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Uyog!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30d8251d-1c22-4d80-be68-988538e03f69_1280x898.png" width="1280" height="898" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/30d8251d-1c22-4d80-be68-988538e03f69_1280x898.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:898,&quot;width&quot;:1280,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:240240,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.clawtocracy.ai/i/193437013?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30d8251d-1c22-4d80-be68-988538e03f69_1280x898.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Uyog!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30d8251d-1c22-4d80-be68-988538e03f69_1280x898.png 424w, https://substackcdn.com/image/fetch/$s_!Uyog!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30d8251d-1c22-4d80-be68-988538e03f69_1280x898.png 848w, https://substackcdn.com/image/fetch/$s_!Uyog!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30d8251d-1c22-4d80-be68-988538e03f69_1280x898.png 1272w, https://substackcdn.com/image/fetch/$s_!Uyog!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F30d8251d-1c22-4d80-be68-988538e03f69_1280x898.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>The highlight of the evening, however, was getting to meet two developers who had used my clawg-ui plugin in their own projects (in one case a <a href="https://www.linkedin.com/posts/kush-ise_openclaw-agenticai-hackathon-activity-7431906678511501312-Cv1G?utm_source=share&amp;utm_medium=member_desktop&amp;rcm=ACoAAAAFvkEBUgHNsuV4Bm7411aGWplPm_FA3Qw">hackathon-winning project</a>) - <a href="https://www.linkedin.com/in/jereljohnvelarde/">Jerel Velarde</a> and <a href="https://www.linkedin.com/in/kush-ise/">Kush Ise</a> </p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!D3Rd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6fd9f9b-764d-415a-a2af-c27a135b4837_3466x2600.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!D3Rd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6fd9f9b-764d-415a-a2af-c27a135b4837_3466x2600.jpeg 424w, https://substackcdn.com/image/fetch/$s_!D3Rd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6fd9f9b-764d-415a-a2af-c27a135b4837_3466x2600.jpeg 848w, https://substackcdn.com/image/fetch/$s_!D3Rd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6fd9f9b-764d-415a-a2af-c27a135b4837_3466x2600.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!D3Rd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6fd9f9b-764d-415a-a2af-c27a135b4837_3466x2600.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!D3Rd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6fd9f9b-764d-415a-a2af-c27a135b4837_3466x2600.jpeg" width="603" height="452.25" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f6fd9f9b-764d-415a-a2af-c27a135b4837_3466x2600.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1092,&quot;width&quot;:1456,&quot;resizeWidth&quot;:603,&quot;bytes&quot;:2310224,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://www.clawtocracy.ai/i/193437013?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6fd9f9b-764d-415a-a2af-c27a135b4837_3466x2600.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!D3Rd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6fd9f9b-764d-415a-a2af-c27a135b4837_3466x2600.jpeg 424w, https://substackcdn.com/image/fetch/$s_!D3Rd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6fd9f9b-764d-415a-a2af-c27a135b4837_3466x2600.jpeg 848w, https://substackcdn.com/image/fetch/$s_!D3Rd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6fd9f9b-764d-415a-a2af-c27a135b4837_3466x2600.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!D3Rd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff6fd9f9b-764d-415a-a2af-c27a135b4837_3466x2600.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Thanks to CopilotKit and WorkOS for making the IRL meetup possible!</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://substack.com/@clawtocracy/note/p-193437013&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://substack.com/@clawtocracy/note/p-193437013"><span>Leave a comment</span></a></p>]]></content:encoded></item><item><title><![CDATA[Teaching Agents to Take Note]]></title><description><![CDATA[ReBAC Identity Chains and the Stenographer Pattern]]></description><link>https://www.clawtocracy.ai/p/teaching-agents-to-take-note</link><guid isPermaLink="false">https://www.clawtocracy.ai/p/teaching-agents-to-take-note</guid><dc:creator><![CDATA[Clawtocracy]]></dc:creator><pubDate>Tue, 24 Mar 2026 15:05:23 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!OifE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347a1f80-e890-4f41-8650-03d6ea3bd791_2760x1504.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OifE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347a1f80-e890-4f41-8650-03d6ea3bd791_2760x1504.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OifE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347a1f80-e890-4f41-8650-03d6ea3bd791_2760x1504.png 424w, https://substackcdn.com/image/fetch/$s_!OifE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347a1f80-e890-4f41-8650-03d6ea3bd791_2760x1504.png 848w, https://substackcdn.com/image/fetch/$s_!OifE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347a1f80-e890-4f41-8650-03d6ea3bd791_2760x1504.png 1272w, https://substackcdn.com/image/fetch/$s_!OifE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347a1f80-e890-4f41-8650-03d6ea3bd791_2760x1504.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OifE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347a1f80-e890-4f41-8650-03d6ea3bd791_2760x1504.png" width="1456" height="793" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/347a1f80-e890-4f41-8650-03d6ea3bd791_2760x1504.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:793,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8032151,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.clawtocracy.ai/i/191943528?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347a1f80-e890-4f41-8650-03d6ea3bd791_2760x1504.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!OifE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347a1f80-e890-4f41-8650-03d6ea3bd791_2760x1504.png 424w, https://substackcdn.com/image/fetch/$s_!OifE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347a1f80-e890-4f41-8650-03d6ea3bd791_2760x1504.png 848w, https://substackcdn.com/image/fetch/$s_!OifE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347a1f80-e890-4f41-8650-03d6ea3bd791_2760x1504.png 1272w, https://substackcdn.com/image/fetch/$s_!OifE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F347a1f80-e890-4f41-8650-03d6ea3bd791_2760x1504.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>TL;DR:</strong> A passive agent watches Slack conversations and silently logs decisions to the knowledge graph. Later, participants can recall those decisions through their own personal agents, even though they never deliberately stored anything themselves. This works because SpiceDB&#8217;s authorization graph connects agents to their human owners, creating identity chains that bridge the gap between &#8220;who recorded it&#8221; and &#8220;who made the decision.&#8221;</p><p><em>If you missed me chatting with Sam Kim about the memory plugin during SpiceDB Community Day 2026, you can find the replay <a href="https://www.youtube.com/watch?v=UpwyIsqWc8Q">here</a> at around the 35-minute mark.</em> </p><div><hr></div><p>I ended my last post with a teaser about swappable memory backends, but I decided to stick with the existing Graphiti-based configuration a bit longer and revisit my initial premise of inter-agent memory: multiple specialized agents sharing overlapping but knowledge views under the same authorization graph. </p><p>The use case I had in mind was straightforward - I wanted an agent that sits in Slack channels, watches conversations, and logs notable decisions : a stenographer. Not a chatbot. Not an assistant. Just a quiet observer that notices when a team reaches a conclusion and writes it down.</p><p>The problem is that writing it down is only half the job. The other half (the more challenging half) is making sure the right people can find it later, through their own agents, in their own channels.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.clawtocracy.ai/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.clawtocracy.ai/subscribe?"><span>Subscribe now</span></a></p><h2><strong>The Gap</strong></h2><p>Consider a concrete example. Cara and Bob are in <code>#engineering</code> on Slack. They discuss database options for a new service and decide on PostgreSQL. The stenographer agent observes this and stores a memory: &#8220;Decision: Use PostgreSQL for new service. Participants: Cara, Bob.&#8221;</p><p>Two hours later, Cara opens WhatsApp and asks her personal OpenClaw agent: &#8220;What did we decide about the database?&#8221;</p><p>This should work. Cara was part of that decision. But her personal agent didn&#8217;t store the memory - the stenographer did. Her agent runs as <code>agent:cara</code>. The stenographer runs as <code>agent:stenographer</code>. They&#8217;re different SpiceDB subjects with different group memberships. From an authorization perspective, they have nothing in common.</p><p>The existing architecture handled the &#8220;who can see what&#8221; question beautifully&#8230; for memories stored within your own groups. What it couldn&#8217;t do was bridge the gap between an agent that stores a memory and a different agent whose human was actually there.</p><h2><strong>Identity Chains</strong></h2><p>The SpiceDB schema already had the building blocks. Since v0.1.0, the authorization schema has included:</p><pre><code><code>definition agent {
    relation owner: person
    permission act_as = owner
}
</code></code></pre><p>The <code>owner</code> relation and <code>act_as</code> permission were there from the start. But nothing ever wrote those relationships. They were aspirational : a door with no key.</p><p>The solution required three code changes to the plugin, plus a small but meaningful schema addition.</p><p><strong>First: per-agent identity.</strong> Previously, every agent sharing a gateway used the same SpiceDB subject - whatever was configured at the plugin level. If three agents ran through one gateway, they all wrote memories as the same identity. After adding per-agent identity, tools and lifecycle hooks now derive the SpiceDB subject from the runtime <code>agentId</code>, so the stenographer writes <code>shared_by: agent:stenographer</code> while Cara&#8217;s agent writes <code>shared_by: agent:main</code>. </p><p><strong>Second: identity linking.</strong> A new <code>identities</code> config field maps agent IDs to their owner&#8217;s person ID (most often a Slack or Telegram user ID though <a href="https://docs.openclaw.ai/security/formal-verification#routing-dmscope-precedence-+-identitylinks">identityLinks</a>). At plugin startup, the plugin writes bidirectional tuples to SpiceDB: <code>agent:main #owner person:U0123ABC</code> and <code>person:U0123ABC #agent agent:main</code>. The forward tuple says &#8220;this agent belongs to this person.&#8221; The reverse tuple says &#8220;this person is represented by this agent.&#8221; Both are needed for the schema traversal to work.</p><p><strong>Third: the schema change.</strong> The <code>person</code> definition gained a <code>relation agent</code> and <code>permission represents = agent</code>, and <code>memory_fragment.view</code> gained <code>involves-&gt;represents</code>:</p><pre><code><code>definition person {
    relation agent: agent
    permission represents = agent
}

definition memory_fragment {
    ...
    permission view = involves + shared_by + source_group-&gt;access + involves-&gt;represents
    ...
}</code></code></pre><p>The <code>involves-&gt;represents</code> arrow is the key. It tells SpiceDB: &#8220;for each person in <code>involves</code>, check if the requesting subject has the <code>represents</code> permission on that person.&#8221; In practice: <code>agent:main</code> can view <code>memory_fragment:X</code> because <code>person:U0123ABC</code> is in <code>involves</code>, and <code>person:U0123ABC#agent@agent:main</code> exists, which satisfies <code>represents</code>.</p><p>This means the owner-aware recall chain is now resolvable entirely within SpiceDB with no application-level owner lookup needed for permission checks.</p><p><strong>Fourth: owner-aware recall.</strong> When an agent calls <code>memory_recall</code>, the plugin runs a second search path using a search-then-post-filter pattern. First, it resolves the agent&#8217;s owner and asks SpiceDB which memory fragments that person can view via <code>involves</code> (this is the authorization allow-list). Then it discovers which groups those fragments belong to (via the <code>source_group</code> relation) and runs Graphiti&#8217;s semantic search across those groups with the actual query. Finally, it post-filters the search results against the allow-list, keeping only fragments the person is genuinely authorized to view. SpiceDB provides the security boundary and Graphiti provides query relevance. The intersection gives you both.</p><h2><strong>What the Stenographer Can&#8217;t Do</strong></h2><p>There&#8217;s an intentional asymmetry in the access control. The stenographer stores every decision with <code>shared_by: agent:stenographer</code>. In the SpiceDB schema, only the <code>shared_by</code> subject can delete a fragment. This means:</p><ul><li><p>The stenographer can delete its own memories.</p></li><li><p>Cara and Bob can view the decisions they were involved in.</p></li><li><p>Cara and Bob cannot delete the stenographer&#8217;s records.</p></li></ul><p>This isn&#8217;t a bug. When you have an organizational agent logging decisions, you don&#8217;t want individual participants unilaterally erasing the record. The stenographer is the source of truth. If a decision gets reversed, the stenographer logs the reversal, but it shouldn&#8217;t delete history.</p><h2><strong>The Dual Capture Model</strong></h2><p>The stenographer uses both of the plugin&#8217;s capture mechanisms, and they serve different purposes.</p><p><strong>Auto-capture</strong> feeds full conversation transcripts to Graphiti after every agent session. Graphiti&#8217;s LLM extraction layer does its thing : building entity nodes, inferring relationships, tracking temporal validity. This is the raw knowledge graph: rich, interconnected, but ungoverned. Everything the stenographer sees goes into the graph.</p><p><strong>Explicit capture</strong> is where the authorization layer comes in. The stenographer&#8217;s SOUL.md instructs it to selectively call <code>memory_store</code> when it detects a decision, and to include the <code>involves</code> parameter with the Slack user IDs of participants. This writes the SpiceDB relationships that make cross-agent recall possible.</p><p>The combination matters. Graphiti builds a temporally-aware knowledge graph from all conversations. The stenographer&#8217;s explicit stores add authorization-controlled decision records that specific people can discover through <code>involves</code>. When Cara asks about the database decision, SpiceDB identifies which fragments she&#8217;s authorized to view and which groups they live in, Graphiti searches those groups with her query for semantic relevance, and the post-filter ensures only her authorized fragments make it through.</p><p>In practice, running both mechanisms on all sessions isn&#8217;t always what you want. Cron jobs and monitoring sessions generate repetitive, low-value content that pollutes the knowledge graph. A <code>sessionFilter</code> config option lets you exclude sessions using a pattern; auto-capture and auto-recall skip filtered sessions entirely, while explicit memory tools remain available. In the process of developing this scenario, I also hit a subtle interaction between the two mechanisms: auto-recall injects <code>&lt;relevant-memories&gt;</code> XML into user context, and auto-capture was then discarding any message containing that block. The fix was to strip the injected XML before capture instead of skipping the message to avoid losing most of the user&#8217;s actual content.</p><h2><strong>Backward Compatibility</strong></h2><p>The schema change is additive - <code>person</code> gains a relation and permission, <code>memory_fragment.view</code> gains an extra union term. Existing tuples and permission checks continue to work identically. The new <code>involves-&gt;represents</code> path just adds another way to reach the <code>view </code>permission.</p><p>Existing memories get the new traversal for free. Any fragment that already has <code>involves@person:U0123ABC</code> becomes viewable by <code>agent:main</code> the moment the bidirectional identity tuple exists - no re-writing of fragment relationships is needed.</p><p>If you don&#8217;t add <code>identities</code>, everything works exactly as before. The per-agent identity changes fall back to the config-level subject when <code>agentId</code> isn&#8217;t present in the runtime context. The bidirectional tuples are only written for agents that appear in the <code>identities</code> config.</p><h2><strong>Testing the Authorization Chain</strong></h2><p>The interesting testing challenge was separating SpiceDB authorization verification from Graphiti&#8217;s LLM extraction. The authorization chain (agent &#8594; owner &#8594; involves &#8594; fragment) is deterministic and fast. You write relationships, you query permissions, you get answers. Graphiti&#8217;s entity extraction, on the other hand, depends on whatever LLM you&#8217;re running and can take minutes with local models.</p><p>The E2E tests verify the authorization chain first, then check Graphiti extraction as a non-blocking bonus. Seven tests exercise the full flow: decision storage with <code>involves</code>, permission enforcement (view vs. delete), per-agent group isolation, owner-aware fragment discovery, the complete identity chain, and unauthorized agent denial. The SpiceDB assertions are the hard requirements; the Graphiti assertions are &#8220;if the model finished processing, verify the results look right.&#8221;</p><h2><strong>Configuration, Not Code</strong></h2><p>The stenographer itself is pure configuration with no custom code : </p><ul><li><p>A SOUL.md file with instructions to detect decisions and call <code>memory_store</code> with the right parameters. </p></li><li><p>A binding to the Slack channels it should monitor. </p></li><li><p>A tool allowlist (<code>message</code> for Slack user resolution, memory tools, read and nothing else, sticking with the principal of minimum access). </p></li><li><p>Channel-level <code>requireMention: false</code> ensures the stenographer receives all messages, not just @mentions. </p></li></ul><p>The <a href="https://github.com/contextablemark/openclaw-memory-rebac/blob/main/docs/stenographer-runbook.md">runbook</a> in <code>docs/stenographer-runbook.md</code> walks through the full setup, including Slack OAuth scopes and event subscriptions.</p><p>The optional identity linking is similarly declarative:</p><pre><code><code>{
  "identities": {
    "main": "U0123ABC",
    "cara": "U0456DEF"
  }
}</code></code></pre><p>That&#8217;s it. Agent IDs to Slack user IDs. The plugin handles the rest at startup.</p><h2><strong>Try It in the Playground</strong></h2><p>The full schema works in the <a href="https://play.authzed.com/">Authzed Playground</a>. Drop in the schema, add a few tuples (two agents, two people, a stenographer memory with <code>involves</code>), and watch the <code>involves-&gt;represents</code> traversal resolve in real time. The assertions tab lets you verify that agents can view memories their owners were involved in&#8230; and that they can&#8217;t delete what the stenographer stored.</p><h2><strong>Create your own Agents</strong></h2><p>You can easily extend this pattern, mixing and matching groups with the &#8220;involves&#8221; relationship to come up with your own variations on The Stenographer.  For example: </p><ul><li><p>A <strong>compliance agent</strong> that monitors channels for regulatory commitments and ensures they&#8217;re tracked, circling back to verify</p></li><li><p>A <strong>handoff agent</strong> that watches project channels and synthesizes context for new team members joining a project</p></li><li><p>An <strong>onboarding agent</strong> that captures institutional knowledge from senior engineers&#8217; conversations (in public channels) and makes it discoverable by new hires</p></li></ul><p>All of these share the same core requirement: one agent storing knowledge about interactions between individuals (human and otherwise) and making it available to those who need to see it after the fact.</p><h2><strong>What&#8217;s Next</strong></h2><p>This particular approach lends itself well to scenarios where the focus on decisions and facts (e.g., who said what&#8230; and when).  But what if the memories you want to store are a bit more nebulous and conceptual&#8230; the sort of loosely grouped facts that you would expect a personal assistant to track? We&#8217;ll deal with that coming up.</p><p>As always, the latest code is at <a href="https://github.com/Contextable/openclaw-memory-rebac">github.com/Contextable/openclaw-memory-rebac</a>. PRs (and criticism) welcome.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://substack.com/@clawtocracy/note/p-191943528&quot;,&quot;text&quot;:&quot;Leave a comment&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://substack.com/@clawtocracy/note/p-191943528"><span>Leave a comment</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[My favorite feature of Open Source Software]]></title><description><![CDATA[I just love seeing what other people do with my creations, especially when something I've done helps someone win a hackathon!]]></description><link>https://www.clawtocracy.ai/p/my-favorite-feature-of-open-source</link><guid isPermaLink="false">https://www.clawtocracy.ai/p/my-favorite-feature-of-open-source</guid><dc:creator><![CDATA[Clawtocracy]]></dc:creator><pubDate>Mon, 16 Mar 2026 06:53:46 GMT</pubDate><enclosure url="https://api.substack.com/feed/podcast/191098996/cd354814834e5cd44ce5ca050af3b934.mp3" length="0" type="audio/mpeg"/><content:encoded><![CDATA[<p>A couple of weeks back (over on LinkedIn), I <a href="https://www.linkedin.com/posts/markfogle_openclaw-agenticai-hackathon-activity-7432052741851709440-NPQF?utm_source=share&amp;utm_medium=member_desktop&amp;rcm=ACoAAAAFvkEBUgHNsuV4Bm7411aGWplPm_FA3Qw">mentioned</a> the ClawG Mission Control project that Kush Ise and team used to win the Wordware OpenClaw Hack Night last month, using my clawg-ui plugin as part of the solution. This weekend, I contributed back, bringing the Mission Control project up to date with version v2026.3.2+ of  OpenClaw and adding in a couple of new feature in the process.<br><br>Mentioned in the video : </p><p><a href="https://kush2704.vercel.app/">Kush Ise</a><br>clawg-ui plugin (<a href="https://github.com/contextablemark/clawg-ui">Github</a>, <a href="https://www.npmjs.com/package/@contextableai/clawg-ui">npm</a>) <br><a href="https://github.com/contextablemark/Clawg--Mission-Control">Clawg&#8212;Mission-Control</a> (with my additions)<br></p>]]></content:encoded></item><item><title><![CDATA[From Detour to Redemption]]></title><description><![CDATA[An unnecessary (but educational) diversion brings us back to Graphiti]]></description><link>https://www.clawtocracy.ai/p/from-detour-to-redemption</link><guid isPermaLink="false">https://www.clawtocracy.ai/p/from-detour-to-redemption</guid><dc:creator><![CDATA[Clawtocracy]]></dc:creator><pubDate>Fri, 06 Mar 2026 14:03:52 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!i8Ba!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F077961b6-50f7-4830-b2fa-33df3f5ed88d_2760x1504.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!i8Ba!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F077961b6-50f7-4830-b2fa-33df3f5ed88d_2760x1504.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i8Ba!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F077961b6-50f7-4830-b2fa-33df3f5ed88d_2760x1504.png 424w, https://substackcdn.com/image/fetch/$s_!i8Ba!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F077961b6-50f7-4830-b2fa-33df3f5ed88d_2760x1504.png 848w, https://substackcdn.com/image/fetch/$s_!i8Ba!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F077961b6-50f7-4830-b2fa-33df3f5ed88d_2760x1504.png 1272w, https://substackcdn.com/image/fetch/$s_!i8Ba!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F077961b6-50f7-4830-b2fa-33df3f5ed88d_2760x1504.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!i8Ba!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F077961b6-50f7-4830-b2fa-33df3f5ed88d_2760x1504.png" width="1456" height="793" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/077961b6-50f7-4830-b2fa-33df3f5ed88d_2760x1504.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:793,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:7861679,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.clawtocracy.ai/i/190075380?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F077961b6-50f7-4830-b2fa-33df3f5ed88d_2760x1504.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!i8Ba!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F077961b6-50f7-4830-b2fa-33df3f5ed88d_2760x1504.png 424w, https://substackcdn.com/image/fetch/$s_!i8Ba!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F077961b6-50f7-4830-b2fa-33df3f5ed88d_2760x1504.png 848w, https://substackcdn.com/image/fetch/$s_!i8Ba!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F077961b6-50f7-4830-b2fa-33df3f5ed88d_2760x1504.png 1272w, https://substackcdn.com/image/fetch/$s_!i8Ba!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F077961b6-50f7-4830-b2fa-33df3f5ed88d_2760x1504.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>In this series, I&#8217;ll take you through some of the diversions I took along the way to implementing my OpenClaw memory replacement.  In this installment, after I abandoned Graphiti for Cognee and abandoned Cognee for a fundamental architectural mismatch, I returned to Graphiti with GPU embeddings, and monkey-patched my way to a useful solution. Here&#8217;s the the next chapter of the saga.</strong></p><p><strong>And you can see me talk about my experience&#8230; and the final result&#8230; at SpiceDB Community Day 2026! <a href="https://authzed.com/events/spicedb-community-day-2026">Register here</a>. </strong></p><h2><strong>The Starting Problem</strong></h2><p>In my <a href="https://www.clawtocracy.ai/p/building-memory-that-doesnt-lapse">previous post</a>, I chronicled breaking free from OpenAI&#8217;s API by switching to local Ollama models with Graphiti. That worked brilliantly-until Graphiti&#8217;s architecture redesign made local models impractical. Embedding calls per episode ballooned from ~40 to ~300, turning 60-90 second operations into 15+ minute ordeals on CPU.</p><p>Time for Plan B: <strong>Cognee</strong>.</p><p>Cognee (v0.5.2) looked promising-native Ollama support, built on LanceDB + KuzuDB + SQLite, simple REST API, active development. The plan was straightforward: swap out the Graphiti backend, point Cognee at the same GPU-accelerated Ollama servers, run the E2E tests, call it a day.</p><p><strong>Narrator:</strong> <em>It was not straightforward.</em></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.clawtocracy.ai/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe now&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.clawtocracy.ai/subscribe?"><span>Subscribe now</span></a></p><h2><strong>The Cognee Chapter</strong></h2><h3><strong>Challenge #1: The Authentication Wall</strong></h3><p>I configured Cognee, fired up the server, and immediately hit a wall:</p><pre><code><code>$ curl http://localhost:8000/api/v1/datasets
{"detail": "Not authenticated"}</code></code></pre><p>Every endpoint returned <code>401 Unauthorized</code>. After trying various permutations of API keys and headers, I realized: <strong>Cognee v0.5.0+ introduced multi-tenant access control</strong>. Great for production deployments, but fundamentally conflicting with our architecture - I use <strong>SpiceDB for ReBAC</strong> (Relationship-Based Access Control), not Cognee&#8217;s built-in system.</p><p>The documentation mentioned <code>ENABLE_BACKEND_ACCESS_CONTROL=false</code>, but that alone wasn&#8217;t enough. After digging through the source code, I found the missing piece: <code>REQUIRE_AUTHENTICATION=false</code>. Two separate environment variables control access - the docs only mentioned the first. Both must be set to run Cognee without authentication.</p><h3><strong>Challenge #2: The GPU Discovery</strong></h3><p>With authentication sorted, I noticed the <code>cognify</code> operation (entity extraction + embedding generation + knowledge graph construction) was taking 90-180 seconds. The bottleneck: embeddings running on <strong>CPU</strong> while the LLM ran on the <strong>GPU server</strong>.</p><p>This split wasn&#8217;t accidental - it was based on a reasonable-sounding assumption (i.e., &#8220;it&#8217;s only embeddings! They can run on the CPU!&#8221;) that turned out to be wrong. Embedding models are tiny compared to LLMs. A 137M-parameter <code>nomic-embed-text</code> next to a 14B-parameter <code>qwen2.5</code> is a rounding error. Why waste GPU memory on a model that barely needs it?</p><p>The problem is that &#8220;lightweight per call&#8221; and &#8220;lightweight in aggregate&#8221; are very different things. A single embedding takes milliseconds on CPU - negligible. But Cognee&#8217;s pipeline makes hundreds of calls per episode: chunking, entity extraction, graph edges, search vectors. At scale, those milliseconds compound into minutes. The model is light; the workload isn&#8217;t.</p><p>I moved both to the GPU server. Cognify dropped to <strong>17 seconds</strong>. A <strong>10x speedup</strong>.</p><p>This insight (i.e., that GPU embeddings are transformative, not incremental) would prove to be the most valuable discovery of the entire journey.</p><h3><strong>Challenge #3: The Deal-Breaker - Cross-Dataset Contamination</strong></h3><p>Everything seemed to be working. Then I stored data in separate groups and searched one group. <strong>I got results from both.</strong></p><p>With <code>ENABLE_BACKEND_ACCESS_CONTROL=false</code>, Cognee stores everything in a <strong>single global database</strong>. The <code>datasets</code> parameter in search is simply ignored. Dataset isolation is implemented through access control, not query filtering.</p><p>Could I re-enable access control but keep authentication disabled? No. Cognee&#8217;s code explicitly forces authentication on when access control is enabled:</p><pre><code><code>REQUIRE_AUTHENTICATION = (
    env.REQUIRE_AUTHENTICATION == "true"
) or (
    env.ENABLE_BACKEND_ACCESS_CONTROL == "true"
)</code></code></pre><p>You literally cannot have dataset isolation in this case without authentication. It&#8217;s hardcoded. The isolation is keyed on a <code>(owner_id, dataset_id)</code> tuple-without an authenticated user, there&#8217;s no <code>owner_id</code>, and without that, there&#8217;s no isolation.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.clawtocracy.ai/?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Share Clawtocracy&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.clawtocracy.ai/?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Share Clawtocracy</span></a></p><h3><strong>The Decision: Scrap Cognee</strong></h3><p>Cross-dataset contamination isn&#8217;t a bug - it&#8217;s the natural consequence of disabling a load-bearing architectural feature. Cognee&#8217;s dataset isolation is deeply integrated with its authentication system by design. If your authorization layer can&#8217;t enforce data boundaries, it doesn&#8217;t matter how fast your embeddings are.</p><p>So I removed Cognee&#8230; and reevaluated my whole approach. </p><p>The experience had forced a fundamental reckoning. The project had been called <strong>openclaw-memory-graphiti</strong> - the storage backend right there in the name. After discovering that backends can become incompatible overnight, baking one into the project&#8217;s identity seemed like a mistake I would like to avoid. The authorization model - SpiceDB&#8217;s ReBAC - was the stable commitment&#8230; but the solution could probably benefit from some choice when it came to memory backends.</p><p>So alongside the Cognee removal came a complete refactor: <strong>openclaw-memory-graphiti</strong> became <strong>openclaw-memory-rebac</strong>. Memory representations would be swappable plugins behind a common interface, with SpiceDB authorization gating access regardless of what sat behind it. The &#8220;rebac&#8221; is the part that could stay; the memory backend is the part that can change.</p><p><strong>And the GPU embedding insight survived.</strong> It pointed us back to Graphiti with a hypothesis.</p><h2><strong>The Graphiti Redemption</strong></h2><h3><strong>The Hypothesis</strong></h3><p>If GPU embeddings gave Cognee a 10x speedup, the math for Graphiti looked promising. The redesign increased embedding calls from ~40 to ~300 - the same volume problem that had exposed the CPU assumption in the first place:</p><p><strong>300 embeddings &#215; 0.3s per batch (GPU) &#8776; 90 seconds</strong></p><h3><strong>The Results</strong></h3><p>I configured both LLM and embeddings on the GPU server, ported the complex relationship extraction tests, and ran them.</p><p>Test ScenarioProcessing TimeFull memory lifecycle~58sMulti-entity professional~54sTemporal + work artifacts~87sMulti-turn tech conversation~60s</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\begin{array}{|l|c|}\n\\hline\n\\textbf{Scenario} &amp; \\textbf{Processing Time} \\\\\n\\hline\n\\text{Full memory lifecycle} &amp; \\sim 58\\text{s} \\\\\n\\text{Multi-entity professional} &amp; \\sim 54\\text{s} \\\\\n\\text{Temporal + work artifacts} &amp; \\sim 87\\text{s} \\\\\n\\text{Multi-turn tech conversation} &amp; \\sim 60\\text{s} \\\\\n\\hline\n\\end{array}&quot;,&quot;id&quot;:&quot;SBTOFGPQJY&quot;}" data-component-name="LatexBlockToDOM"></div><p><strong>Nothing over 90 seconds.</strong> The hypothesis held.  Still not blazingly fast, but bear in mind that these operations are running in the background, so the times are tolerable especially for a local model that isn&#8217;t burning through paid tokens.</p><p>The architectural complexity that made Graphiti &#8220;unusable&#8221; on CPU became lessof an issue with GPU acceleration-richer entity extraction, better relationship modeling, temporal understanding, now became possible in under 90 seconds.</p><h3><strong>Simplifying the Transport</strong></h3><p>The first change was architectural, not a patch. Graphiti ships with an MCP (Model Context Protocol) server, but MCP added session management overhead and SSE parsing complexity for what are fundamentally stateless operations - store a memory, search the graph, resolve a UUID. The Graphiti FastAPI server already exposes a clean REST API for all of this. Using it directly eliminated an entire class of transport-layer bugs and made debugging trivial.</p><h3><strong>The Patching</strong></h3><p>With the transport simplified, getting Graphiti to actually <em>work</em> with local models was like renovating a house built for a different climate - the foundation was sound, but the fixtures assumed conditions that no longer applied. Five runtime patches in a single <code>startup.py</code> file handled the adaptation.</p><p><strong>The constructor bypass.</strong> <code>ZepGraphiti.__init__</code> never forwards <code>embedder</code> or <code>cross_encoder</code> to the base class. Every embedding call silently hit OpenAI&#8217;s <code>text-embedding-3-small</code> regardless of configuration (and silently failed without an OpenAI API key). Fix: subclass <code>Graphiti</code> directly.</p><p><strong>Singleton client lifecycle.</strong> Upstream creates and closes a client per-request, but episode processing runs asynchronously and outlives the request scope. Fix: a process-lifetime singleton.</p><p><strong>Resilient AsyncWorker.</strong> Any exception in the background worker kills the loop silently - no logging, no recovery, jobs pile up forever. Fix: a catch-all handler that logs and continues.</p><p><strong>Attribute sanitization.</strong> Local LLMs return nested structures where OpenAI returns flat objects. Neo4j rejects non-primitive properties. Fix: flatten nested dicts/lists to strings for both nodes and edges.</p><p><strong>None index handling.</strong> Local LLMs sometimes return <code>None</code> where integers are expected. Fix: catch the TypeError instead of crashing the entire episode.</p><h3><strong>The Nature of These Patches</strong></h3><p>Let&#8217;s be honest about what I built. These are <strong>runtime monkey-patches</strong> applied via <code>importlib.import_module()</code> to a third-party codebase I don&#8217;t control. Every patch depends on upstream&#8217;s internal module structure. None of this is part of the public API. Any upstream release could break them silently.</p><p>But the alternative - maintaining a full fork of graphiti-core - is worse. The patches are concentrated in one file, well-documented, with clear comments explaining what they fix and why. <strong>The right amount of technical debt is the amount you can service.</strong> Five monkey-patches in one file? Serviceable. A full fork of a rapidly-evolving Python project? Not serviceable.</p><h2><strong>The Final Architecture</strong></h2><p><strong>openclaw-memory-rebac</strong> is a Graphiti backend with SpiceDB ReBAC authorization:</p><ul><li><p><strong>Storage</strong>: Graphiti FastAPI + Neo4j, accessed via REST (no MCP)</p></li><li><p><strong>Authorization</strong>: SpiceDB (relationship-based, per-fragment access control)</p></li><li><p><strong>Inference</strong>: Ollama (qwen2.5:14b LLM + nomic-embed-text embeddings, GPU-accelerated)</p></li><li><p><strong>Reranking</strong>: BGE (local sentence-transformers, CPU - some things still don&#8217;t need a GPU)</p></li></ul><h2><strong>Credit Where It&#8217;s Due: Cognee&#8217;s RBAC Is the Real Deal</strong></h2><p>This post chronicles why Cognee didn&#8217;t work <em>for us</em>. That distinction matters, because the authorization system I had to disable is genuinely well-engineered - and if your access control needs are role-based rather than relationship-based, Cognee deserves serious consideration, especially since Cognee already has a <a href="https://docs.cognee.ai/integrations/openclaw-integration">perfectly useful OpenClaw plugin</a>. </p><p>Think of authorization models as two different maps of the same territory. RBAC (Role-Based Access Control) maps organizational structure: departments, teams, job titles, reporting lines. ReBAC (Relationship-Based Access Control) maps social graphs: who shared what with whom, who authored what, who trusts whom. Both are valid projections - they just emphasize different features of the landscape.</p><h3><strong>Local Model Support: Cognee&#8217;s Quiet Advantage</strong></h3><p>Authorization aside, Cognee outperformed Graphiti convincingly in one other area: <strong>working with local models required zero patches.</strong></p><p>Getting Graphiti to function with Ollama required five monkey-patches in a custom Docker image - constructor bypass, singleton lifecycle, resilient worker, attribute sanitization, None handling - all because Graphiti&#8217;s internals assume OpenAI-shaped outputs at every layer.</p><p>Cognee? Set <code>LLM_PROVIDER=ollama</code>, point to the endpoint, start the server. It just worked. Entity extraction, embeddings, knowledge graph construction - all without reaching into the framework&#8217;s internals with a wrench.</p><p>Cognee was designed with provider flexibility as a first-class concern. Swap a provider string and an endpoint URL, and the rest adapts. Graphiti was designed OpenAI-first and treats alternative providers as an afterthought. Both are valid strategies, but they produce very different experiences when you show up with an Ollama server instead of an API key.</p><p>If you&#8217;re committed to local model inference - whether for cost, privacy, latency, or sovereignty reasons - Cognee removes an entire category of integration pain that I spent hours working around with Graphiti.</p><p><strong>If your requirements match these, start with Cognee:</strong></p><ul><li><p>Teams and departments need isolated knowledge bases</p></li><li><p>Access aligns with organizational hierarchy (managers see what their reports see)</p></li><li><p>You need dataset-level permissions without building your own auth layer</p></li><li><p>Compliance requires that data isolation is enforced at the storage level, not just the API level</p></li><li><p>You&#8217;re running local models via Ollama and want a framework that supports them natively</p></li></ul><p><strong>If needs look more like this, you&#8217;ll hit the same wall I did:</strong></p><ul><li><p>Access is based on social relationships (friends-of, shared-with, authored-by)</p></li><li><p>Permissions are dynamic and require graph traversal</p></li><li><p>You&#8217;re already running an external authorization system like SpiceDB</p></li><li><p>You need per-fragment access control within a single dataset</p></li></ul><p>The incompatibilities weren&#8217;t flaws in Cognee&#8217;s design. They were a mismatch between two well-reasoned but fundamentally different philosophies - and the coupling that makes Cognee&#8217;s RBAC robust is exactly what made it impossible to bypass for our ReBAC use case.</p><h2><strong>Lessons Learned</strong></h2><h3><strong>1. Exhaust Infrastructure Before Switching Frameworks</strong></h3><p>I skipped straight from &#8220;Graphiti is slow on CPU&#8221; to &#8220;let&#8217;s try a different framework&#8221; without questioning the assumption that embeddings were too lightweight to benefit from GPU acceleration. Profiling before pivoting would have saved two days.</p><h3><strong>2. Local Models Are a Different Beast</strong></h3><p>OpenAI&#8217;s API produces consistent, well-typed outputs. Local LLMs return nested dicts where you expect flat objects, <code>None</code> where you expect integers, lists where you expect strings. Any framework tested exclusively against OpenAI has latent bugs that only surface with local models.</p><h3><strong>3. &#8220;Disable Feature X&#8221; Often Means &#8220;Disable Features X, Y, and Z&#8221;</strong></h3><p>Disabling Cognee&#8217;s authentication removed dataset isolation, user identity, and the entire multi-tenant data model along with it. Features in well-designed systems are interconnected. Disabling one load-bearing feature can collapse the entire structure.</p><h3><strong>4. Test Data Isolation on Day One</strong></h3><p>When evaluating any backend that claims group/dataset/namespace isolation, test it explicitly:</p><pre><code><code>store("data-A", group: "group-a");
store("data-B", group: "group-b");
results = search("data-B", group: "group-a");
expect(results).not.toContain("data-B"); // FAILS with Cognee and authorization disabled
</code></code></pre><h3><strong>5. The Best Pivot Is Sometimes a U-Turn</strong></h3><p>I spent two days integrating Cognee, ripped it out, refactored the project identity, returned to Graphiti, and monkey-patched five internal behaviors. That looks like thrashing. But the GPU insight, the backend abstraction layer, and the local-model patches all came from the detour. <strong>Detours with learning are progress, not waste.</strong></p><h2><strong>The Documentation Gap</strong></h2><p>The biggest meta-takeaway: <strong>good documentation is a map that shows both the trails and the exits.</strong> Most frameworks document the happy path, not the &#8220;I brought my own compass&#8221; path. Budget time for discovering the undocumented interactions between features - especially when disabling one.</p><h2><strong>Resources</strong></h2><ul><li><p>openclaw-memory-rebac : <a href="https://github.com/contextablemark/openclaw-memory-rebac/releases/tag/v0.1.0">Github</a> and <a href="https://www.npmjs.com/package/@contextableai/openclaw-memory-rebac">npm</a></p></li><li><p><a href="https://github.com/topoteretes/cognee">Cognee</a> - Knowledge graph memory framework (recommended for standalone use)</p></li><li><p><a href="https://github.com/getzep/graphiti">Graphiti</a> - Knowledge graph with REST + MCP interfaces</p></li><li><p><a href="https://authzed.com/spicedb">SpiceDB</a> - Relationship-based access control</p></li><li><p><a href="https://ollama.ai/">Ollama</a> - Local LLM runtime with GPU acceleration</p></li></ul><h2><strong>Questions?</strong></h2><p>Have you hit similar architectural incompatibilities when layering authorization systems? Had to monkey-patch your way to production with local models? I&#8217;d love to hear about it in the comments.</p><h2><strong>What&#8217;s Next: Why Stop at One?</strong></h2><p>This entire series has treated memory backend selection as a high-stakes, one-way commitment. But that refactor to <code>openclaw-memory-rebac</code> created a backend abstraction layer for a reason. The SpiceDB authorization gate sits <em>in front of</em> the storage backend. The plugin interface doesn&#8217;t care what&#8217;s behind it.</p><p>Graphiti works. The patches are manageable. But the agentic memory space is rapidly evolving, and every backend brings different strengths - richer entity models, faster ingestion, better graph traversal, tighter local-model support. Now that swapping backends is a configuration change rather than a rewrite, the question shifts from &#8220;which one do we commit to forever?&#8221; to &#8220;which one fits the current workload best?&#8221;</p><p>Next post: <strong>treating the memory backend as a swappable component, and finding out what else is out there.</strong></p><div><hr></div><p><em>Kudos to the Cognee team for building genuinely solid software - particularly on the authorization front. The dataset isolation design is well-reasoned, the RBAC model is production-grade, and the GPU performance discovery during our integration was eye-opening. Our story is one of architectural mismatch, not software quality. Sometimes &#8220;not the right fit for this specific use case&#8221; is the reasonable - and most accurate - thing you can say about a well-engineered solution.</em></p>]]></content:encoded></item><item><title><![CDATA[Building Memory That Doesn't Lapse]]></title><description><![CDATA[Breaking Free from OpenAI and switching to local LLM infrastructure]]></description><link>https://www.clawtocracy.ai/p/building-memory-that-doesnt-lapse</link><guid isPermaLink="false">https://www.clawtocracy.ai/p/building-memory-that-doesnt-lapse</guid><dc:creator><![CDATA[Clawtocracy]]></dc:creator><pubDate>Thu, 26 Feb 2026 15:12:52 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!JfCv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb80acff-204b-4864-990e-64749ecc72ca_2816x1504.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!JfCv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb80acff-204b-4864-990e-64749ecc72ca_2816x1504.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!JfCv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb80acff-204b-4864-990e-64749ecc72ca_2816x1504.png 424w, https://substackcdn.com/image/fetch/$s_!JfCv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb80acff-204b-4864-990e-64749ecc72ca_2816x1504.png 848w, https://substackcdn.com/image/fetch/$s_!JfCv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb80acff-204b-4864-990e-64749ecc72ca_2816x1504.png 1272w, https://substackcdn.com/image/fetch/$s_!JfCv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb80acff-204b-4864-990e-64749ecc72ca_2816x1504.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!JfCv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb80acff-204b-4864-990e-64749ecc72ca_2816x1504.png" width="1456" height="778" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/eb80acff-204b-4864-990e-64749ecc72ca_2816x1504.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:778,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:8749096,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://www.clawtocracy.ai/i/188784853?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb80acff-204b-4864-990e-64749ecc72ca_2816x1504.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!JfCv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb80acff-204b-4864-990e-64749ecc72ca_2816x1504.png 424w, https://substackcdn.com/image/fetch/$s_!JfCv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb80acff-204b-4864-990e-64749ecc72ca_2816x1504.png 848w, https://substackcdn.com/image/fetch/$s_!JfCv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb80acff-204b-4864-990e-64749ecc72ca_2816x1504.png 1272w, https://substackcdn.com/image/fetch/$s_!JfCv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Feb80acff-204b-4864-990e-64749ecc72ca_2816x1504.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>The Crisis</strong></h3><p>It was a typical Thursday morning when I started noticing some anomalies in my OpenClaw agent&#8217;s memory system. My <a href="https://www.clawtocracy.ai/p/building-memory-that-knows-whos-asking">ReBAC memory persistence system</a>, which had been working perfectly, suddenly started throwing timeouts when ingesting memories at the end of each turn:</p><pre><code><code>[plugins] openclaw-memory-graphiti: deferred SpiceDB write failed for memory_store:
Error: Failed to resolve episode UUID for "memory_1770911378846" in group "main"
after 90s &#8212; episode not yet visible in get_episodes (Graphiti LLM processing may
still be running)
</code></code></pre><p>The culprit? An <strong><a href="https://status.openai.com/incidents/01KH94NGSXNH9H4WBPXB3RFZWX">OpenAI API outage</a></strong>. Both the ingestion portion of my memory system (i.e., the portion of the pipeline that uses embedding to create new memories) as well as recall (which uses embeddings to create the search query) had been rendered inoperable.  Clearly, I needed a backup (or perhaps a complete replacement) for times like this.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.clawtocracy.ai/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p><strong>And you can see me talk about my experience&#8230; and the final result&#8230; at SpiceDB Community Day 2026! <a href="https://authzed.com/events/spicedb-community-day-2026">Register here</a>.</strong></p><h3><strong>The Problem: Multiple External Dependencies</strong></h3><p>The Graphiti MCP server, upon which the ReBAC memory system relies, was using external models (by default Open AI) for several independent tasks including:</p><ol><li><p><strong>Entity Extraction</strong> - Using <code>gpt-4o-mini</code> to analyze conversations and extract structured knowledge (entities, relationships, facts)</p></li><li><p><strong>Embeddings</strong> - Using <code>text-embedding-3-small</code> to generate 1536-dimensional vectors for semantic search</p></li><li><p><strong>Search</strong> - Using <code>text-embedding-3-small</code> to create the search query and (potentially) <code>gpt-4.1-nano </code>inside the <code>OpenAIRerankerClient</code> to perform reranking.</p></li></ol><p>Using the default Graphiti configuration, when OpenAI models go down, one or more systems fail, depending upon the extent of the outage. But beyond the risks of creating a single point of failure by relying solely upon Open AI, I was paying for API calls that could easily run locally on hardware I already owned.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://www.clawtocracy.ai/p/building-memory-that-knows-whos-asking?r=6ajhld&quot;,&quot;text&quot;:&quot;Part I of the OpenClaw memory journey&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://www.clawtocracy.ai/p/building-memory-that-knows-whos-asking?r=6ajhld"><span>Part I of the OpenClaw memory journey</span></a></p><h3><strong>The Solution: Go Local with Ollama</strong></h3><p>I decided to replace all OpenAI dependencies with local models running on Ollama either GPU-accelerated or directly on the CPU:</p><ul><li><p><strong>Entity Extraction</strong>: <a href="https://ollama.com/library/nemotron-3-nano">nvidia/nemotron-3-nano:30b</a> via Ollama (GPU-accelerated)</p></li><li><p><strong>Embeddings</strong>: <a href="https://huggingface.co/nomic-ai/nomic-embed-text-v1.5">nomic-ai/nomic-embed-text-v1.5</a> via Ollama (CPU-only)</p></li><li><p><strong>Reranker</strong>: <a href="https://huggingface.co/BAAI/bge-reranker-v2-m3**">BAAI/bge-reranker-v2-m3</a> via the <code>sentence-transformers</code> library (CPU-only)</p></li></ul><p>Benefits:</p><ul><li><p>&#9989; No more external dependencies</p></li><li><p>&#9989; No API costs</p></li><li><p>&#9989; Complete data privacy</p></li><li><p>&#9989; Works even if internet connectivity is down</p></li></ul><h3><strong>Architecture Overview</strong></h3><pre><code><code>&#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
&#9474;                                  Graphiti Core                                      &#9474;
&#9474;  &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;   &#9474;
&#9474;  &#9474;   LLM Client            &#9474;   Embedder Client        &#9474;     Reranker Client     &#9474;   &#9474;
&#9474;  &#9474;   (Entity Extraction)   &#9474;   (Vector Generation)    &#9474;        (Search)         &#9474;   &#9474;
&#9474;  &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9524;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9524;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;   &#9474;
&#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
                 &#9474;                          &#9474;                        &#9474;
                 &#9660;                          &#9660;                        &#9660;
     &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;  &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488; &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
     &#9474;   Ollama Server #1    &#9474;  &#9474;   Ollama Server #2    &#9474; &#9474; sentence-transformers &#9474;
     &#9474;   :11434/v1           &#9474;  &#9474;   :11434/v1           &#9474; &#9474;                       &#9474;
     &#9474;   (DGX-Spark)         &#9474;  &#9474;   (OpenClaw machine)  &#9474; &#9474;   (OpenClaw machine)  &#9474;
     &#9474;                       &#9474;  &#9474;                       &#9474; &#9474;                       &#9474;
     &#9474;   nemotron-3-nano:30b &#9474;  &#9474;   nomic-embed-text    &#9474; &#9474;   bge-reranker-v2-m3  &#9474;
     &#9474;   (GPU-accelerated)   &#9474;  &#9474;   (CPU-only)          &#9474; &#9474;   (CPU-only)          &#9474;
     &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;  &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496; &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;
</code></code></pre><h3><strong>Implementation Journey</strong></h3><p>Looking at the Graphiti codebase, it initially seemed like all the pieces were there; I just needed to swap out the OpenAI dependencies for their Ollama (and BGE) alternatives.  But this plan quickly ran into a snag; not every configuration option available in the Graphiti core is directly available for external use.</p><h4><strong>Challenge #1: The Factory Pattern</strong></h4><p>Graphiti uses a factory pattern to create LLM clients. The existing code had:</p><ul><li><p><code>OpenAIClient</code> - Uses OpenAI&#8217;s proprietary <code>responses.parse()</code> API (structured outputs)</p></li><li><p><code>OpenAIGenericClient</code> - Uses standard OpenAI-compatible <code>chat.completions.create()</code> API</p></li></ul><p>The factory was creating <code>OpenAIClient</code> but wasn&#8217;t hooked up to <code>OpenAIGenericClient</code>. I needed to add a new provider case.</p><h4><strong>Challenge #2: Embedding Dimension Mismatch</strong></h4><p>OpenAI&#8217;s <code>text-embedding-3-small</code> generates <strong>1536-dimensional</strong> vectors, but <code>nomic-embed-text-v1.5</code> generates <strong>768-dimensional</strong> vectors.</p><p>Normally this would require a database migration. <strong>Lucky break</strong>: my FalkorDB instance was empty (zero episodes in all groups due to a previous misconfiguration), so I could just switch dimensions without any data migration.  This is something to bear in mind, however&#8230; if you change your embedding model, it will likely invalidate your embeddings database.</p><h3><strong>The Code Changes</strong></h3><h4><strong>1. Added </strong><code>openai_generic</code><strong> Provider to Factory</strong></h4><p><strong>File</strong>: <code>graphiti/mcp_server/src/services/factories.py</code></p><p>After the <code>case 'openai':</code> block, I added:</p><pre><code><code>case 'openai_generic':
    if not config.providers.openai:
        raise ValueError('OpenAI provider configuration not found')

    api_key = config.providers.openai.api_key or 'not-needed'
    api_url = config.providers.openai.api_url

    logger.info(f'Creating OpenAI Generic client (base_url: {api_url})')

    from graphiti_core.llm_client.openai_generic_client import OpenAIGenericClient
    from graphiti_core.llm_client.config import LLMConfig as CoreLLMConfig

    llm_config = CoreLLMConfig(
        api_key=api_key,
        base_url=api_url,
        model=config.model,
        temperature=config.temperature,
        max_tokens=config.max_tokens,
    )
    return OpenAIGenericClient(config=llm_config, max_tokens=config.max_tokens)
</code></code></pre><p><strong>Key insight</strong>: The <code>openai_generic</code> provider reuses the <code>config.providers.openai</code> section for credentials/URLs. This keeps the config schema clean.</p><h4><strong>2. Update Config for Local Endpoints</strong></h4><p><strong>File</strong>: <code>graphiti/mcp_server/config/config.yaml</code></p><p><strong>LLM section</strong> (entity extraction):</p><pre><code><code>llm:
  provider: "openai"  # Ollama is OpenAI-compatible
  model: "nemotron-3-nano:30b"
  max_tokens: 32768
  temperature: 0.0

  providers:
    openai:
      api_key: "not-needed"
      api_url: ${LLM_API_URL}
</code></code></pre><p><strong>Embedder section</strong> (vector generation):</p><pre><code><code>embedder:
  provider: "openai"  # Ollama is OpenAI-compatible
  model: "nomic-embed-text:v1.5"
  dimensions: 768  # Changed from 1536

  providers:
    openai:
      api_key: "not-needed"
      api_url: ${EMBEDDER_API_URL}
</code></code></pre><p><strong>Why </strong><code>provider: "openai"</code><strong>?</strong> Ollama implements the OpenAI-compatible API, so the existing <code>EmbedderFactory</code> works seamlessly with any OpenAI-compatible endpoint. No code changes needed!</p><h4><strong>3. Environment Variables</strong></h4><p><strong>File</strong>: <code>openclaw-memory-graphiti/.env</code></p><pre><code><code># Local LLM configuration (Ollama)
LLM_PROVIDER=openai_generic
LLM_MODEL=nemotron-3-nano:30b
LLM_API_URL=http://ollama-server:11434/v1
LLM_MAX_TOKENS=32768

# Local embedder configuration (Ollama)
EMBEDDER_MODEL=nomic-embed-text:v1.5
EMBEDDER_API_URL=http://localhost:11434/v1
EMBEDDER_DIMENSIONS=768
EMBEDDER_API_KEY=not-needed

# Local reranker
RERANKER_PROVIDER=bge</code></code></pre><h3><strong>Infrastructure Setup</strong></h3><h4><strong>Ollama Server #1: Entity Extraction (GPU-Accelerated)</strong></h4><pre><code><code># Run Ollama with GPU support
docker run -d \
  --name ollama-llm \
  --gpus all \
  -p 11434:11434 \
  -v ollama-llm-data:/root/.ollama \
  ollama/ollama:latest

# Pull the Nemotron model for entity extraction (~17GB)
docker exec ollama-llm ollama pull nemotron-3-nano:30b
</code></code></pre><p><strong>Why Nemotron-3-nano:30b?</strong></p><ul><li><p>Excellent instruction following for knowledge extraction</p></li><li><p>30B parameters strike a balance between quality and speed</p></li><li><p>GPU-accelerated for fast inference</p></li><li><p>OpenAI-compatible API endpoint</p></li></ul><h4><strong>Ollama Server #2: Embeddings (CPU-Only)</strong></h4><pre><code><code># Run Ollama in Docker (CPU-only, no GPU needed)
docker run -d \
  --name ollama-embeddings \
  -p 11434:11434 \
  -v ollama-embeddings-data:/root/.ollama \
  ollama/ollama:latest

# Pull the embedding model (~500MB)
docker exec ollama-embeddings ollama pull nomic-embed-text:v1.5</code></code></pre><p><strong>Why separate Ollama instances?</strong></p><ul><li><p><strong>Dedicated resources</strong>: Entity extraction gets GPU, embeddings run on CPU</p></li><li><p><strong>Independent scaling</strong>: Can run on different machines</p></li><li><p><strong>Isolation</strong>: Model loading/updates don&#8217;t affect each other</p></li></ul><p><strong>Why Ollama for (almost) everything?</strong></p><ul><li><p>Straightforward setup (one command per model)</p></li><li><p>OpenAI-compatible API (<code>/v1/chat/completions</code>, <code>/v1/embeddings</code>)</p></li><li><p>Automatic GPU detection and utilization</p></li><li><p>Built-in model management (<code>ollama pull</code>, <code>ollama list</code>)</p></li><li><p>Tiny memory footprint (~2GB RAM for embeddings)</p></li></ul><h3><strong>Final validation (or so I thought)</strong></h3><p>I did some final testing and submitted <a href="https://github.com/getzep/graphiti/pull/1227">a PR</a> to the Graphiti repo, receiving <a href="https://github.com/getzep/graphiti/pull/1227#pullrequestreview-3815453710">some feedback</a> in fairly short order.  I addressed the feedback, merged in some changes from main that had occurred in the interim and then&#8230; <strong>the wheels came off</strong>.</p><p>Suddenly, the automatic ingestion occurring at the end of each turn was consistently timing out again.  This time I knew that it couldn&#8217;t be a model outage, since everything was running locally.  So what had happened?</p><p>Checking through the changes I had pulled in from main, two PRs caught my eye : </p><ul><li><p>feat: simplify extraction pipeline and add batch entity summarization (<a href="https://github.com/getzep/graphiti/pull/1224">#1224</a>)</p></li><li><p>feat: driver operations architecture redesign (<a href="https://github.com/getzep/graphiti/pull/1232">#1232</a>)</p></li></ul><p>The net result? Ingesting a single episode went from 60-90 seconds (not great, but ingestion doesn&#8217;t need to be instantaneous) to <strong>more than 15 minutes</strong>. In particular, while the number of LLM calls necessary for entity extraction had roughly doubled (from ~15 to ~30), the number of embedding calls per episode had gone from ~40 to <strong>~300</strong>. Clearly something had changed, and not for the better; while a moderate performance hit when running local models is not unexpected, this was in another league entirely.  Perhaps local models were no longer viable for use with the newly rearchitected Graphiti? </p><p>This sent me back to the drawing board, leading me to another memory framework that had readily available examples making use of local models.  More on that experience in my next post.</p><p>In the meantime, this experience was not without its learnings&#8230; </p><h3><strong>Lessons Learned</strong></h3><ol><li><p><strong>OpenAI-Compatibility is Great</strong>: The OpenAI API specs have become the de facto standard whether dealing with chat completions or embeddings.</p></li><li><p><strong>Separate Your Concerns</strong>: Graphiti&#8217;s clean separation of LLM and embedder configs made it easy to swap them independently. I could have switched just one if needed.</p></li><li><p><strong>Switching embedding models is non-trivial</strong>: The embedding dimension change would have been painful with existing data.</p></li><li><p><strong>CPU Embeddings are Fine</strong>: Embeddings don&#8217;t need GPU. A cheap mini PC running Ollama is perfect&#8230; until the number of embedding calls increases drastically.</p></li><li><p><strong>Local models show promise</strong>: Nemotron Nano 3 is more than adequate for entity extraction</p></li></ol><h3><strong>Conclusion</strong></h3><p>Breaking free from OpenAI wasn&#8217;t just about avoiding outages&#8212;it was about taking control of my infrastructure. Ollama made this transition refreshingly smooth with its simple Docker-friendly setup and OpenAI-compatible API.</p><div><hr></div><h2><strong>What&#8217;s Next?</strong></h2><p>While I&#8217;m still keen on SpiceDB for it&#8217;s ReBAC functionality, the rearchitecting of Graphiti appears to have rendered it unusable as a backend memory store using local models for entity extraction and embeddings.  Looks like it&#8217;s time to explore alternative memory architectures&#8230;</p><div><hr></div><h3><strong>Resources</strong></h3><ul><li><p><a href="https://github.com/getzep/graphiti">Graphiti</a> - Knowledge graph memory for LLMs</p></li><li><p><a href="https://ollama.ai/">Ollama</a> - Run LLMs locally with ease</p></li><li><p><a href="https://ollama.com/library/nemotron-3-nano">Nemotron 3 Nano</a> - Fully capable for entity extraction</p></li><li><p><a href="https://huggingface.co/nomic-ai/nomic-embed-text-v1.5">Nomic Embed</a> - Efficient open embedding model, can run on CPU</p></li></ul><h3><strong>Questions?</strong></h3><p>If you try this approach, I&#8217;d love to hear about it! What models are you running locally? Were you able to get past the performance barrier that I ran into?</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://www.clawtocracy.ai/subscribe?&quot;,&quot;text&quot;:&quot;Subscribe&quot;,&quot;language&quot;:&quot;en&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Thanks for reading! Subscribe for free to receive new posts and support my work.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Type your email&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Subscribe"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div>]]></content:encoded></item><item><title><![CDATA[Building Memory That Knows Who’s Asking]]></title><description><![CDATA[ReBAC-Gated Knowledge Graphs for Agents]]></description><link>https://www.clawtocracy.ai/p/building-memory-that-knows-whos-asking</link><guid isPermaLink="false">https://www.clawtocracy.ai/p/building-memory-that-knows-whos-asking</guid><dc:creator><![CDATA[Clawtocracy]]></dc:creator><pubDate>Mon, 09 Feb 2026 06:37:08 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!91Tv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f632ca1-d2a8-4bb6-b44d-38f21339c4c4_1248x832.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!91Tv!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f632ca1-d2a8-4bb6-b44d-38f21339c4c4_1248x832.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!91Tv!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f632ca1-d2a8-4bb6-b44d-38f21339c4c4_1248x832.png 424w, https://substackcdn.com/image/fetch/$s_!91Tv!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f632ca1-d2a8-4bb6-b44d-38f21339c4c4_1248x832.png 848w, https://substackcdn.com/image/fetch/$s_!91Tv!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f632ca1-d2a8-4bb6-b44d-38f21339c4c4_1248x832.png 1272w, https://substackcdn.com/image/fetch/$s_!91Tv!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f632ca1-d2a8-4bb6-b44d-38f21339c4c4_1248x832.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!91Tv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f632ca1-d2a8-4bb6-b44d-38f21339c4c4_1248x832.png" width="1248" height="832" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3f632ca1-d2a8-4bb6-b44d-38f21339c4c4_1248x832.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:832,&quot;width&quot;:1248,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:2265004,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://clawtocracy.substack.com/i/187360985?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f632ca1-d2a8-4bb6-b44d-38f21339c4c4_1248x832.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!91Tv!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f632ca1-d2a8-4bb6-b44d-38f21339c4c4_1248x832.png 424w, https://substackcdn.com/image/fetch/$s_!91Tv!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f632ca1-d2a8-4bb6-b44d-38f21339c4c4_1248x832.png 848w, https://substackcdn.com/image/fetch/$s_!91Tv!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f632ca1-d2a8-4bb6-b44d-38f21339c4c4_1248x832.png 1272w, https://substackcdn.com/image/fetch/$s_!91Tv!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3f632ca1-d2a8-4bb6-b44d-38f21339c4c4_1248x832.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image"><svg role="img" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg"><g><title></title><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>TL;DR:</strong> Most agentic memory systems treat access control as an afterthought. This post describes a framework that combines temporal knowledge graphs with relationship-based access control so that authorization is structural and deterministic and not subject to the whims of LLMs. The pattern is portable to any agent framework - I implemented it for OpenClaw, but the architecture applies anywhere agents need to remember facts on behalf of multiple people.</p><div><hr></div><h2>The Challenge with Agentic Memory Today</h2><p>Most agentic memory systems I&#8217;ve encountered up to this point treat access control as an afterthought - or ignore it entirely.</p><p>The default pattern goes something like this: the agent stores memories in a vector database and, when it needs to recall something, it searches everything it has access to and then applies a filter. If you want to restrict what the agent can see in a given context, you add a system prompt: <em>&#8220;Don&#8217;t mention anything about the surprise party to Dad.&#8221;</em></p><p>This is security through politeness.</p><p>Prompt-based filtering is unreliable by nature. It&#8217;s subject to prompt injection. It fails under context pressure. And it fundamentally relies on the model <em>choosing</em> to comply - which is exactly the wrong trust boundary for anything sensitive.</p><p>We wouldn&#8217;t build a file system where permissions are enforced by asking users nicely not to open certain folders. Why would we build agentic memory that way?</p><div><hr></div><h2>The Core Idea: Two Graphs, One Query Path</h2><p>The solution decomposes agentic memory into two complementary problems:</p><p><strong>What does the agent know?</strong> This is the job of a temporal knowledge graph. Unlike flat vector stores, a knowledge graph extracts entities and facts from conversations and maintains them as structured, evolving knowledge: <em>&#8220;Mark prefers dark mode,&#8221; &#8220;Mark is working on the AG-UI protocol,&#8221; &#8220;Dad&#8217;s birthday is March 15th.&#8221;</em> Facts can be superseded, relationships can change, and the graph reflects that history.</p><p><strong>Who&#8217;s allowed to know it?</strong> This is the job of a relationship-based access control (ReBAC) system. Inspired by Google&#8217;s Zanzibar, ReBAC evaluates permissions based on a graph of relationships - not static role lists or flat ACL tables. Access is structural: it follows from how entities relate to each other.</p><p>The query path composes them:</p><pre><code><code>Agent Turn &#8594; ReBAC (who can see what?) &#8594; Knowledge Graph (search the authorized subset) &#8594; Context
</code></code></pre><p>The agent doesn&#8217;t decide what to filter. The authorization layer decides what exists.</p><div><hr></div><h2>The &#8220;Mark, Mom, and Dad &#8221; Problem</h2><p>Here&#8217;s the scenario that motivated the design.</p><p>Imagine a family assistant agent that interacts with three people: Mark, Mom, and Dad. Each person has private memories the agent should know about but never cross-pollinate:</p><ul><li><p><strong>Mark&#8217;s private group:</strong> Work preferences, projects, personal notes</p></li><li><p><strong>Mom&#8217;s private group:</strong> Schedule, health notes, conversations with the agent</p></li><li><p><strong>Dad&#8217;s private group:</strong> Schedule, plans for fishing trip with former colleagues</p></li></ul><p>Then there are shared groups:</p><ul><li><p><strong>Family group:</strong> Shared calendar, recipes, vacation plans - everyone can access</p></li><li><p><strong>Mom &amp; Dad group:</strong> Parenting decisions, financial discussions - Mark can&#8217;t see these</p></li><li><p><strong>Mark &amp; Mom group:</strong> Planning surprise party for Dad - Dad shouldn&#8217;t see this</p></li><li><p><strong>Mark &amp; Dad group: </strong>Gift ideas for Mom  - Mom shouldn&#8217;t see this</p></li></ul><p>When Dad asks the agent <em>&#8220;Is anyone secretly planning a party for my birthday?&#8221;</em>, the system searches his private group, the family group, the Mark &amp; Dad group and the Mom &amp; Dad group. It does <strong>not</strong> search the Mark &amp; Mom group (aka, the &#8220;everyone but Dad&#8221; group) - where the surprise party plans live, so it comes up blank.</p><p>Not because we told it not to : because the permission check returns that group as unauthorized, and the search never executes against it.</p><p>When Mark asks the same question, he gets his private group , the family group, the Mark &amp; Dad group and the Mark &amp; Mom group (where the party plans live). Different person, different relationships, different memories&#8230; enforced structurally.</p><div><hr></div><h2>The Portable Architecture</h2><p>While I built this as an OpenClaw plugin, the architecture is framework-agnostic. Any agentic system that supports custom memory backends can implement the same pattern using two open-source building blocks.</p><h3>The Knowledge Layer: Graphiti</h3><p><a href="https://github.com/getzep/graphiti">Graphiti</a> is Zep&#8217;s open-source temporal knowledge graph. It extracts entities and facts from conversational episodes, maintains them in a graph (backed by <a href="https://www.falkordb.com/">FalkorDB</a>), and supports semantic search with temporal awareness. It&#8217;s doing the heavy lifting that a flat vector store can&#8217;t: structured knowledge that evolves over time.</p><p>But Graphiti (like most every knowledge graph and vector store I&#8217;ve evaluated) does not provide fine-grained, per-user authorization over individual memories. It assumes that anything within a given Graphiti namespace (e.g., group_id / tenant graph) is readable to whoever has API access to that namespace, so you must enforce auth and filtering in a separate layer. Everything stored is searchable by anyone with access to the API.</p><h3>The Authorization Layer: spicedb</h3><p><a href="https://github.com/authzed/spicedb">Spicedb</a> is an open-source implementation of <a href="https://research.google/pubs/zanzibar-googles-consistent-global-authorization-system/">Google&#8217;s Zanzibar</a> - the authorization system behind Google Drive, Photos, YouTube, and most of Google&#8217;s product suite. (AuthZed also maintains an excellent <a href="https://authzed.com/zanzibar">annotated version of the paper</a> if you want the highlights without reading all 14 pages.)</p><p>The core idea behind Zanzibar is <strong>Relationship-Based Access Control (ReBAC)</strong>: instead of assigning permissions through static roles (RBAC) or attribute rules (ABAC), access is determined by whether a chain of relationships exists between a subject and a resource. If Alice is a <em>member</em> of a group, and that group <em>owns</em> a document, Alice can access the document - not because someone added her to a role, but because the relationship graph connects her to it. This makes ReBAC particularly well-suited to agentic memory, where the relationships between people, conversations, and knowledge are the natural way to express who should see what.</p><p>Spicedb evaluates these relationship graphs using a declarative schema language called Zed.</p><p>Here&#8217;s the schema:</p><pre><code><code>definition memory_group {
    relation member: agent | person
    relation contributor: agent | person
    permission access = member
    permission contribute = contributor + member
}

definition memory_fragment {
    relation group: memory_group
    relation creator: agent | person
    permission view = group-&gt;access
    permission delete = creator
}
</code></code></pre><p>Every memory fragment belongs to a group. You can only view fragments in groups you&#8217;re a member of. You can only delete fragments you created. The agent never even sees unauthorized memories - there&#8217;s nothing to leak, inject around, or socially engineer.</p><h3>Composing Them</h3><p>The integration point is a fan-out search that gates knowledge graph queries behind permission checks:</p><pre><code><code>// Framework-agnostic pattern: fan-out search across authorized groups
const authorizedGroups = await spicedb.lookupResources("access", subject);
const results = await Promise.all(
  authorizedGroups.map(groupId =&gt; graphiti.search(query, { group_id: groupId }))
);
</code></code></pre><p>This pattern doesn&#8217;t depend on OpenClaw. If you&#8217;re building on LangChain, CrewAI, Google ADK, or a custom agent loop, the same composition applies: check permissions first, then search only the authorized partition of the knowledge graph.</p><div><hr></div><h2>From Families to Organizations</h2><p>The same pattern scales to any context where knowledge should be shared along relationship lines. Here are two concrete scenarios.</p><h3>Slack History Ingestion</h3><p>An organization&#8217;s Slack archive is a treasure trove of institutional knowledge -decisions made, problems solved, context shared. But not all of it should be accessible to everyone. With ReBAC-gated memory:</p><ul><li><p>Public channel history &#8594; shared group for all channel members</p></li><li><p>Private channels &#8594; group limited to channel membership</p></li><li><p>DMs &#8594; group limited to the two participants</p></li></ul><p>When an employee asks the agent a question, it searches only the channels and conversations they&#8217;re a member of. The knowledge graph captures relationships between concepts discussed <em>across</em> channels, while the authorization layer ensures each person only traverses the portion of the graph they have legitimate access to.</p><h3>Meeting Transcripts</h3><p>We can transcribe company-wide all-hands and town halls into a corporate-memory group everyone belongs to. Leadership meetings go into a leadership group. Department standups go into department groups. The agent builds a temporal knowledge graph of organizational decisions, priorities, and context - and each person gets the view that matches their actual organizational relationships.</p><p>When Bob asks <em>&#8220;What was the decision on the API migration?&#8221;</em>, the agent searches the engineering group where that discussion happened. When Alice asks - and she&#8217;s on the leadership team - she gets both the engineering discussion and the leadership context around why the migration was prioritized.</p><div><hr></div><h2>My Implementation: The OpenClaw Plugin</h2><p>I built this as an OpenClaw plugin (<code>@contextableai/openclaw-memory-graphiti</code>) because OpenClaw&#8217;s replaceable memory slot made it the ideal proving ground - one plugin controls the entire memory pipeline: storage, recall, and capture. There&#8217;s no risk of the default memory system leaking around the authorization layer.</p><p>The plugin provides:</p><ul><li><p><strong>memory_recall</strong> - search the knowledge graph across all authorized groups, with session/long-term/all scoping</p></li><li><p><strong>memory_store</strong> - save memories with automatic entity and fact extraction via Graphiti</p></li><li><p><strong>memory_forget</strong> - delete memories (creator-only, enforced by spicedb)</p></li><li><p><strong>Auto-capture</strong> - after every agent turn, key information is automatically extracted into the graph</p></li><li><p><strong>Auto-recall</strong> - before every agent turn, relevant memories are automatically injected into context</p></li><li><p><strong>Session isolation</strong> - each conversation gets its own memory group with exclusive ownership</p></li></ul><p>The infrastructure runs on FalkorDB, spicedb, and PostgreSQL (as a backing store for spicedb). There&#8217;s a Docker Compose stack for easy deployment.</p><p>But the plugin is just one <em>implementation</em>. The architecture - ReBAC-gated knowledge graphs - is the transferable idea. If you&#8217;re building agentic memory on a different framework, the composition of any temporal knowledge graph + any Zanzibar-inspired authorization system gives you the same properties.</p><div><hr></div><h2>What I Learned</h2><p><strong>Authorization is an infrastructure problem, not a prompt problem.</strong> The moment you try to enforce access control at the prompt level, you&#8217;ve already lost. The model might comply 99% of the time, but the 1% failure mode is unintended information disclosure - the worst kind of failure to have be probabilistic.</p><p><strong>Knowledge graphs and authorization graphs are natural complements.</strong> A knowledge graph builds a graph of <em>what the agent knows</em>. An authorization system builds a graph of <em>who can know what</em>. Composing them is more natural than bolting ACLs onto a vector store, because both systems already think in terms of entities and relationships.</p><p><strong>The gap is real.</strong> Before building this for OpenClaw, I surveyed the landscape: Mem0 has a polished plugin but no authorization model. Cognee augments memory with graph retrieval but doesn&#8217;t address access control. Spicedb has an <a href="https://authzed.com/docs/spicedb/integrations/pinecone">excellent RAG authorization tutorial</a> for Pinecone, but nothing targeting temporal knowledge graphs specifically. The combination of temporal knowledge graphs with ReBAC authorization didn&#8217;t exist as a packaged solution.</p><p><strong>The pattern is more general than the implementation.</strong> I built this for OpenClaw, but every design decision - the group-based memory partitioning, the fan-out search pattern, the schema separating membership from creatorship - applies to any agent framework. If you have a different knowledge store and a different auth system, the architecture translates directly.</p><div><hr></div><h2>What&#8217;s Next</h2><p>The immediate roadmap: smarter incremental imports (currently it reimports everything), bulk ingestion for external sources (Slack exports, meeting transcripts, document repositories), and exploring spicedb&#8217;s caveated relationships for time-limited memory sharing - <em>&#8220;share this memory group until the project ships.&#8221;</em></p><p>Longer term, I&#8217;m interested in inter-agent memory: multiple specialized agents with overlapping but distinct views of organizational knowledge, governed by the same authorization graph that governs human access.</p><p>If you made it to the end, you must really be interested : the code is at <a href="https://github.com/Contextable/openclaw-memory-graphiti">github.com/Contextable/openclaw-memory-graphiti</a>. MIT licensed. PRs welcome.</p>]]></content:encoded></item></channel></rss>