<?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: Hal Borland</title>
    <description>The latest articles on DEV Community by Hal Borland (@infurno).</description>
    <link>https://dev.to/infurno</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%2F14954%2F676d84d1-b3e5-43c1-8171-0708ba565334.png</url>
      <title>DEV Community: Hal Borland</title>
      <link>https://dev.to/infurno</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/infurno"/>
    <language>en</language>
    <item>
      <title>Why I Stopped Sending My Voice Notes to OpenAI (And Built My Own Engine in Go)</title>
      <dc:creator>Hal Borland</dc:creator>
      <pubDate>Thu, 05 Nov 2026 03:23:22 +0000</pubDate>
      <link>https://dev.to/infurno/why-i-stopped-sending-my-voice-notes-to-openai-and-built-my-own-engine-in-go-g09</link>
      <guid>https://dev.to/infurno/why-i-stopped-sending-my-voice-notes-to-openai-and-built-my-own-engine-in-go-g09</guid>
      <description>&lt;p&gt;A few months ago, I caught myself doing something stupid. &lt;/p&gt;

&lt;p&gt;I was walking down the street, had an idea about a client's server migration, pulled out my phone, and rambled into a popular "AI voice note" app. Ten seconds later, my speech was neatly transcribed into text. Convenient.&lt;/p&gt;

&lt;p&gt;Then I paused and thought about what actually just happened under the hood:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;My phone recorded an uncompressed audio sample of my physical voice.&lt;/li&gt;
&lt;li&gt;The app uploaded that &lt;code&gt;.m4a&lt;/code&gt; file to a public cloud API endpoint.&lt;/li&gt;
&lt;li&gt;The file was queued on someone else's infrastructure, processed through OpenAI's Whisper API, and retained according to their enterprise data logging window.&lt;/li&gt;
&lt;li&gt;The transcribed text was beamed back to my phone.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For a shopping list, fine. For unfiltered thoughts, client system architecture, financial notes, or personal venting? That is an absurd privacy posture.&lt;/p&gt;

&lt;p&gt;Most modern "AI note apps" are nothing more than thin wrappers over third-party speech APIs. Even when they claim "we don't train models on your data," your raw voice—with your biometric tone, background ambient noise, and unfiltered thoughts—is leaving your perimeter and sitting in commercial cloud buckets.&lt;/p&gt;

&lt;p&gt;I decided to stop streaming my voice to OpenAI. Instead, I spent the last few weeks building my own private capture engine using Go, PostgreSQL, and a self-hosted &lt;code&gt;faster-whisper&lt;/code&gt; sidecar running on bare Linux. &lt;/p&gt;

&lt;p&gt;Here is what I learned about the latency tradeoffs, the memory architecture, and why note-taking systems constantly fail.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Latency Myth: Cloud APIs vs. Local CTranslate2
&lt;/h3&gt;

&lt;p&gt;The standard argument for using a cloud speech API is speed and compute capacity. The assumption is that a giant cluster in &lt;code&gt;us-east-1&lt;/code&gt; will transcribe audio much faster than a modest virtual server.&lt;/p&gt;

&lt;p&gt;In practice, that assumption is dead wrong for short voice notes (10 to 60 seconds).&lt;/p&gt;

