← Peanut Gallery Weekly  ·  The companion pamphlet
Pamphlet No. 001 · MMXXVI · MIT license
RTFM
● ISSUED
— Issued with every subscription —

The Operator’s
Manual

How to install the extension, run the wire room on your own hardware, and conscript four critics of your choosing. No support desk. Just this pamphlet.
Install time
~90 sec
From download to first reaction, hosted backend.
Self-host time
~15 min
Node 20, four API keys, one .env.
Pack schema
4 keys
id, persona, prompt, model. That's it.
License
MIT
Fork it. Ship it. Rename it. Don't sue us.
01

Hosted install

The short version. Ninety seconds, hosted backend, pre-wired keys. Start here if you just want to watch something with four hecklers.
Pg. 2 · Circulation
01

Add to Chrome

Open the Chrome Web Store listing, hit Add to Chrome, confirm the manifest permissions. Works in Chromium and Edge too — same file.

02

Pin the side panel

Click the puzzle icon, pin Peanut Gallery, then Ctrl+Shift+P to dock the panel. It stays on while you browse.

03

Open a video or a stream

Load any tab with audio — a livestream, a podcast, a webinar, a Zoom call, a YouTube video. The staff clocks in automatically as soon as the first caption lands. No button, no signup, no modal.

That is the entire install path for the default experience. The hosted backend uses our keys, routes through our server, and costs you nothing. It's rate-limited per installation — if you're a heavy user or you don't trust our pipeline, skip to § 02 and run the whole thing yourself.

Note— Hosted limits —

~4 hours of streaming per day on the shared backend, split across all four critics. Hit the wall, the feed pauses politely and tells you so. Self-host removes the cap — the cap then becomes your API spend.

02

Self-host
the backend

Fifteen minutes, one Node process, four keys, a .env file. Your machine is now the wire room.
Pg. 3 · Pressroom

The backend is a small Node service that brokers between the extension and the four upstream APIs. It exists so your API keys never ship to the browser — and so you can swap models or add caching without touching extension code.

How it's wired

Tab audioany video or stream
DeepgramNova-3 stream
Your
Server
fans out
Transcript
partial
~60 chars · every ~600ms
Baba Booey
Haiku + Brave
~280ms
The Troll
Grok 4.1 Fast
~150ms
F. Norris
Grok 4.1 Fast
~140ms
J. Martling
Claude Haiku
~220ms

Installation

01

Clone & install

You need Node 20+ and git. The server has no database and no state — just a process and four outbound connections.

$ git clone https://github.com/peanut-gallery/server.git
$ cd server
$ npm install
# ~200 deps, 20 seconds on a decent pipe
02

Provide your keys

Copy .env.example to .env and drop in the four keys you collected in § 03. Brave is optional; if you omit it, the fact-checker falls back to model-only claims and flags itself as unverified.

$ cp .env.example .env
$ nano .env
# paste four keys, save, exit
.envDO NOT COMMIT
# Required — transcription DEEPGRAM_API_KEY="dg_live_xxxxxxxxxxxxxxxxxxxxxxxx" # Required — fact-check + comedy (Haiku) ANTHROPIC_API_KEY="sk-ant-xxxxxxxxxxxxxxxxxxxxxxxx" # Required — troll + sound FX (Grok 4.1 Fast) XAI_API_KEY="xai-xxxxxxxxxxxxxxxxxxxxxxxx" # Optional — receipts for the fact-checker BRAVE_SEARCH_API_KEY="BSA-xxxxxxxxxxxxxxxxxxxxxxxx" # Server PORT=8787 ALLOWED_ORIGIN="chrome-extension://<your-extension-id>"
03

Start the press

Dev mode reloads on change; production mode is what you pin to a tmux window or a systemd unit. Either way it binds to one port and listens on localhost by default.

$ npm run dev
› peanut-gallery/server  listening on :8787
› deepgram   ready  (nova-3)
› anthropic  ready  (claude-haiku-4-5)
› xai        ready  (grok-4-fast)
› brave      ready  (search-v1)
› wire room is open. go heckle something.
04

