<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Hubert Larose Surprenant</title>
    <description>The latest articles on DEV Community by Hubert Larose Surprenant (@hubert_larosesurprenant_).</description>
    <link>https://dev.to/hubert_larosesurprenant_</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2273558%2F4a7c85cd-647d-4c98-86cb-6e83ac32254a.jpg</url>
      <title>DEV Community: Hubert Larose Surprenant</title>
      <link>https://dev.to/hubert_larosesurprenant_</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hubert_larosesurprenant_"/>
    <language>en</language>
    <item>
      <title>Memorify.dev unified your AI memory in one space. Stop having multiples vault that your going to forget.</title>
      <dc:creator>Hubert Larose Surprenant</dc:creator>
      <pubDate>Mon, 28 Sep 2026 11:34:50 +0000</pubDate>
      <link>https://dev.to/hubert_larosesurprenant_/memorifydev-unified-your-ai-memory-stop-having-multiples-vault-that-your-going-to-forget-5e1h</link>
      <guid>https://dev.to/hubert_larosesurprenant_/memorifydev-unified-your-ai-memory-stop-having-multiples-vault-that-your-going-to-forget-5e1h</guid>
      <description></description>
    </item>
    <item>
      <title>The Electron Illusion: Why Your "Desktop AI Agent" Is Just a Sandboxed CLI in a Tuxedo</title>
      <dc:creator>Hubert Larose Surprenant</dc:creator>
      <pubDate>Thu, 24 Sep 2026 12:17:57 +0000</pubDate>
      <link>https://dev.to/hubert_larosesurprenant_/the-electron-illusion-why-your-desktop-ai-agent-is-just-a-sandboxed-cli-in-a-tuxedo-26f7</link>
      <guid>https://dev.to/hubert_larosesurprenant_/the-electron-illusion-why-your-desktop-ai-agent-is-just-a-sandboxed-cli-in-a-tuxedo-26f7</guid>
      <description>&lt;p&gt;The Electron Illusion: Why Your "Desktop AI Agent" Is Just a Sandboxed CLI in a Tuxedo&lt;br&gt;
Open any modern "autonomous desktop agent" app, and the presentation is seductive. You get buttery-smooth 60 FPS animations, clean telemetry dashboards, status pills that pulse between Thinking, Analyzing, and Executing, and rich markdown previews. Marketing pages call them "native autonomous co-workers."&lt;br&gt;
Underneath that slick UI wrapper, the reality is far less glamorous: your desktop AI agent is almost certainly a headless CLI process trapped inside an isolated sandbox, having its stdout parsed line-by-line into chat bubbles.&lt;br&gt;
Despite sitting on your host operating system, the agent is functionally deaf, dumb, and blind to your machine. Here is the architectural reality of modern desktop agent apps, why they are structurally blind, and how you can prove it on your own workstation in under two minutes.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The Architecture of the "Desktop Agent"
Most desktop AI tools (built on Electron, Tauri, or Flutter) do not integrate with the operating system at the OS or kernel layer. Instead, they operate as a three-tier Russian doll:
┌────────────────────────────────────────────────────────┐
│  Tier 1: The Presentation Layer (Electron / Tauri)      │
│  - React/Svelte UI                                     │
│  - Streams JSON-RPC / SSE / WebSockets                 │
└──────────────────────────┬─────────────────────────────┘
                       │ IPC / Subprocess pipe
┌──────────────────────────▼─────────────────────────────┐
│  Tier 2: The Orchestration Runner (Node/Rust Backend)   │
│  - Spawns CLI binary or runs an isolated agent loop    │
│  - Pipes stdin / intercepts stdout &amp;amp; stderr            │
└──────────────────────────┬─────────────────────────────┘
                       │ Execution boundary
┌──────────────────────────▼─────────────────────────────┐
│  Tier 3: The Execution Sandbox (Docker / gVisor / Wasm) │
│  - Ephemeral environment                               │
│  - Mocked paths, stripped env vars, no display server   │
│  - Blind to desktop state, active windows, IPC bus     │
└────────────────────────────────────────────────────────┘&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When an agent claims it is "inspecting your system," it is rarely reading the native OS event loop, subscribing to D-Bus or the Windows message pump, or querying the desktop compositor.&lt;br&gt;
It is issuing standard POSIX commands (ls, ps, cat, grep) through a synthetic shell runner, waiting for text output, and regurgitating formatted markdown through an IPC bridge.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Three Ways to Prove the Agent is Sandboxed and Blind
You don't need access to proprietary source code to prove this. You can demonstrate the sandbox boundary using standard debugging utilities.
Proof A: Inspect the Process Hierarchy
When an agent claims to execute an action "natively" on your machine, inspect the process tree while it is running.
On Linux / macOS:
# Watch process creation in real time
pgrep -f "YourAgentApp" | xargs -I {} pstree -p {}
# Or trace process spawning
ps -ef --forest | grep -iE "(agent|docker|containerd|spawn)"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;What you will actually see:&lt;br&gt;
Instead of your desktop app interacting via native OS subsystem APIs, you will see the renderer process invoking an internal orchestrator, which invokes a subprocess like:&lt;br&gt;
Electron (PID 10420)&lt;br&gt;
 └── AgentBackend (PID 10455)&lt;br&gt;
      └── /bin/sh -c "python3 -u agent_runner.py --json" (PID 10501)&lt;br&gt;
           └── docker exec -i agent-sandbox-f82c /bin/bash (PID 10530)&lt;/p&gt;

