Add case study: self-hosted speech-to-text API (EN + ZH)
Deploy / build (push) Successful in 25s

New flagship case study covering a GPU-backed whisper.cpp transcription API
reachable from Windows, iPhone, iPad and Android behind an authenticated
gateway — framed as a service offering with the office-productivity case
(roughly 5x typing throughput, unlimited, audio never leaves the premises).

Content:
- EN + ZH posts (same slug -> auto language switch)
- "Why it matters" opener, hire CTA with clickable WhatsApp + mailto
- Four documented traps: incomplete CUDA component selection, loopback bind
  mistaken for a firewall problem, n8n Code nodes discarding binary + the
  data0 key name, and nginx default.conf hijacking port 80
- Honest scoping of the auth model (access control, not hardened public API)

Assets:
- Custom OG image + 16:9 banner (generator entries appended, not patched
  inside the template-literal maps)

Also marks Mem0 (B1) done and adds B1b to project-state.md.
This commit is contained in:
2026-09-19 07:55:29 +08:00
parent 044ded45b3
commit 9fa366a0ac
7 changed files with 685 additions and 5 deletions
+21
View File
@@ -544,6 +544,27 @@ BANNERS['running-tts-as-a-service-with-token-sidecars'] = {
],
};
BANNERS['self-hosted-speech-to-text-api'] = {
titlebar: 'root@gpu-pc — whisper.cpp',
lines: [
{ t: 'cmd', text: 'netstat -an | grep 20129' },
{ t: 'ok', text: 'TCP 127.0.0.1:20129 LISTENING ← only this PC' },
{ t: 'dim', text: 'iPhone · iPad · Android · work PCs ?' },
{ t: 'cmd', text: '--host 0.0.0.0 + firewall -RemoteAddress LocalSubnet' },
{ t: 'ok', text: 'TCP 0.0.0.0:20129 LISTENING ← reachable' },
{ t: 'cmd', text: 'n8n gate: Authorization header → per-device key' },
{ t: 'err', text: 'bad key → 403' },
{ t: 'hl', text: '{"text":"…"} large-v3 on RTX 3060 · 130 wpm' },
],
flow: [
{ n: '1', label: 'phone dictates' },
{ n: '2', label: 'HTTPS + key' },
{ n: '3', label: 'n8n gate' },
{ n: '4', label: 'GPU transcribe' },
{ n: '5', label: '5x typing ✓' },
],
};
// ---------- read frontmatter ----------
const postPath = join(ROOT, 'src', 'content', 'posts', `${slug}.md`);
let category = 'devops';
+5
View File
@@ -171,6 +171,11 @@ const TERMINALS = {
<div class="line"><span class="prompt">$</span><span class="cmd">swap SATA cable/port · rerun mkfs</span><span class="fix">→ clean · 0 errors ✓</span></div>`,
};
TERMINALS['self-hosted-speech-to-text-api'] = `
<div class="line"><span class="prompt">$</span><span class="cmd">whisper-server --host 0.0.0.0 --port 20129 · large-v3 · RTX 3060</span></div>
<div class="line"><span class="prompt">&nbsp;</span><span class="err">connect ETIMEDOUT 192.168.1.123:20129 — bound to 127.0.0.1 only</span></div>
<div class="line"><span class="prompt">$</span><span class="cmd">bind 0.0.0.0 · firewall LocalSubnet · n8n key gate</span><span class="fix">→ 130 wpm ✓</span></div>`;
const DEFAULT_TERMINAL = `
<div class="line"><span class="prompt">$</span><span class="cmd">engineering · devops · self-hosting</span></div>
<div class="line"><span class="prompt">&nbsp;</span><span class="fix">read the full post →</span></div>`;