&lt;p&gt;When you use a cloud API (like OpenAI's &lt;code&gt;v1/audio/transcriptions&lt;/code&gt;), you pay three distinct latency taxes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Network Upload Latency:&lt;/strong&gt; Streaming a multi-megabyte audio file from a mobile phone over cellular LTE/5G to an external API gateway.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queueing Overhead:&lt;/strong&gt; Waiting for a spot in a shared multi-tenant cluster.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;TLS Handshake &amp;amp; Gateway Overhead:&lt;/strong&gt; Establishing connections across multiple corporate boundaries.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here is what actual measurements look like for a 20-second voice memo:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Architecture&lt;/th&gt;
&lt;th&gt;Network + Queue Overhead&lt;/th&gt;
&lt;th&gt;Inference Latency&lt;/th&gt;
&lt;th&gt;Total End-to-End Latency&lt;/th&gt;
&lt;th&gt;Audio Sovereignty&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;OpenAI Whisper API&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;1,200ms – 2,200ms&lt;/td&gt;
&lt;td&gt;~800ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;2,000ms – 3,000ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Stored in third-party buckets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Google Cloud Speech-to-Text&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;900ms – 1,800ms&lt;/td&gt;
&lt;td&gt;~700ms&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;1,600ms – 2,500ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;❌ Processed on Google Cloud&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Self-Hosted &lt;code&gt;faster-whisper&lt;/code&gt; (CPU)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;80ms – 150ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~750ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;~850ms – 900ms&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;✅ Audio never leaves the server&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Running &lt;code&gt;faster-whisper&lt;/code&gt; (using &lt;code&gt;CTranslate2&lt;/code&gt; with INT8 quantization on a modern Linux CPU instance) transcribes a 20-second audio clip in roughly &lt;strong&gt;750 to 800 milliseconds&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Because the audio is uploaded directly to my own web server over an existing HTTP/2 or HTTP/3 connection terminated by Caddy, network overhead drops to under 150ms. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Total time from releasing the mic button to text on screen: under 1 second.&lt;/strong&gt; &lt;/p&gt;

&lt;p&gt;Local inferencing is not slower than the cloud. For voice capture, it is twice as fast.&lt;/p&gt;




&lt;h3&gt;
  
  
  The Stack: Boring, Predictable, and Fast
&lt;/h3&gt;

&lt;p&gt;I wanted zero maintenance overhead. No Kubernetes clusters, no bloated Node microservices, and no multi-gigabyte Docker containers running out of RAM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────────────────────────────────────────────────────────┐
│                   Client (Browser / PWA)               │
│         Audio recorded via Web MediaRecorder API       │
└───────────────────────────┬────────────────────────────┘
                            │ HTTPS / TLS (Caddy)
                            ▼
┌────────────────────────────────────────────────────────┐
│                 Go Application Server                  │
│       - Statically linked binary (~17MB RAM)           │
│       - Session auth &amp;amp; rate limiting                   │
│       - Streaming reverse-proxy to local sidecar       │
└─────────────┬────────────────────────────┬─────────────┘
              │ Local Unix Socket / Port   │ SQL
              ▼                            ▼
┌───────────────────────────────┐ ┌──────────────────────┐
│  faster-whisper Sidecar       │ │      PostgreSQL      │
│  - Python 3 + CTranslate2     │ │  - Notes, tags, auth │
│  - base.en model in RAM       │ │  - Denormalized text │
│  - Audio discarded on output  │ │  - Instant indexing  │
└───────────────────────────────┘ └──────────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  1. The Web Layer (Go)
&lt;/h4&gt;

&lt;p&gt;The core web app is written in Go. A single compiled binary (&lt;code&gt;srv&lt;/code&gt;) that runs as a systemd service, consuming about &lt;strong&gt;14MB of RAM&lt;/strong&gt; under normal load. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Handles user authentication (TOTP 2FA, session tokens).&lt;/li&gt;
&lt;li&gt;Serves lightweight server-rendered HTML templates with htmx.&lt;/li&gt;
&lt;li&gt;Manages incoming audio streams and talk-time quotas.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  2. The Audio Sidecar (&lt;code&gt;faster-whisper&lt;/code&gt;)
&lt;/h4&gt;

&lt;p&gt;Rather than embedding heavy C++ bindings inside the Go process, the transcription worker runs as a dedicated Python sidecar using &lt;code&gt;faster-whisper&lt;/code&gt; with the &lt;code&gt;base.en&lt;/code&gt; model loaded permanently into memory.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It exposes a single internal endpoint on loopback (&lt;code&gt;127.0.0.1:8010&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The Go server forwards the incoming audio stream directly over loopback.&lt;/li&gt;
&lt;li&gt;The sidecar transcribes the audio, returns JSON containing the raw text, and &lt;strong&gt;immediately drops the audio buffer from memory&lt;/strong&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  3. Zero-Retention Memory Hygiene
&lt;/h4&gt;

&lt;p&gt;This was a non-negotiable rule: &lt;strong&gt;temporary audio files must never touch a persistent disk.&lt;/strong&gt;&lt;br&gt;
Many transcription setups write incoming &lt;code&gt;.wav&lt;/code&gt; files to &lt;code&gt;/tmp/&lt;/code&gt;, run a script against them, and rely on a cron job or cleanup routine to delete them later. That leaves a window where unencrypted voice recordings sit on a hard drive.&lt;/p&gt;

&lt;p&gt;In this pipeline:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The audio is received in memory via an &lt;code&gt;io.Reader&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Passed directly to the transcription worker via memory streams.&lt;/li&gt;
&lt;li&gt;Discarded the instant the string is emitted.&lt;/li&gt;
&lt;li&gt;If the server loses power, there is zero audio residue anywhere on the filesystem.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  The Real Problem With Note Apps: Cognitive Stall
&lt;/h3&gt;

&lt;p&gt;Building the transcription pipeline solved the privacy problem, but it exposed a much bigger issue: &lt;strong&gt;how note-taking apps actually work.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Most note systems (Obsidian, Notion, Bear, Apple Notes) make a fundamental mistake: &lt;strong&gt;they force you to organize a thought at the exact moment you have it.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You have an idea while walking or stepping out of a meeting. You open an app. It asks you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Which folder does this go in?&lt;/li&gt;
&lt;li&gt;Which tags should you apply?&lt;/li&gt;
&lt;li&gt;What should the title be?&lt;/li&gt;
&lt;li&gt;Should you link this to an existing daily note?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;By the time you navigate a nested database or pick three tags, the momentum of the thought is gone. This is called &lt;strong&gt;cognitive stall&lt;/strong&gt;. It is why most people end up with dozens of abandoned note systems.&lt;/p&gt;

&lt;p&gt;To make voice capture actually work, you have to decouple capture from review:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Capture Phase (Friction &amp;lt; 2 seconds):&lt;/strong&gt;&lt;br&gt;
One button. Speak or type. No titles, no mandatory folders, no hierarchy. Raw thoughts land in an unformatted chronologic feed.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Review Phase (The Morning Paper):&lt;/strong&gt;&lt;br&gt;
You don't organize notes while taking them. You review them once a day or once a week.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To solve this visually, I built a feature called &lt;strong&gt;Free View&lt;/strong&gt;:&lt;/p&gt;

&lt;p&gt;Instead of scrolling through an endless list of cards, clicking one button transforms your raw notes into an &lt;strong&gt;editorial morning newspaper&lt;/strong&gt;. It uses serif typography, multi-column newsprint rules, drop caps, and automatic layout balancing. &lt;/p&gt;

&lt;p&gt;Reading your thoughts formatted like a printed front page makes synthesis natural. You read what you thought yesterday with your morning coffee, archive what’s done, and export what matters.&lt;/p&gt;




&lt;h3&gt;
  
  
  Keeping Data Portable
&lt;/h3&gt;

&lt;p&gt;The final trap of modern SaaS note apps is proprietary database lock-in. If an app stores your thoughts in a custom format or charges you to export them, you are a hostage, not a customer.&lt;/p&gt;

&lt;p&gt;Every note in this system has three export rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;One-Click Exports:&lt;/strong&gt; Full archives export any time to standard Markdown (&lt;code&gt;.md&lt;/code&gt;), JSON, and plain text.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Secret Share Links:&lt;/strong&gt; If you want to show someone a note, the system generates an unguessable 16-hex read-only URL (&lt;code&gt;/s/&amp;lt;slug&amp;gt;&lt;/code&gt;) with &lt;code&gt;noindex&lt;/code&gt; headers and OpenGraph previews, so it looks like a clean article in Slack or iMessage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No Trackers:&lt;/strong&gt; Zero third-party telemetry, zero analytics scripts, and no marketing tracking cookies.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  What I Shipped
&lt;/h3&gt;

&lt;p&gt;I ended up turning this entire architecture into a production app called &lt;strong&gt;&lt;a href="https://tabbynote.com" rel="noopener noreferrer"&gt;Tabbynote&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;It’s built for anyone who wants instant, voice-first thought capture without handing their raw audio to OpenAI or Google. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stack:&lt;/strong&gt; Go, PostgreSQL, Caddy, &lt;code&gt;faster-whisper&lt;/code&gt; (&lt;code&gt;base.en&lt;/code&gt;), vanilla CSS.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free Plan:&lt;/strong&gt; 100 notes, 30 minutes of voice transcription a month, Free View layout, and all export formats included. No credit card required.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Pro Tier:&lt;/strong&gt; $49/year for unlimited notes, developer theme packs (Catppuccin, Tokyo Night, Dracula), custom monospace typography (JetBrains Mono, Fira Code), and secret share links.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you want to test how fast self-hosted Whisper feels in a real browser, you can try it at &lt;strong&gt;&lt;a href="https://tabbynote.com" rel="noopener noreferrer"&gt;tabbynote.com&lt;/a&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;I'd be interested to hear how other developers are handling the privacy tradeoff with local vs. cloud AI in their own tooling. Are you running local inferencing, or is the convenience of OpenAI’s API still worth the data risk for you?&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>go</category>
      <category>privacy</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Hi, I'm Hal Borland</title>
      <dc:creator>Hal Borland</dc:creator>
      <pubDate>Sat, 03 Jun 2017 18:37:58 +0000</pubDate>
      <link>https://dev.to/infurno/hi-im-hal-borland</link>
      <guid>https://dev.to/infurno/hi-im-hal-borland</guid>
      <description>&lt;p&gt;I have been coding for 20 years.&lt;/p&gt;

&lt;p&gt;You can find me on GitHub as &lt;a href="https://github.com/infurno" rel="noopener noreferrer"&gt;infurno&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I live in Harrison, Arkansas.&lt;/p&gt;

&lt;p&gt;I work for Many People&lt;/p&gt;

&lt;p&gt;I mostly program in these languages: Python, JS, Shell, PHP.&lt;/p&gt;

&lt;p&gt;I am currently learning more about python, Go , Ruby, Rust, .&lt;/p&gt;

&lt;p&gt;Nice to meet you.&lt;/p&gt;

&lt;p&gt;Just a view of where I live. &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh73vja91y2hfnmhqgbqx.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh73vja91y2hfnmhqgbqx.jpg" alt="Alt Text" width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>introduction</category>
    </item>
  </channel>
</rss>