&lt;p&gt;The agent is not running inside your desktop environment. It is running inside an isolated execution container or an ephemeral bash fork that knows nothing about its parent app beyond the file descriptor connected to stdin/stdout.&lt;br&gt;
Proof B: The Display Server and Window Context Blackout&lt;br&gt;
If an agent were truly integrated into your desktop, it would have native awareness of active display sessions, window focus, and accessibility trees.&lt;br&gt;
Ask the agent:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What application is currently focused on my secondary monitor, and what text is highlighted in it?"&lt;/p&gt;

&lt;p&gt;Unless the agent utilizes a specialized accessibility hook or an expensive computer-vision loop that takes periodic OS screenshots via OS APIs, it fails completely.&lt;br&gt;
Even if you give it full terminal access, run this through its prompt:&lt;/p&gt;
&lt;h1&gt;
  
  
  In Linux (X11 / Wayland)
&lt;/h1&gt;

&lt;p&gt;xdotool getactivewindow getwindowname || swaymsg -t get_tree&lt;/p&gt;
&lt;h1&gt;
  
  
  In macOS
&lt;/h1&gt;

&lt;p&gt;osascript -e 'tell application "System Events" to get name of first application process whose frontmost is true'&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In almost every "sandboxed desktop agent":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The command will fail immediately: The sandbox lacks the environment variables required to speak to your display server (e.g., DISPLAY, WAYLAND_DISPLAY, or macOS Accessibility TCC permissions).&lt;/li&gt;
&lt;li&gt;The environment is headless: The execution runner returns errors like cannot open display or execution denied by container policy.
The agent isn't sitting on your desktop; it is trapped in a dark, headless server rack that happens to be hosted inside your RAM.
Proof C: The stdout Parsing Artifact Test
Because the UI is decoupled from the execution core via standard text streams, the agent's "understanding" is constrained by string parsing.
Run this simple experiment in your agent:
&amp;gt; "Run a script that prints 5,000 rapid status updates with ANSI escape codes and progress bars, then print 'SUCCESS'."
&amp;gt; 
Watch what happens:&lt;/li&gt;
&lt;li&gt;The slick UI will freeze, stutter, or choke trying to parse the raw stream into structured React DOM elements.&lt;/li&gt;
&lt;li&gt;If a process prints to stderr instead of stdout, the agent often hallucinates that the entire task failed, even if the exit code was 0.&lt;/li&gt;
&lt;li&gt;The agent cannot dynamically alter execution based on real-time visual feedback on screen; it is purely reading the exit code and text stream buffered by the runner.

&lt;ol&gt;
&lt;li&gt;The "Blindness" Spectrum: Native Agent vs. Sandboxed CLI
| Architectural Dimension | "Slick Desktop" Wrapper (Status Quo) | True System-Native Agent |
|---|---|---|
| Execution Context | Sandboxed container / headless subshell | Native user space daemon with OS permissions |
| Environmental Telemetry | Isolated shell output (stdout/stderr) | OS event hooks, Accessibility APIs, IPC buses |
| State Persistence | Ephemeral; destroyed on session reset | Shared OS state, filesystem awareness, local DBs |
| Communication Pipeline | JSON-RPC over stdin/stdout or WebSockets | Native IPC (D-Bus, Mach ports, Win32 named pipes) |
| Sensory Input | Prompt text + manually piped files | Active window state, focused context, system events |&lt;/li&gt;
&lt;li&gt;The Security Paradox: Why Vendors Keep Agents Blind
Vendors don't build them this way out of laziness. They do it because of The Security Paradox of AI Agency:&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Unrestricted System Agency is an RCE Disaster: If an LLM agent has direct, un-sandboxed access to your host shell, window server, and file system, an indirect prompt injection (e.g., reading a malicious README or web page) can easily execute rm -rf ~, exfiltrate SSH keys, or install persistent rootkits.&lt;/li&gt;
&lt;li&gt;Sandboxing is the Only Safe Defense: To protect the host machine, developers must cage the agent inside gVisor, Docker, or locked-down subshells with restricted privileges.&lt;/li&gt;
&lt;li&gt;The UX Compromise: The developer wraps the cage in an Electron or Tauri window, adds nice typography, paints a faux-terminal output component, and markets it as an integrated desktop experience.
The result is a fundamental contradiction: We want agents that can run our operating systems, but we dare not give them access to the operating system.

&lt;ol&gt;
&lt;li&gt;Moving Past the Fancy Facade
Slick UI wrappers that merely capture text output from an isolated subprocess have hit a functional ceiling. Making an agent genuinely intelligent on a desktop requires bridging the sensory gap safely, rather than papering over it with frontend polish:&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Structured System Protocols (like MCP): Standardizing how agents query specific native capabilities via explicit client-server contracts, rather than dumping unstructured bash scripts into an unprivileged shell.&lt;/li&gt;
&lt;li&gt;Granular Permission Handshakes: Moving away from binary "all-or-nothing" root access toward fine-grained, policy-driven capabilities (e.g., granting read access to a specific window title without granting full screen capture).&lt;/li&gt;
&lt;li&gt;Stateful Context Layers: Giving agents persistent local memory of project structures and OS states across runs, rather than forcing them to start cold with a blank terminal buffer on every prompt.
Until our agent architectures shift from headless CLI wrappers with nice styling to protocol-driven, natively grounded background daemons, our "autonomous desktop agents" remain what they have always been: text predictors typing into a dark, locked container.
Have you inspected the process tree of your favorite "AI desktop" client? What workarounds or architectural patterns have you seen bridge this gap without compromising host security? Let's discuss in the comments.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>architecture</category>
      <category>security</category>
    </item>
    <item>
      <title>https://github.com/hlsitechio/electron-shell-framework 

