npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

modelhitch-electron

v0.1.0

Published

Electron adapter for ModelHitch — streaming BYOK AI in desktop apps with OS-backed credential storage.

Readme

ModelHitch for Electron

modelhitch-electron is the Electron adapter for ModelHitch. It is a thin layer over the published modelhitch package — same client, same providers, same failover and usage tracking — wired for a desktop runtime:

  • OS-backed BYOK keys. SafeStorageKeyStore encrypts each provider key with Electron safeStorage (macOS Keychain, Windows DPAPI, Linux Secret Service when available) and persists the ciphertext in the app's user-data directory. Keys never reach your backend.
  • Renderer-safe IPC. Renderer windows cannot call safeStorage directly. Register registerKeyStoreIpc in the main process and pass createIpcKeyStore() to createElectronModelHitch in the renderer.
  • A bundler-safe entry point. Some Electron bundlers resolve the Node modelhitch entry and pull in the bridge server and node:sqlite. Importing modelhitch-electron gets you the browser-safe surface instead.

Runs in the main process and in renderer windows from the same package.

Install

npm install modelhitch modelhitch-electron

Requires Electron 28 or later.

Quick start (main process)

import { createElectronModelHitch } from 'modelhitch-electron';

const mh = createElectronModelHitch({ defaultProviderId: 'openai' });

// The user pastes their own key once; it is encrypted with the OS credential store.
await mh.keystore?.set('openai', keyPastedByUser);

const stream = await mh.stream({
  model: 'gpt-4o-mini',
  messages: [{ role: 'user', content: 'Hello from Electron' }],
});

for await (const chunk of stream) {
  if (chunk.type === 'text-delta') setReply((prev) => prev + chunk.text);
}

createElectronModelHitch accepts every ModelHitchOptions field (providers, policy, autoMode, keystore, ...) plus one Electron-specific option:

| Option | Default | Purpose | | --- | --- | --- | | safeStorage | {} | Options for the default SafeStorageKeyStore (prefix, storageDir, fallback) |

For catalog mode ({ catalog: { ... } }), use await createElectronModelHitchAsync({ ... }) — the models.dev inventory has to be fetched before the first call.

Renderer process

Register the keystore in the main process, then point the renderer client at IPC:

// main.ts
import { app, BrowserWindow } from 'electron';
import { SafeStorageKeyStore } from 'modelhitch-electron';
import { registerKeyStoreIpc } from 'modelhitch-electron/ipc';

app.whenReady().then(() => {
  registerKeyStoreIpc(new SafeStorageKeyStore());
  new BrowserWindow({ /* ... */ });
});
// renderer.ts
import { createElectronModelHitch } from 'modelhitch-electron';
import { createIpcKeyStore } from 'modelhitch-electron/ipc';

const mh = createElectronModelHitch({
  defaultProviderId: 'openai',
  keystore: createIpcKeyStore(),
});

Credentials

import { SafeStorageKeyStore } from 'modelhitch-electron';

const keys = new SafeStorageKeyStore({
  prefix: 'com.acme.app',
  storageDir: '/custom/path/modelhitch-keys',
});

await keys.set('openai', userKey);
await keys.get('openai');
await keys.delete('openai');

Entries are namespaced per provider (modelhitch_openai.enc), so switching providers never overwrites another provider's key.

When safeStorage.isEncryptionAvailable() is false (common on some Linux setups without a Secret Service provider), the store falls back to an in-memory MemoryKeyStore unless you pass a custom fallback. Treat that as a development-only path — production Linux builds should ship with a working OS secret backend or route keys through your backend / a ModelHitch bridge.

React hooks

modelhitch-electron/react re-exports useChat, useStream, and createBridgeClient unchanged. They point at a ModelHitch bridge, which is the right shape when your backend owns the keys, tools, and failover:

import { useChat } from 'modelhitch-electron/react';

const { messages, pending, send, isThinking } = useChat({
  baseUrl: 'http://127.0.0.1:3939/v1',
  model: 'openai/gpt-4o-mini',
});

Use http://127.0.0.1:3939/v1 when pointing at a bridge running on the same machine.

Which pattern?

| | Direct BYOK | Bridge | | --- | --- | --- | | Key owner | The end user | Your backend / local bridge | | Key storage | OS credential store (main) or IPC | Server env vars / local bridge | | Extra infra | None | A bridge/proxy service | | Use | createElectronModelHitch | useChat / createBridgeClient |

Never ship your own provider key inside an Electron binary — a desktop app is readable. Direct mode is only for keys the user supplies; anything else belongs behind the bridge.

Main-process bridge hosting

This package is a client library. It does not embed or wrap the ModelHitch CLI. To host a local bridge from your app's main process, install modelhitch separately and import the Node entry in main only:

import { startBridge } from 'modelhitch'; // main process only — never bundle in renderer

Keep bridge startup in the main process and point renderer UI code at http://127.0.0.1:3939/v1.

Development

The adapter is typechecked and tested against in-repo stubs for the Electron and modelhitch peers, so no Electron app tree is needed:

npm run typecheck            # tsc -p tsconfig.dev.json (stubbed peers)
npx vitest run --config vitest.config.ts

Building or publishing uses the real peers:

npm install
npm run build

See PUBLISHING.md.