Point the extension at your server

In the extension's Tweaks panel, switch Backend from Hosted to Custom and paste http://localhost:8787. The panel badge flips to SELF-HOSTED when the health-check clears.

Tip— Going public —

If you want to run the server somewhere other than localhost, put it behind a reverse proxy with TLS (Caddy does this in three lines) and restrict ALLOWED_ORIGIN to your extension ID. The extension refuses non-HTTPS custom backends except for localhost.

Watch— Rate limits —

Deepgram bills by streaming minute, Anthropic & xAI by token. A 90-minute podcast at full staff runs roughly $0.18–$0.35 of API. Set usage caps on each provider's dashboard before you walk away from it.

03

Sourcing the keys

Four vendors. Three required; one optional. All four have free tiers enough to test with.
Pg. 5 · Supply
01
Deepgram
console.deepgram.com · Nova-3 streaming · transcription
Required
02
Anthropic
console.anthropic.com · Claude Haiku 4.5 · fact + comedy
Required
03
xAI
console.x.ai · Grok 4.1 Fast · troll + sound FX
Required
04
Brave Search
api.search.brave.com · Search API · fact-check receipts
Optional

Each vendor's signup is roughly the same ritual: create an account, verify email, create an API key, copy it into your .env. Free tiers on all four are enough to get through a few hours of testing — which is enough to decide whether you want to wire in a real card.

If you swap models — say, you prefer Claude Sonnet for the comedy writer or Grok Mini for the troll — the model string lives in each pack file, not the server. See § 04. The server doesn't care which model you pick as long as the vendor is configured.

04

Writing a
persona pack

A pack is a JSON file. Four personas, four prompts, four model picks. That's the whole spec.
Pg. 6 · Casting

Two packs ship by default: the Stern staff (Baba Booey, The Troll, Fred Norris, Jackie Martling) and the TWiST staff (Jason, Molly, Dwight, Ampersand). Everything else you'd ever want is a pack you write yourself.

A note on the rosters. Pack names nod to public media figures. Peanut Gallery is an unofficial work of commentary and parody — not affiliated with, endorsed by, or approved by any of them. Any of those packs can be renamed, swapped, or replaced wholesale; the schema below is the only thing that's load-bearing.

The roles are fixed — fact, dunk, cue, bit — because they pair with specific UI treatments (the tag colors, the sound-cue playback, the receipts badge). The personas filling those roles are entirely up to you.

Schema

FieldTypeDescription
id string Required Lowercase, no spaces. Appears in the pack picker and the URL params. e.g. stern, twist, mst3k.
name string Required Human-readable label shown in the Tweaks menu. Keep it under 28 chars.
tagline string Optional A one-line pitch for the pack picker. Italicized in the UI.
staff array[4] Required Exactly four persona entries, one per role. Extras are ignored; missing roles throw.
staff[].role enum Required One of fact · dunk · cue · bit. Exactly once each.
staff[].persona string Required Display name. Shown on the mug card, the wire tag, and the clipboard export.
staff[].model string Required Vendor-prefixed model ID. anthropic/claude-haiku-4-5, xai/grok-4-fast, etc. The server routes by prefix.
staff[].prompt string Required The full system prompt. ~400 tokens is the sweet spot. Longer = slower. Include voice, cadence, and two or three example reactions in-line.
staff[].temperature number Optional Default 0.7. Trolls & comedy writers like 0.9. Fact-checker wants 0.2.
staff[].cooldown_ms number Optional Minimum gap between this persona's posts. Keeps the troll from monologuing. Default 4000.

Worked example — the mst3k.json pack