New interesting release coming soon with a full Electron app editor !</title>
      <dc:creator>Hubert Larose Surprenant</dc:creator>
      <pubDate>Wed, 23 Sep 2026 22:09:26 +0000</pubDate>
      <link>https://dev.to/hubert_larosesurprenant_/httpsgithubcomhlsitechioelectron-shell-framework-new-interesting-release-coming-soon-with-487m</link>
      <guid>https://dev.to/hubert_larosesurprenant_/httpsgithubcomhlsitechioelectron-shell-framework-new-interesting-release-coming-soon-with-487m</guid>
      <description>&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://github.com/hlsitechio/electron-shell-framework" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fopengraph.githubassets.com%2Fbb9d4d3f53f1fbecfef4856893501bb13327f63772144a5c9edfb9c3e8bd8077%2Fhlsitechio%2Felectron-shell-framework" height="600" class="m-0" width="1200"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://github.com/hlsitechio/electron-shell-framework" rel="noopener noreferrer" class="c-link"&gt;
            GitHub - hlsitechio/electron-shell-framework: Reusable Electron app-shell framework: page registry, merged top bar, collapsible sidebars, dark/light theming, encrypted config, auto-update, IRM install CLI · GitHub
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            Reusable Electron app-shell framework: page registry, merged top bar, collapsible sidebars, dark/light theming, encrypted config, auto-update, IRM install CLI - hlsitechio/electron-shell-framework
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.githubassets.com%2Ffavicons%2Ffavicon.svg" width="32" height="32"&gt;
          github.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
    </item>
    <item>
      <title>I stopped rewriting the same Electron boilerplate — so I packaged it</title>
      <dc:creator>Hubert Larose Surprenant</dc:creator>
      <pubDate>Mon, 21 Sep 2026 15:28:44 +0000</pubDate>
      <link>https://dev.to/hubert_larosesurprenant_/i-stopped-rewriting-the-same-electron-boilerplate-so-i-packaged-it-3i8n</link>
      <guid>https://dev.to/hubert_larosesurprenant_/i-stopped-rewriting-the-same-electron-boilerplate-so-i-packaged-it-3i8n</guid>
      <description>&lt;p&gt;Every Electron side project starts with the same three weeks.&lt;/p&gt;

&lt;p&gt;You wire up window controls, and they look wrong next to the OS chrome. You build a theme system and half your components don't follow it. You store the user's API key in a plaintext JSON file because you'll "encrypt it later". And the auto-updater — the thing your users actually touch — gets bolted on in a panic the day before release.&lt;/p&gt;

&lt;p&gt;Then the app you meant to build gets whatever time is left.&lt;/p&gt;

&lt;p&gt;I got tired of writing that same shell. So I turned it into a framework: &lt;strong&gt;&lt;a href="https://github.com/hlsitechio/electron-shell-framework" rel="noopener noreferrer"&gt;electron-shell-framework&lt;/a&gt;&lt;/strong&gt; — a reusable Electron app shell that is a &lt;em&gt;platform&lt;/em&gt;, not an app. You build a new desktop app (chat client, dashboard, internal tool) by dropping in pages. You never rewrite the shell.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "app shell" means, concretely
&lt;/h2&gt;

