Find a file
cogwheel 9ee7b114ee
Talk with a realtime voice in calls on Open WebUI, Hermes and Direct (#842)
* Keep what a voice call said on its messages, and read Open WebUI's realtime call settings

Open WebUI 0.12 stores a realtime call's spoken transcripts in each
message's meta.voice and replays them to the chat model. A message
Conduit wrote again from its own state carried no meta, and the server
replaces each message whole, so the next push would have erased them.
Rows rewritten locally now keep the meta stored on them, the voice record
is read into the message's metadata, and the echo writes it back where
Open WebUI reads it. Another device's chat:message:voice refreshes the
chat.

Direct chats replay what a call spoke the way Open WebUI does: a reply
the voice gave by itself becomes its transcript, labeled as historical
voice speech, and what was spoken over a chat answer follows that answer.

The config's audio.realtime block and the chat.call permission are read,
ready for gating realtime calls; unreadable permissions count as unknown.

* Speak and listen through each chat's own server, including Hermes and Direct

The Server engines for speech to text and text to speech only worked
with an Open WebUI server, and in a Hermes or Direct chat they quietly
used whichever Open WebUI server was signed in. Server speech now
follows the selected model's backend: an Open WebUI model uses its
server, a Hermes chat on a Desktop Gateway uses the gateway's audio
relay, and a Direct or Apple chat uses a new Voice provider. A chat
whose backend has none uses the device instead; audio never goes to a
server the chat does not belong to.

Hermes speech goes through /api/audio/transcribe and /api/audio/speak
for the connection's profile, with the providers and keys configured on
the gateway; the voice-config route, which hands out provider keys, is
never called.

The Voice provider is one Direct connection that speaks the OpenAI API,
chosen in Audio settings with its transcription model, speech model and
voice. Requests go to its audio/transcriptions and audio/speech with
the connection's own key, headers, api-version and certificates. Picking
OpenAI fills in the models Open WebUI defaults to. The page is also in
the native Settings sheet on iOS.

The server voice picker shows only for Open WebUI, which lists voices;
Hermes and the Voice provider speak with their own.

* Talk to a realtime voice in calls, ready for Open WebUI and Direct

Open WebUI 0.12 can run calls through an OpenAI realtime voice that
answers small talk itself and hands real requests to the chat's model.
This adds the call side of that, shared by Open WebUI and Direct chats;
the audio engine it needs comes next, so calls stay as they are until
then.

The engine follows Open WebUI's web client: one request to the voice at
a time, made only while nobody is speaking; the chat's latest state sent
before each; speaking over the voice cancels its reply and tells it how
much was heard; a request the voice delegates runs as an ordinary chat
turn, and its outcome goes back to be spoken. Open WebUI's server
bridge carries a call for its chats. A Direct or Apple chat talks to the
Voice provider's Realtime API instead, with the bridge's rules checked
on the device, so one engine runs both; the device's bridge is tested
against the server's own rules, and the engine's tests hold every
command to them.

A delegated request is sent like a typed message, with the user's words
marked as spoken. A reply the voice gives itself is stored through the
same send as a finished answer under the voice model, and no model
runs; Open WebUI replays it to the chat model as past speech. What the
voice says about a delegated answer is merged into that answer's
meta.voice. Words nothing answered stay in the call's captions.

The call controller runs either engine: a call with a realtime voice
keeps CallKit, the audio session, background work, mute, speaker and
the call panel as before. Whether a call has one is decided when it
starts, and it falls back to a Standard call whenever anything is
unknown: Standard chosen in settings, a temporary chat, a server without
realtime calls, an account that may not call or whose permissions could
not be read, a sign-in with an API key, or a Voice provider without a
realtime model, voice and transcription model.

Direct connections now share their key, header and certificate setup
between HTTP and WebSocket, and Open WebUI's socket headers are built in
one place, as is the WebSocket that never follows redirects.

* Translate the Voice provider settings into Polish

Polish carries every English key, so the Voice provider's strings need
their Polish copies; without them polish_localization_test fails.

* Talk with a realtime voice in calls on iOS and Android

Calls in Direct and Apple chats with a Voice provider, and in Open WebUI
chats on a server with realtime calls, now talk with a realtime voice:
both sides can speak at once, and speaking over the voice stops it.

The voice needs the microphone and the speaker on one echo-cancelled
stream, so it does not hear itself. On iOS one audio engine runs with
voice processing on: the microphone is converted to 24 kHz frames, and
the voice plays from a queue that works like Open WebUI's web worklet,
starting once 80 ms are buffered and reporting how much of each part
was played when the user cuts in. It restarts after a route change. On
Android the microphone records as voice communication with the
platform's echo canceller, noise suppressor and gain control, and the
voice plays on the same audio session; what was heard comes from the
track's play position. A microphone that only offers 48 kHz is brought
down to 24 kHz. If the audio stops mid-call, the call ends and says so.

Audio settings gain Calls: Realtime, the default, or Standard, which
keeps every call turn by turn. The Voice provider takes a realtime
model, voice and optional instructions, prefilled for OpenAI. Both are
in the native Settings sheet on iOS too.

* Tell the user what a realtime call could not do, and mark what its voice said

A realtime call's problems that do not end it now show as messages in
the user's language: words that could not be made out, a reply cut
short, and a part of the call that could not be saved to the chat.

A reply the voice gave by itself shows its voice model with a waveform
and "spoken", even when the answer above came from the same model, and
offers no Regenerate: there is no chat model answer to run again.

Opening another chat, or none, ends a realtime call: its voice talks
about the chat it started in, and a request it handed over is answered
there. A call started without a chat follows the chat its first turn
creates, and that chat's server id.

* Talk with Hermes's GPT-Live voice in calls on a Desktop Gateway

A Hermes profile that runs voice calls through GPT-Live, and has an
OpenAI key for it, now gets realtime calls in Conduit too. The gateway
keeps the key: Conduit makes a WebRTC offer, Hermes opens the session
with OpenAI and returns the answer, and the voice's audio and events then
flow between the phone and OpenAI. Profiles in the chained voice mode, a
gateway that cannot say, and Responses API connections keep Standard
calls.

The call follows Hermes's own desktop client. GPT-Live takes its own
turns; a request it delegates carries no text, so Conduit sends the
user's latest words as an ordinary Hermes turn, marked as coming from
voice-live, with the spoken conversation of the last five minutes as
context only the model reads. While Hermes works the voice is told so
quietly; an approval waiting in the chat is mentioned once; the answer
is handed back in sentence-sized pieces for the voice to say. A newer
request stops the one still running, mute reaches the session, and
ending a call closes the session before the connection.

The WebRTC call comes from flutter_webrtc, behind the same port as the
other call audio, so the call controller, CallKit, background work and
the call panel are shared with every other call.

* Translate realtime calls and the Voice provider into every language

The Calls setting, the Voice provider page, the call's notices and the
"spoken" label were in English and Polish only, and the Localization check
requires every key in every language. They are now in all of them.

Every language also still said Server speech always goes to Open WebUI.
It now follows the chat's own server, so the descriptions name Open WebUI,
Hermes, or the Voice provider for Direct chats, as the English does.

* Keep the Voice provider page and the iOS call audio within the app's rules

The Voice provider page imported the Material and Cupertino libraries
directly, which the platform UI check allows only behind the shared
facade; it now takes its icons from the shared vocabulary. The package
boundary check also lists the new iOS call audio bridge, which reaches its
host only through ConduitBridgeHost.

* Fix what review found in realtime calls

- A call writes only to its own chat. What it said could land in the chat
  the user opened as the call ended, and a call started without a chat
  adopted any chat opened next. It now follows only the chat its own send
  creates and that chat's server id, writes nothing elsewhere, and merges a
  delegated answer's transcript into the call's chat even after a switch.
- A call that starts a new chat no longer seeds GPT-Live with the previous
  chat's messages, or names that chat to Open WebUI.
- Hanging up while a call is starting leaves no microphone, WebRTC peer or
  socket open. A WebSocket handshake that times out is stopped, and a
  socket that opens after it is closed.
- GPT-Live's microphone goes quiet before the session is asked to close,
  and an answer handed over as the call ends goes on in the chat unfollowed.
- Muting while speaking no longer holds a finished answer back until the
  user speaks again.
- Small talk held behind a delegated answer is saved after that answer
  when the call ends, so the answer stays last while it runs and can still
  be stopped. If it is still running a few seconds later, the small talk
  stays in the captions.
- A delegated answer whose send failed after it was placed ends as failed
  instead of working forever. Hermes approvals and questions now count as
  waiting for the user, so the voice says to answer them in the chat.
- GPT-Live's request is the user's latest words from the last five
  minutes, as in Hermes's own client; words from long before no longer
  join it.
- Read-aloud and dictation keep the server they started with, so text and
  recordings never go to another chat's server when the model changes.
- A voice reply's "spoken" label appears when its transcript arrives after
  the message, the native Voice provider choice is kept while connections
  reload, the call mode is saved on its own key, a Direct voice turn
  releases its database lease if saving it fails, and ending the call
  controller ends its realtime voice.

* Fix what review found in the realtime call audio on iOS and Android

- Android: a piece of the voice's speech taken just before the user cut in
  could still play, and count as heard. It is now taken with its clear
  generation, so a clear right after drops it. A stopped call's capture
  and playback threads stop with it, and an old thread sends nothing to
  the next call; the recorder is stopped before its thread is awaited, so
  a blocked read returns first.
- iOS: after a Bluetooth or headset route change, the microphone tap and
  converter are rebuilt for the new route's format. The playback queue no
  longer builds a string on the audio thread for every render.

* Fix what the second review found in realtime calls

- The first request in a new chat no longer ends the call: the call's own
  send counts as creating its chat until it is done, as the chat can open
  only after the answer is placed.
- A chat opened while the call connects ends it, and the voice never reads
  a chat other than the call's own.
- A reply cut off by hanging up waits behind a delegated answer still
  running, like the small talk held before it.
- A GPT-Live request replaced while the one before it was stopping is
  dropped instead of being sent to the chat.
- Resetting settings clears the Voice provider for good; it came back on
  the next launch.
- An earlier version of a voice reply is not labeled as spoken: versions
  keep no transcript to tell.
- The call's chat host closes even when the voice's ending fails, and
  chunkForCommentary takes only a positive limit.

* Follow only the chat a realtime call's own send created

A call started without a chat followed any chat that opened while its
first send was running, so a chat the user opened during that wait became
the call's: the voice read it, and later requests went there. The call now
remembers the messages its sends placed and follows a new chat only if it
holds them, which every send path's new chat does when it opens.

* Hand GPT-Live's newest request to the chat only once older answers stop

A request replaced while the one before it was still stopping skipped the
wait: the newest went to the chat while the first answer still ran, and
the chat put it off as busy. Stopping replaced requests now runs as one
chain, and a new request waits for all of it, including stops added while
it waited.

* Leave answers handed over as a realtime call ends running in the chat

- A request handed to the chat as the call ended was stopped by the bridge
  engine, though the chat should go on answering it. Both engines now stop
  only following such an answer, through a new DelegatedTurn.close that
  leaves the answer running, and its listener no longer outlives the call.
- A GPT-Live request still being handed to the chat when a newer one came
  is now stopped once the chat has it, before the newer request goes, so
  the chat never takes two at once.

* Name Apple chats where Server speech says it uses the Voice provider

The Voice provider page says Direct and Apple chats use it, but the
Server speech descriptions named only Direct chats. Both now say Direct
and Apple chats, in every language, worded as each language's Voice
provider description already words it.

* Keep realtime calls working alongside enforced temporary chats

Open WebUI 0.12 support (#843) made a new chat start temporary when the
server enforces it, and reading that now reaches the signed-in account and
its permissions. Three things follow for realtime calls:

- A call that starts a new chat stays Standard when the server will keep
  that chat temporary, as it already did for a temporary chat that is open.
- A realtime route that cannot be read keeps the call Standard instead of
  failing it, like anything else the route cannot tell.
- The Hermes route tests say the server enforces nothing, as they never
  sign in to Open WebUI.

* End a realtime call at once when it is hung up while connecting

- Hanging up while a realtime voice was still connecting waited for the
  start queued ahead of the stop, so the microphone and connection could
  stay up until the connection was made or timed out. The stop now ends a
  connecting voice right away, and the start it cuts short counts as
  cancelled, not failed.
- A voice reply's "spoken" label kept its old language after the app's
  language changed: the cached header now keys on the label as shown.

* Let every end of a realtime call wait for its one ending

A hang-up while the voice connects ends it at once, and the teardown that
follows ends it again. The second end returned as soon as the voice was
marked ended, so teardown could turn off the audio session before the
voice had stopped, and an error stopping it went unreported. Both engines
now keep their ending: every end waits for the same one, and teardown
reports what it throws.

* End a connecting realtime call at once when the system call UI hangs up

Hanging up from the CallKit screen queued the stop without the immediate
end a hang-up in the app gets, so a realtime voice still connecting kept
its microphone and connection until it connected or timed out, and could
connect after the call had ended. Both hang-ups now share that immediate
end, and the system's own end of the call is still not repeated.

* Fix what Greptile's full review found in realtime calls

- A chat opened while a call was still setting up kept the call going with
  the voice made for the chat the user left. The call now notes its chat
  when it starts, and a different chat open by the time it connects
  cancels it.
- Stopping a request the call replaces now waits for the chat's stop to
  settle, so the stop's late effects cannot reach the new request.
- An answer whose send failed after it was placed is marked failed in the
  chat, the way a typed send's is, instead of staying shown as running and
  keeping the chat busy.
- Small talk waits while the chat is answering, whoever asked; it used to
  wait only for answers the call had delegated, so a typed answer could
  stop being the chat's last message and could no longer be stopped.
- Pause holds the voice's answers until the call resumes, instead of only
  muting the microphone while finished answers went on being spoken.
- Speech to text set to Server uses the device in a chat whose backend
  cannot transcribe, as the Voice provider promised, instead of hiding
  voice input.
- Choosing the native Voice provider's connection, or editing its fields,
  now redraws that page, which iOS redraws only when it is the page the
  update is for.

* Hold a realtime call to the chat it was asked for, and unpause on unmute

- The chat a call is for is now noted when the call is asked for, before
  the setup that waits for the microphone and the route. A chat opened
  during that wait cancels the call instead of becoming its chat.
- Unmuting a paused realtime call, from the app or CallKit, now resumes it
  too; the voice stayed paused and kept its answers back while the call
  showed as live.

* Keep held small talk from silencing requests, and pause notices honest

- Small talk said while a typed answer runs waited on the call's own
  writes, which a request then waited behind for minutes, so the caller
  heard nothing instead of being told the chat was busy. It is now held
  apart: a request meanwhile hears at once that the chat is busy, and one
  made after the answer comes after the held words.
- A GPT-Live request that waited for approval while the call was paused,
  then finished, no longer has the voice say on resume that it still
  waits; its answer is still said.
- The native Voice provider page is patched alone and at once when it
  opens, rather than after the speech voices it does not show have loaded,
  so the patch cannot land over text the user is typing.

* Leave GPT-Live answers alone as a call closes, and drop replaced notices

- A request the voice handed over while the call was closing replaced the
  answer still running, which stopped it although an ended call leaves its
  answer to go on in the chat. Such a request now replaces nothing.
- A request replaced while the call was paused had its held approval
  notice said on resume, as if it still waited. Replacing a request now
  drops what was held about it.
2026-10-13 12:05:02 +05:30
.github Enforce the platform UI seam with a shrinking migration allow-list 2026-10-08 01:19:29 +05:30
android Talk with a realtime voice in calls on Open WebUI, Hermes and Direct (#842) 2026-10-13 12:05:02 +05:30
assets Add localized release notes for v4.1.9 2026-10-06 09:44:25 +05:30
docs Run the app test suite through combined entrypoints 2026-09-29 00:25:39 +05:30
hermes-src@939e45c91d Fix ten reported issues and vendor Hermes Agent as an API reference (#725) 2026-09-15 00:48:03 +05:30
ios Talk with a realtime voice in calls on Open WebUI, Hermes and Direct (#842) 2026-10-13 12:05:02 +05:30
lib Talk with a realtime voice in calls on Open WebUI, Hermes and Direct (#842) 2026-10-13 12:05:02 +05:30
openwebui-src@15818d88fd Support Open WebUI 0.12 (#843) 2026-10-11 19:46:31 +05:30
packages Talk with a realtime voice in calls on Open WebUI, Hermes and Direct (#842) 2026-10-13 12:05:02 +05:30
pigeons Put every account and connection on one Accounts page, and add them from one sheet (#828) 2026-10-09 22:32:09 +05:30
scripts Redesign the Conduit 4.0 release announcement (#602) 2026-07-31 21:54:47 +05:30
test Talk with a realtime voice in calls on Open WebUI, Hermes and Direct (#842) 2026-10-13 12:05:02 +05:30
third_party Render Mermaid diagrams natively (#585) 2026-07-22 22:53:45 +05:30
tool Talk with a realtime voice in calls on Open WebUI, Hermes and Direct (#842) 2026-10-13 12:05:02 +05:30
.gitignore Run the app test suite through combined entrypoints 2026-09-29 00:25:39 +05:30
.gitmodules Fix ten reported issues and vendor Hermes Agent as an API reference (#725) 2026-09-15 00:48:03 +05:30
.metadata feat: API auth with custom headers 2025-08-16 15:51:27 +05:30
analysis_options.yaml Leave tool/pigeon_codegen out of the root analysis 2026-09-24 10:06:43 +00:00
cargokit_options.yaml build(cargokit): add configuration options file 2026-03-28 20:48:16 +05:30
devtools_options.yaml feat: API auth with custom headers 2025-08-16 15:51:27 +05:30
l10n.yaml feat(l10n): add Polish localization (#617) 2026-08-20 22:12:00 +05:30
LICENSE Update copyright name and link 2026-08-03 15:38:52 +05:30
paseo.json Simplify worktree setup and build instructions 2026-08-24 23:38:52 +05:30
PRIVACY_POLICY.md Add on-device web search for Direct models 2026-09-29 12:26:20 +05:30
pubspec.lock Talk with a realtime voice in calls on Open WebUI, Hermes and Direct (#842) 2026-10-13 12:05:02 +05:30
pubspec.yaml Talk with a realtime voice in calls on Open WebUI, Hermes and Direct (#842) 2026-10-13 12:05:02 +05:30
README.md Add Apple on-device support and improve direct chats (#651) 2026-08-22 23:47:51 +05:30
THIRD_PARTY_NOTICES.md Show models.dev logos as Direct model avatars 2026-09-29 12:43:24 +05:30

Conduit

Conduit icon

A native mobile client for Open WebUI, your own model endpoints, and self-hosted agents.

Latest Release GitHub all downloads License: GPL-3.0

Get it on Google Play Download on the App Store

Vercel OSS Program   cogwheel0/conduit: #1 Dart repository of the day on Trendshift

Connect · Screenshots · Features · Getting Started · Privacy · Build from Source


Conduit demo


Open WebUI is excellent on the desktop. On mobile it breaks down at the edges: authentication behind a reverse proxy, streaming that drops when the app backgrounds, getting a screenshot into a prompt, starting a chat from the home screen. Conduit is a real Flutter app built to close that gap, and as of 4.0 it works with or without an Open WebUI server at all.

Your chats live on your device first. Nothing routes through a backend the maintainer operates.

Ways to connect

On first launch Conduit asks how you want to connect. Pick one, add the others later, and skip any sign-in you don't need.

Open WebUI Your self-hosted server Full feature set: chats, folders, notes, channels, workspace, tools, web search, image generation
Direct OpenAI-compatible, Ollama, OpenRouter Talk straight to a provider or a model on your own machine. No Open WebUI account required
Apple On-Device Apple Intelligence Run Apple's local model offline on eligible iOS 26 devices without an API key
Apple PCC Apple Private Cloud Compute Use Apple's private cloud model on eligible iOS 27 devices without an API key
Hermes Your self-hosted agent An agent that runs tools, asks before sensitive steps, and works on a schedule

Direct connections cover OpenAI-compatible endpoints (Chat Completions or Responses), LM Studio, Azure-style API versions, native Ollama, and first-party OpenRouter. Bring an API key, or skip it for a local endpoint that doesn't want one. Direct connections you already configured in Open WebUI come along automatically. Keys and custom headers stay in platform secure storage.

Apple On-Device is an iOS-only Direct provider backed by Apple's local SystemLanguageModel. It requires iOS 26 and Apple Intelligence, works offline, and supports streaming, sampling controls, and JSON-schema responses with a 4K context window. Image input, reasoning controls, and tool calling are not available.

Apple Private Cloud Compute is an iOS-only Direct provider. It uses Apple's Foundation Models framework, streams responses through the same chat path as other Direct models, and requires iOS 27, Apple Intelligence availability, and Apple's managed PCC entitlement. It supports image input, reasoning levels, sampling and output limits, JSON-schema responses, live quota/context status, and an optional on-device fallback for PCC network failures. Tool calling is not enabled.

Hermes Agent connects to your own Hermes server. You watch its tools work live, approve sensitive steps before they run, and let scheduled agents run while you sleep. Conversations and schedules get their own tab, and Conduit only exposes the capabilities your server actually reports.

Screenshots

Chat Connect Chats Voice
Multimodal chat with an image attachment and streaming response Choose a Conduit chat backend Chat with rich replies and Ask Conduit input Voice call mode listening with call controls

What you get

Chat that survives mobile

Token-by-token streaming over WebSocket. The transcript holds its place while a response grows, pinned prompts stay put, and long conversations load without stalling the UI. Search across conversations, organize with folders, pin what matters, or start a temporary chat that leaves nothing behind.

Rendering that holds up on a phone

Native Flutter surfaces, not a web view wrapped in a shell:

  • syntax-highlighted code blocks with copy and preview
  • Mermaid diagrams rendered natively
  • LaTeX and math
  • expandable reasoning, tool-call, and code-execution sections
  • inline citations, source cards, and follow-up suggestions
  • Chart.js embeds

A real Workspace

Models, knowledge, prompts, tools, and skills as native screens with unified settings navigation. Sections you don't have permission for simply don't appear.

Everything else

Area What's included
Files and media Uploads, re-attaching previously uploaded server files, multimodal prompts, clipboard image paste, audio attachments
Notes Autosave, pinning, AI-generated titles, AI enhancement, audio recording, all available offline
Channels Threads and reactions, when your server enables them
Voice Voice input with on-device or server speech recognition, plus a full voice-call mode
Home screen Widgets on iOS and Android for new chat, mic, camera, photos, and clipboard; app quick actions; iOS App Intents and Shortcuts
Sharing Share-sheet ingestion from other apps straight into a prompt
Terminal Interactive sessions over WebSocket with a file browser, shown only when your server exposes it
Personalization Light, dark, and system themes; five accent palettes; adaptive Material and Cupertino UI; haptics
Languages 14 locales: English, German, Spanish, French, Italian, Japanese, Korean, Dutch, Russian, Simplified and Traditional Chinese, Czech, Slovak, Polish

Server-dependent features (channels, notes, web search, image generation, toggle filters, terminal) appear only when your deployment exposes them.

Built for self-hosted reality

Most mobile clients assume a plain login form. Real deployments rarely look like that.

  • Every auth path Open WebUI offers: username and password, LDAP, manual JWT entry, SSO and OAuth providers.
  • Reverse proxies actually work: oauth2-proxy, Authelia, Authentik, Pangolin, and Cloudflare Tunnel, by capturing the right cookies and session state on-device.
  • Custom headers during setup for environments that require X-API-Key, Authorization, or organization routing headers.
  • Credentials in Keychain or Keystore, never plain-text local storage.
  • Tracks upstream: Conduit supports Open WebUI 0.11.

Getting started

Install from the App Store or Google Play, then pick how you want to connect.

Open WebUI
  1. Launch Conduit and choose Open WebUI.
  2. Enter your instance's base URL.
  3. Add any required custom headers.
  4. Sign in with username and password, LDAP, JWT, SSO, or proxy auth.
  5. Pick a model and start chatting.
Direct connection
  1. Launch Conduit and choose Direct connection.
  2. Add an OpenAI-compatible or Ollama profile with its base URL and any API key or custom headers.
  3. Test the connection, enable it, and select a discovered or manually entered model.
  4. Choose whether new direct chats use Open WebUI history when one is signed in, or stay only on this device. Existing chats keep their current location.
  5. Start chatting. No Open WebUI account required.
Apple On-Device
  1. On an eligible iOS 26 device, launch Conduit and choose Apple On-Device.
  2. Conduit checks Apple Intelligence and local-model availability. No API key or network connection is required.
  3. Start chatting. New Apple-only installations keep Direct history on the device unless you later enable Open WebUI history.
Apple Private Cloud Compute
  1. On an eligible iOS 27 device, launch Conduit and choose Apple Private Cloud Compute.
  2. Conduit checks Apple Intelligence and PCC availability. No API key is required.
  3. Start chatting. New PCC-only installations keep Direct history on the device unless you later enable Open WebUI history.
Hermes Agent
  1. Launch Conduit and choose Hermes Agent.
  2. Enter your Hermes server URL and API_SERVER_KEY.
  3. Optionally set a memory key to scope the agent's long-term memory to you. One is generated automatically on first chat if you leave it blank.
  4. Open the Hermes tab for conversations and scheduled agents.

Privacy

  • Chats, notes, and drafts are stored on your device. Notes and drafts stay available without a connection.
  • Credentials use platform secure storage: Keychain on iOS, Keystore on Android.
  • No third-party analytics or advertising SDKs.
  • Diagnostic logging is local and transient.
  • No developer-operated backend relays your data. Traffic goes from your device to the server or provider you configured, and nowhere else.
  • Signing out lets you choose what stays behind. Clearing everything clears on-device chats too, and fails safely rather than half-deleting.

Full details in PRIVACY_POLICY.md.

Build from source

See docs/BUILDING.md for requirements, submodules, codegen, verification, project layout, and troubleshooting.

git clone --recursive https://github.com/cogwheel0/conduit.git
cd conduit
flutter pub get
dart run build_runner build
flutter run -d ios   # or: flutter run -d android

Clone recursively and run build_runner. The Mermaid renderer is a submodule, and generated Dart files are git-ignored.

Contributing

Conduit is actively developed and feedback is welcome.

Unsolicited pull requests are not the primary contribution path right now. Open an issue or discussion first so changes line up with the roadmap.

Enterprise and white-label

For private distribution, internal deployment support, or a custom enterprise/white-label build, open a discussion or reach the maintainer at cogwheel@cogwheel.app.

Support

If Conduit is useful to you, you can support development through GitHub Sponsors or Buy Me a Coffee.

Acknowledgements

License

Released under the GPL-3.0 License.

Conduit is an independent client and is not affiliated with Open WebUI.