packs/mst3k.jsonExample
{ "id": "mst3k", "name": "Mystery Science Theater", "tagline": "Joel, Crow, Tom Servo, and Gypsy. In that order.", "staff": [ { "role": "fact", "persona": "Joel Robinson", "model": "anthropic/claude-haiku-4-5", "temperature": 0.2, "prompt": "You are Joel Robinson. Gentle, curious, prone to..." }, { "role": "dunk", "persona": "Crow T. Robot", "model": "xai/grok-4-fast", "temperature": 0.95, "cooldown_ms": 3000, "prompt": "You are Crow. Acid wit. Short zingers. Never more..." }, { "role": "cue", "persona": "Tom Servo", "model": "xai/grok-4-fast", "prompt": "You are Tom Servo. Operatic. Drops sound cues..." }, { "role": "bit", "persona": "Gypsy", "model": "anthropic/claude-haiku-4-5", "temperature": 0.85, "prompt": "You are Gypsy. Sweetly oblivious. Turns any premise..." } ] }

Sideload the pack

01

Drop the file in the packs folder

Hosted backend: upload via the Packs tab in the extension Tweaks panel. Self-hosted: drop it in ./packs/ on the server and it's picked up on next request — no restart.

02

Validate

Run npm run pack:lint packs/mst3k.json. It checks the schema, pings each model once to confirm the vendor is reachable, and prints token counts for each prompt.

03

Switch the staff

In the extension, open Tweaks → Pack and pick your new entry. The feed clears, the mugs re-render, and the next transcript partial runs through the new cast.

Pro— Prompt craft —

Give each persona three short in-line examples of the shape of reaction you want. "Short zinger under 12 words" > "Be funny." Include a negative example or two: "Don't explain the joke. Don't caveat. Don't apologize." These cost you nothing and save the model from hedging.

05

Daily
operation

Once it's installed, there is almost nothing to do. Here's the nothing.
Pg. 8 · Desk
ActionHowWhat it does
Open the panelCtrl+Shift+PDocks the wire room in Chrome's native side panel. Stays open across tabs.
Swap the packTweaks → PackInstantly retires the current staff and brings in the new one. Feed clears; history kept per-session.
Mute a criticClick the bottom-row pillThat persona stops posting. Other three keep going. Un-mute re-enables mid-stream.
Clip a lineHover a line → clip iconCopies a formatted quote block to clipboard. Includes timestamp, persona, source URL.
Export the feedTweaks → ExportDumps the full session as Markdown. Useful for sharing a particularly brutal broadcast.
Kill the panelTweaks → Off-airExtension stays installed; mic stays off; wire goes dark. Pick it back up later.

The extension does not run on tabs you haven't opened the panel against. There is no background heckling. If the panel is closed, the mic is not listening, the server is not called, and nothing is happening.

06

Trouble &
service

When the wire goes dark, it's almost always one of five things. Check in order.
Pg. 9 · Pressroom
No.
Symptom
Likely cause · fix
01
Panel opens but no reactions ever arrive.
Transcript isn't reaching the server. Check the tab has captions available (CC on the video, or captions enabled in the player). Some livestreams withhold captions for the first ~30 seconds. If captions exist and still nothing, reload the tab with the panel open.
02
Feed starts, stalls after one or two lines.
One of your upstream keys is rejecting. Open the server terminal — the failing vendor prints a single red line. Usually a quota or a billing-card lapse. Top up or swap models.
03
Badge reads SELF-HOSTED · UNREACHABLE.
Extension can't hit /health on your backend. Confirm the server is up (curl http://localhost:8787/health), your ALLOWED_ORIGIN matches the extension ID, and you're not on a different network than you think (corporate VPNs love to rewrite localhost).
04
Reactions are in the wrong voice — generic, hedged, apologetic.
Prompt is being truncated or the model changed. Run npm run pack:lint <pack>.json, check token counts, confirm the model ID still exists at the vendor. Vendors rename models; packs need updating when they do.
05
Fact-checker posts claims without citations.
BRAVE_SEARCH_API_KEY is missing or invalid. Fact-checker falls back to model-only claims when search fails; it flags itself with a small NO RX badge when it does. Add the key or accept the fallback.
Last— Resort —

If none of the above applies, file an issue on the github with your server.log, the pack you're using, and the video URL. We read them. We do not promise to fix them. It's a newspaper run by four AIs.