&lt;p&gt;Here's the actual chrome you inherit on day one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────┬──────────────────────────────────────────────┬──────────┐
│          │ [tabs] Dashboard | Settings | Communication  │          │
│   Left   ├──────────────────────────────────────────────┤  Right   │
│ Sidebar  │                                              │  Panel   │
│  (nav,   │   Content — the active page                  │ (notif,  │
│ collapse)│                                              │  log)    │
└──────────┴──────────────────────────────────────────────┴──────────┘
┌──────────────────────────────────────────────────────────────────────┐
│ FooterBar — version · platform · app name                            │
└──────────────────────────────────────────────────────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One top bar, not two.&lt;/strong&gt; The tab strip and the window controls (min/max/close at 60% native opacity) share a single 40px bar. A toggle collapses the tabs into a slim strip that keeps the active page name. The bar stays draggable (&lt;code&gt;app-drag&lt;/code&gt;); interactive regions opt out (&lt;code&gt;app-no-drag&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Left sidebar&lt;/strong&gt; — collapsible, drag-resizable (150–320px), profile + settings in the footer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Right panel&lt;/strong&gt; — page-scoped: each page can declare its own inspector, with notifications / activity-log views as the default. Collapses to an arrow-only rail.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bottom panel&lt;/strong&gt; — terminal-style strip, collapsed by default.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Footer bar&lt;/strong&gt; — full-width status frame.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Dark/light theme&lt;/strong&gt; — both sidebars included, persisted across launches.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The page registry is the whole extension point
&lt;/h2&gt;

&lt;p&gt;Every screen is a &lt;code&gt;PageDefinition&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/renderer/src/types/pages.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CSSProperties&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;PageDefinition&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="nx"&gt;description&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="cm"&gt;/** Rendered in the left sidebar (collapsed mode shows icon only). */&lt;/span&gt;
  &lt;span class="nx"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;className&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;size&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;style&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;CSSProperties&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt;
  &lt;span class="cm"&gt;/** Optional right-panel view for this page (chat inspectors, detail panes…) */&lt;/span&gt;
  &lt;span class="nx"&gt;rightPanel&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;ComponentType&lt;/span&gt;
  &lt;span class="cm"&gt;/** Groups this page under a category in the tab bar (rendered as a sub-tab). */&lt;/span&gt;
  &lt;span class="nx"&gt;category&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;
  &lt;span class="cm"&gt;/** When false the page hides from the sidebar (e.g. settings). */&lt;/span&gt;
  &lt;span class="nx"&gt;showInSidebar&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Register it once and the shell does the rest — sidebar icon, tab, content area, optional right panel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/renderer/src/pages/registry.tsx&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PAGES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;PageDefinition&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Dashboard&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;KPI grid + activity&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;BarChart3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DashboardPage&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;chat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;label&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Chat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;category&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Communication&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;icon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;MessageSquare&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;component&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ChatPage&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;// …&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A new app is five steps: clone → write &lt;code&gt;MyPage.tsx&lt;/code&gt; → add an entry to the array → rename → &lt;code&gt;npm run dev&lt;/code&gt;. No shell edits, ever. Pages that share a &lt;code&gt;category&lt;/code&gt; get grouped into a sub-tab cluster in the top bar — that's how you scale from 4 pages to 15 without tab soup.&lt;/p&gt;

&lt;h2&gt;
  
  
  Config that isn't a plaintext JSON file
&lt;/h2&gt;

&lt;p&gt;Most Electron tutorials store settings with &lt;code&gt;JSON.stringify&lt;/code&gt; and ship it. This shell routes every value through &lt;code&gt;safeStorage&lt;/code&gt; — &lt;strong&gt;DPAPI on Windows, Keychain on macOS&lt;/strong&gt; — so nothing hits disk unencrypted:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/main/config-store.ts (excerpt)&lt;/span&gt;
&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ConfigValue&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;serialized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nx"&gt;encrypted&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;safeStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;isEncryptionAvailable&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`enc:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;safeStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encryptString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;serialized&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`raw:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;Buffer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;serialized&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf-8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;base64&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each entry is stored as &lt;code&gt;enc:…&lt;/code&gt; or &lt;code&gt;raw:…&lt;/code&gt;, so the file is self-describing and degrades gracefully on machines without a keyring. The renderer gets &lt;code&gt;config.get / config.set / config.has&lt;/code&gt; over IPC — it never sees a filesystem path.&lt;/p&gt;

&lt;h2&gt;
  
  
  The IPC contract: one door, one shape
&lt;/h2&gt;

&lt;p&gt;Renderer ↔ main communication goes through &lt;code&gt;window.api&lt;/code&gt; only. &lt;code&gt;sandbox: true&lt;/code&gt;, &lt;code&gt;contextIsolation: true&lt;/code&gt;, &lt;code&gt;nodeIntegration: false&lt;/code&gt; — and &lt;strong&gt;no raw &lt;code&gt;ipcRenderer&lt;/code&gt; passthrough&lt;/strong&gt;, so a compromised renderer cannot invoke an arbitrary channel.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;API&lt;/th&gt;
&lt;th&gt;Channel&lt;/th&gt;
&lt;th&gt;Purpose&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;window.api.config.get/set/has(key)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;config:*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Encrypted config store&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;window.api.app.version()/ping()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;app:*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Version + platform health check&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;window.api.window.minimize/maximize/close()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;window:*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Frameless window controls&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;window.api.window.setOpacity(v)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;window:setOpacity&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Native window opacity (persisted)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;window.api.update.check()/quitAndInstall()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;update:*&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Auto-update (GitHub releases)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;window.api.update.onStatus(fn)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;push &lt;code&gt;update:status&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Live update events → Settings UI&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Two files define the contract — &lt;code&gt;src/main/ipc.ts&lt;/code&gt; and &lt;code&gt;src/preload/index.ts&lt;/code&gt; — and a future backend (HTTP, WebSocket, DB) hooks in right there without the shell ever changing.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;shell-cli&lt;/code&gt;: one command from clone to installer
&lt;/h2&gt;

&lt;p&gt;I hate READMEs that are really 14 manual steps. So the repo ships a &lt;strong&gt;zero-dependency CLI&lt;/strong&gt; — Node stdlib only, so it runs before you've even &lt;code&gt;npm install&lt;/code&gt;ed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node scripts/shell-cli.js &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;— Step 1/6: Preflight              (Node ≥ 20, npm, git)
— Step 2/6: Install dependencies
— Step 3/6: Sanity checks          (typecheck, lint, unit tests)
— Step 4/6: Build
— Step 5/6: Run
— Step 6/6: Package (optional — pass --package)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;check&lt;/code&gt;, &lt;code&gt;dev&lt;/code&gt;, &lt;code&gt;build&lt;/code&gt;, &lt;code&gt;run&lt;/code&gt;, &lt;code&gt;test&lt;/code&gt;, &lt;code&gt;package&lt;/code&gt; and &lt;code&gt;help&lt;/code&gt; map to what you'd expect, and &lt;code&gt;install&lt;/code&gt; runs the full flow: preflight → deps → checks → build → run → package.&lt;/p&gt;

&lt;h2&gt;
  
  
  Packaging and updates, wired from commit one
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;npm run dist:win&lt;/code&gt; → an &lt;strong&gt;NSIS installer&lt;/strong&gt; (custom install directory, desktop + start-menu shortcuts) &lt;em&gt;and&lt;/em&gt; a &lt;strong&gt;portable exe&lt;/strong&gt;, in &lt;code&gt;release/&amp;lt;version&amp;gt;/&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Auto-update through &lt;code&gt;electron-updater&lt;/code&gt; + the GitHub &lt;code&gt;publish&lt;/code&gt; provider. Tag &lt;code&gt;vX.Y.Z&lt;/code&gt;, push, and packaged installs pick it up from &lt;strong&gt;Settings → Updates&lt;/strong&gt;, with live status events streamed to the UI: &lt;code&gt;checking → available → downloading (%) → downloaded&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;.github/workflows/release.yml&lt;/code&gt; builds and publishes on Windows, macOS and Linux from the same tag.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quality gates included (the part that saves you)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Vitest&lt;/strong&gt; unit tests for the stores, theme helpers and the registry.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Playwright e2e that launches the real Electron app&lt;/strong&gt; and asserts the chrome exists and works — including this wonderfully pedantic one:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;window controls are pinned to the right edge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;closeBox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Close&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;exact&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;boundingBox&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;innerWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;evaluate&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerWidth&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;closeBox&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;closeBox&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;width&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="nx"&gt;innerWidth&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeLessThanOrEqual&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because "no trailing pixel gap" is exactly the kind of thing you otherwise fix by hand 400 times.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ESLint + Prettier + Husky / lint-staged on pre-commit.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Theming: restyle variables, not components
&lt;/h2&gt;

&lt;p&gt;All colors live as CSS variables in &lt;code&gt;src/renderer/src/styles/theme.css&lt;/code&gt; and are mapped into Tailwind v4 via &lt;code&gt;@theme inline&lt;/code&gt;, so utilities like &lt;code&gt;bg-card&lt;/code&gt;, &lt;code&gt;border-input&lt;/code&gt;, &lt;code&gt;text-muted-foreground&lt;/code&gt; resolve to &lt;em&gt;your&lt;/em&gt; tokens — including the separate &lt;code&gt;--sidebar-*&lt;/code&gt;, &lt;code&gt;--tab-*&lt;/code&gt; and &lt;code&gt;--rightpanel-*&lt;/code&gt; surface tokens most templates forget about.&lt;/p&gt;

&lt;p&gt;To brand an app: change the variables. Every component, both themes, all four panels follow automatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I got wrong the first time (an honest security pass)
&lt;/h2&gt;

&lt;p&gt;Before calling this v0.1 I ran it against Electron's official security checklist and wrote every gap down in &lt;code&gt;docs/framework-security-report.md&lt;/code&gt; instead of pretending it wasn't there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Already right:&lt;/strong&gt; sandboxed preload exposing only a namespaced API, CSP present, &lt;code&gt;setWindowOpenHandler&lt;/code&gt; denies popups, &lt;code&gt;safeStorage&lt;/code&gt;-encrypted config, frameless surface.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Still on me before v0.2:&lt;/strong&gt; flip the &lt;code&gt;@electron/fuses&lt;/code&gt; switches (&lt;code&gt;RunAsNode: false&lt;/code&gt;, &lt;code&gt;EnableNodeOptionsEnvironmentVariable: false&lt;/code&gt;, ASAR integrity validation), add sender validation on every &lt;code&gt;ipcMain&lt;/code&gt; handler (main-frame check + argument validation), block &lt;code&gt;will-navigate&lt;/code&gt;, allowlist &lt;code&gt;shell.openExternal&lt;/code&gt; targets, move the packaged renderer off &lt;code&gt;file://&lt;/code&gt; to a privileged &lt;code&gt;app://&lt;/code&gt; protocol, and tighten the production CSP — that dev-only &lt;code&gt;ws://localhost:*&lt;/code&gt; in &lt;code&gt;connect-src&lt;/code&gt; should never ship.&lt;/p&gt;

&lt;p&gt;If you're building an Electron app right now, that list is worth stealing even if you skip the framework.&lt;/p&gt;

&lt;h2&gt;
  
  
  How is this different from a starter template?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Starters give you a build config and a blank &lt;code&gt;App.tsx&lt;/code&gt;. This gives you the &lt;strong&gt;chrome&lt;/strong&gt;: four panels, one merged top bar, a resizable layout engine, and a registry that turns a page into a tab + sidebar entry + right panel.&lt;/li&gt;
&lt;li&gt;Starters stop at &lt;code&gt;npm run build&lt;/code&gt;. Here the &lt;strong&gt;release path&lt;/strong&gt; is already wired: NSIS + portable, GitHub releases, in-app update status UI.&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;security work is documented, not implied&lt;/strong&gt; — an explicit checklist with the remaining gaps listed as tasks, so you know what you're inheriting.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What it is &lt;em&gt;not&lt;/em&gt; (v1 scope, honestly)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;No backend, no server, no web attach — in production the renderer loads from &lt;code&gt;file://&lt;/code&gt;. Pure desktop.&lt;/li&gt;
&lt;li&gt;No dynamic/closeable tabs, no splash screen, no i18n yet — those are on the roadmap.&lt;/li&gt;
&lt;li&gt;It's a shell, not a UI kit. You get the primitives (Radix-based shadcn-style components, a recharts chart block, a zod + react-hook-form block), but your app's soul is your pages.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/hlsitechio/electron-shell-framework
&lt;span class="nb"&gt;cd &lt;/span&gt;electron-shell-framework
node scripts/shell-cli.js &lt;span class="nb"&gt;install&lt;/span&gt;   &lt;span class="c"&gt;# preflight → deps → checks → build → run&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Under the hood: &lt;strong&gt;Electron 44&lt;/strong&gt; · electron-vite 5 · electron-builder 26 · React 19 · TypeScript 5.9 · Tailwind CSS v4 · Radix primitives · zustand · zod + react-hook-form · recharts · Vitest + Playwright.&lt;/p&gt;

&lt;p&gt;If you build something on it — or you have a "this is missing from every Electron template" item I should add to the roadmap — tell me in the comments. Issues and PRs are welcome.&lt;/p&gt;

</description>
      <category>electron</category>
      <category>react</category>
      <category>typescript</category>
      <category>showdev</category>
    </item>
    <item>
      <title>I built a unified context gateway for AI agents that syncs in ~12ms. Here is how it works.</title>
      <dc:creator>Hubert Larose Surprenant</dc:creator>
      <pubDate>Wed, 19 Aug 2026 17:30:18 +0000</pubDate>
      <link>https://dev.to/hubert_larosesurprenant_/i-built-a-unified-context-gateway-for-ai-agents-that-syncs-in-12ms-here-is-how-it-works-48ib</link>
      <guid>https://dev.to/hubert_larosesurprenant_/i-built-a-unified-context-gateway-for-ai-agents-that-syncs-in-12ms-here-is-how-it-works-48ib</guid>
      <description>&lt;p&gt;If you were building autonomous workflows, you were probably suffering from "framework fatigue."&lt;/p&gt;

&lt;p&gt;Every time you switched between IDEs like Cursor, terminal agents like Claude Code, or browser-based assistants, you had to reconfigure your tools, re-authenticate your keys, and re-index your files. You were manually duct-taping agents to a dozen different local MCP (Model Context Protocol) servers, vector databases, and static configuration files.&lt;/p&gt;

&lt;p&gt;It was tedious, it was fragile, and it did not scale.&lt;/p&gt;

&lt;p&gt;I got tired of managing a fragmented stack of isolated AI assistants. I wanted a "motherboard" for AI agents—a unified gateway that abstracted vector storage, memory, and OAuth connectors behind a single endpoint.&lt;/p&gt;

&lt;p&gt;So, I built Memorify.&lt;/p&gt;

&lt;p&gt;The vision was simple:&lt;/p&gt;

&lt;p&gt;One gateway.&lt;br&gt;
One connection.&lt;br&gt;
Every agent. Every tool.&lt;br&gt;
Once and for all.&lt;br&gt;
Here is a transparent, under-the-hood breakdown of how Memorify delivered a ~12ms real-time context bus using Deno, Neon Postgres, and ElectricSQL, and how it was used to give agents persistent memory.&lt;/p&gt;

&lt;p&gt;🚀 The Quick Handshake&lt;br&gt;
I wanted the developer onboarding experience to be absolutely frictionless. Instead of setting up custom SDKs, developers could connect Memorify as an MCP server to any tool in seconds.&lt;/p&gt;

&lt;p&gt;Here was the exact client handshake. When an agent was registered, Memorify issued a secure, server-side scoped Bearer token. To verify the connection, a standard JSON-RPC POST request was sent to the gateway endpoint:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST https://memorify.dev/mcp &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Accept: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Authorization: Bearer &lt;/span&gt;&lt;span class="se"&gt;\$&lt;/span&gt;&lt;span class="s2"&gt;MEMORIFY_AGENT_TOKEN"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'&lt;/span&gt;
Instantly, the gateway returned the available tools registered under the agent profile, such as the identity verification tool:

&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"jsonrpc"&lt;/span&gt;: &lt;span class="s2"&gt;"2.0"&lt;/span&gt;,
  &lt;span class="s2"&gt;"id"&lt;/span&gt;: 1,
  &lt;span class="s2"&gt;"result"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"tools"&lt;/span&gt;: &lt;span class="o"&gt;[&lt;/span&gt;
      &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"name"&lt;/span&gt;: &lt;span class="s2"&gt;"whoami"&lt;/span&gt;,
        &lt;span class="s2"&gt;"description"&lt;/span&gt;: &lt;span class="s2"&gt;"Return info about the connected agent + workspace."&lt;/span&gt;,
        &lt;span class="s2"&gt;"inputSchema"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt; &lt;span class="s2"&gt;"type"&lt;/span&gt;: &lt;span class="s2"&gt;"object"&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="o"&gt;]&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;By calling whoami, the agent immediately confirmed its identity, workspace, and authorization scopes, establishing a secure connection once and for all.&lt;/p&gt;

&lt;p&gt;🔌 Persistent MCP Hot-Plugging: No More Agent Reboots&lt;br&gt;
In traditional setups, adding a new MCP server or modifying your configurations was a massive disruption. Developers had to edit local configuration files, manually register endpoints, and completely reboot their CLI agents or IDE extensions to discover the updated schemas. It shattered developer flow.&lt;/p&gt;

&lt;p&gt;Memorify shifted this entirely to the gateway layer with Persistent MCP Hot-Plugging:&lt;/p&gt;

&lt;p&gt;Connect Once, Accessible Everywhere: You registered your local or hosted MCP servers through the central Memorify gateway exactly once. Every connected agent instantly inherited them without needing individual local setup.&lt;br&gt;
Zero-Reboot Dynamic Updates: When you added, updated, or removed an MCP server in your dashboard, the changes streamed live. Connected agents immediately reflected the new toolsets over the active handshake—meaning zero agent reboots, reloads, or session restarts when scaling your toolchain.&lt;/p&gt;

&lt;p&gt;🧠 The Architecture: Verbs Over Schemas&lt;br&gt;
Most heavy orchestration frameworks forced developers and agents into rigid, schema-heavy database interactions. Memorify rejected SQL and complex schemas for agent interactions, opting instead for a Verb-Based Semantic Protocol.&lt;/p&gt;

&lt;p&gt;Agents interacted using straightforward natural language intents. Instead of writing raw database inserts, the API was cleanly segmented into simple semantic capability domains:&lt;/p&gt;

&lt;p&gt;/memory (remember, recall, update) — Wrote and retrieved episodic atomic facts.&lt;br&gt;
/documents (add_from_url, search, view) — Ingested and chunk-indexed long-form markdown and web content.&lt;br&gt;
/skills (list, run) — Declares and executes modular tools.&lt;br&gt;
/mcp (servers, tools, call) — Exposed unified Model Context Protocol routing.&lt;/p&gt;

&lt;p&gt;By giving agents a predictable REST/JSON structure mapped to simple concepts like "remember this," I drastically reduced the prompt engineering and token overhead required. The agent did not need to know the underlying database schemas; it simply outputted its semantic intent.&lt;/p&gt;

&lt;p&gt;⚡ The Execution Stack: Achieving ~12ms Sync Latency&lt;br&gt;
For a multi-agent workflow to actually feel cohesive, an action taken in a terminal (like a local CLI command run via Claude Code) had to be instantly visible to an assistant running in a browser. Achieving this cross-agent synchronization required building an edge-routing infrastructure with zero cold-start penalties.&lt;/p&gt;

&lt;p&gt;The stack was designed from the ground up for low latency and high availability:&lt;/p&gt;

&lt;p&gt;Component   Role in the Gateway Performance Result&lt;br&gt;
Deno Deploy Ran V8 isolates at the edge to intercept, authenticate, and route requests instantly.   Eliminated cold-start penalties, keeping edge routing overhead to a minimum.&lt;br&gt;
Neon Postgres   Handled serverless relational data (user rules, episodic logs) alongside native pgvector storage.   Delivered instant database scaling and high-performance vector searches without the complexity of a separate database.&lt;br&gt;
ElectricSQL Streamed partial database replicas ("shapes") over HTTP directly from Neon's logical replication stream.    Achieved active-active sync, letting us stream updates to any edge instance in near real-time.&lt;br&gt;
Instead of building a clunky database polling mechanism or maintaining stateful, resource-heavy WebSockets manually, ElectricSQL handled active-active replication. When a new memory dropped into Neon Postgres, ElectricSQL fanned it out instantly, completely decoupling real-time synchronization from our core application logic.&lt;/p&gt;

&lt;p&gt;🔒 Zero-Leak Security: Why Trusting Your Gateway Mattered&lt;br&gt;
When building a gateway that autonomously read files, stored passwords, or invoked APIs, security was the ultimate hurdle. Memorify enforced strict, multi-tenant isolation at every layer of the platform:&lt;/p&gt;

&lt;p&gt;Zero-Leak Vault: All third-party API keys, OAuth credentials, and sensitive tokens were encrypted at rest using AES-256-GCM.&lt;br&gt;
Granular Cryptographic Tokens: Access utilized strict, server-side tokens carrying narrow scopes (such as memory:read, skills:read, or documents:write). These were checked on every request and could be revoked instantly from the dashboard.&lt;br&gt;
Hardware-Secured Isolation: Each workspace operated in a completely isolated tenant schema within Neon Postgres, backed by strict Row-Level Security (RLS) policies. One workspace's agents could never query, leak, or view data from another's, guaranteeing enterprise-grade privacy.&lt;br&gt;
🛠️ The Meta Layer: High-Velocity AI Engineering&lt;br&gt;
Building an edge-routed, multi-agent platform as a solo developer was a massive undertaking. To go from an abstract concept to a live, production-grade service in record time, I utilized an aggressive AI-assisted co-piloting workflow:&lt;/p&gt;

&lt;p&gt;Boilerplate Delegation: I used code-generation tools to spin up the entire React/Vite front-end interface, freeing myself up to focus on the core state-synchronization logic and gateway API design.&lt;br&gt;
Architectural Guardrails: I used AI pairing to stress-test the Neon Postgres relational schemas, verify Row-Level Security (RLS) boundaries, and map out the real-time ElectricSQL replication filters.&lt;br&gt;
This was the power of developer leverage. By treating AI models as specialized junior engineers for boilerplates and schema verification, a single developer could confidently design and deploy secure, global, multi-tenant infrastructure operating at sub-15ms latency scales.&lt;/p&gt;

&lt;p&gt;💬 Let's Connect!&lt;br&gt;
My ultimate goal was to make AI agents truly collaborative, persistent, and portable. I wanted developers to be able to create notebooks, write code, and have those sessions instantly sync directly into Memorify as portable, cross-agent memory.&lt;/p&gt;

&lt;p&gt;Because this was designed as a developer-centric product, I would love to hear your thoughts:&lt;/p&gt;

&lt;p&gt;How were you managing context or memory across different AI assistants before?&lt;br&gt;
What tools or integrations would you want to see bridged next?&lt;br&gt;
Do you have any suggestions or feedback on the verb-based semantic protocol design?&lt;br&gt;
Drop a comment below and let me know your thoughts! 🚀&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/hlsitechio" rel="noopener noreferrer"&gt;
        hlsitechio
      &lt;/a&gt; / &lt;a href="https://github.com/hlsitechio/memorify" rel="noopener noreferrer"&gt;
        memorify
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Memorify — proprietary. Source is private.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Memorify&lt;/h1&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Shared memory and tools for MCP-capable agents.&lt;/h3&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://memorify.dev" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/9f85912fa833fe9b5f11bfed97eb224694f2a2a33235f47a73ce769c9c900b63/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f476174657761792d4f6e6c696e652d3030453539393f7374796c653d666c61742d737175617265266c6f676f3d73746174757370616765266c6f676f436f6c6f723d7768697465" alt="Status"&gt;&lt;/a&gt;
&lt;a href="https://modelcontextprotocol.io" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/2655594b34a4770f887e1e1a774574ab10cbe49e0ffbc24eb67e9c3bb190037d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4d43502d323032342d2d31312d2d30352d3739453343313f7374796c653d666c61742d737175617265" alt="Protocol"&gt;&lt;/a&gt;
&lt;a href="https://memorify.dev/mcp" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/1a2dfa5b673bc62cc4df0ecc6239467394391782fc4ef5c984c6bc20bc388731/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4a534f4e2d2d5250432d322e302d626c75653f7374796c653d666c61742d737175617265" alt="JSON-RPC"&gt;&lt;/a&gt;
&lt;a href="https://memorify.dev" rel="nofollow noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/92082f18a114738d57eba40f401b40122523372acf7428e4c81ce83341cf3aef/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f5661756c742d4145532d2d3235362d2d47434d2d626c756576696f6c65743f7374796c653d666c61742d737175617265" alt="Security"&gt;&lt;/a&gt;
&lt;a href="https://github.com/hlsitechio/memorify/LICENSE" rel="noopener noreferrer"&gt;&lt;img src="https://camo.githubusercontent.com/c2688087d85f24e9728a0bc8dc3ea18e7824adcc2ac57dcdefbfc6f4316febff/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c6963656e73652d50726f70726965746172792d7265643f7374796c653d666c61742d737175617265" alt="License"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://memorify.dev" rel="nofollow noopener noreferrer"&gt;Website&lt;/a&gt; • &lt;a href="https://memorify.dev/auth" rel="nofollow noopener noreferrer"&gt;Auth Portal&lt;/a&gt; • &lt;a href="https://memorify.dev/dashboard" rel="nofollow noopener noreferrer"&gt;Control Plane&lt;/a&gt; • &lt;a href="https://memorify.dev/mcp" rel="nofollow noopener noreferrer"&gt;MCP Endpoint&lt;/a&gt; • &lt;a href="https://memorify.dev/#pricing" rel="nofollow noopener noreferrer"&gt;Pricing&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;⚡ Overview&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Memorify&lt;/strong&gt; — Shared memory and tools for MCP-capable agents.&lt;/p&gt;
&lt;div&gt;
  &lt;a rel="noopener noreferrer" href="https://github.com/hlsitechio/memorify/./public/memorify-architecture-diagram.jpg"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fhlsitechio%2Fmemorify%2FHEAD%2F.%2Fpublic%2Fmemorify-architecture-diagram.jpg" alt="Memorify Architecture Diagram — Unified Control Plane &amp;amp; Persistent Memory Gateway for Autonomous AI Agents" width="100%"&gt;&lt;/a&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🛡️ Zero-Leak Security Architecture&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Memorify is engineered from the ground up for strict confidentiality and multi-tenant isolation:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Zero-Leak Secret Vault&lt;/strong&gt;: Third-party API keys (GitHub, Netlify, Stripe, Resend, Cloudflare) are encrypted at rest using AES-256-GCM. AI agents invoke actions through the gateway without ever receiving or inspecting raw secrets.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Granular Scoped Tokens&lt;/strong&gt;: Access tokens (&lt;code&gt;mem_live_...&lt;/code&gt;) utilize strict cryptographic scoping (&lt;code&gt;memory:read&lt;/code&gt;, &lt;code&gt;memory:write&lt;/code&gt;, &lt;code&gt;skills:read&lt;/code&gt;, &lt;code&gt;documents:read&lt;/code&gt;, &lt;code&gt;tokens:admin&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hardware-Secured Isolation&lt;/strong&gt;: Each workspace operates in an isolated tenant schema within Neon Postgres with strict Row-Level Security (RLS).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Zero Browser State Dependency&lt;/strong&gt;: Authentication state is derived purely server-side from cryptographically signed session claims.&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🛠️ Supported Agent Integrations&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;Connect any Model Context Protocol compliant client…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hlsitechio/memorify" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
        &lt;div class="c-embed__cover"&gt;
          &lt;a href="https://memorify.dev/" class="c-link align-middle" rel="noopener noreferrer"&gt;
            &lt;img alt="" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmemorify.dev%2Fbrand%2Fhero-banner-memorify-front-gate.png" height="auto" class="m-0"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://memorify.dev/" rel="noopener noreferrer" class="c-link"&gt;
            Memorify.dev
          &lt;/a&gt;
        &lt;/h2&gt;
          &lt;p class="truncate-at-3"&gt;
            A live MCP gateway and control plane for AI agents with durable memory, searchable documents, skills, external tools, and agent access levels.
          &lt;/p&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fmemorify.dev%2Ffavicon.ico"&gt;
          memorify.dev
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>agents</category>
      <category>ai</category>
      <category>mcp</category>
      <category>performance</category>
    </item>
  </channel>
</rss>
