Tuesday, 23 June 2026

Running Claude on a PowerPC iMac G4 (Debian Ports) A practical guide for chatting with Claude via the API on old PowerPC hardware, where claude.ai itself won't run. Built from a real end-to-end debugging session — every gotcha here actually happened. Why this approach claude.ai needs a modern browser with current JavaScript support. PowerPC Macs (G4, G5) can't run one — TenFourFox-derived browsers (ArcticFox, Spiderweb, etc.) max out well below what the site needs. The fix: skip the browser entirely and talk to the Claude API directly with bash and wget, both of which exist on virtually any Linux system. What you'll need • A PowerPC Mac running a Linux distro (Debian Ports is the realistic choice — there's no official Ubuntu/Lubuntu build for PowerPC) • bash and wget (check with which wget — almost always preinstalled, even on minimal systems) • An Anthropic API key with billing credit added • A second, modern device (phone, laptop, anything with a current browser) — you'll need this to create the API key, since console.anthropic.com won't load on the G4 itself Step 1: Get an API key (on a different device) The Anthropic console is a modern web app and won't load in old PowerPC browsers. Use any other device for this part: 1. Go to console.anthropic.com, sign up or log in 2. Settings → API Keys → Create Key 3. Copy the key immediately — it's only shown once. It looks like sk-ant-api03-... and is over 100 characters long 4. Go to Plans & Billing and add credit (even $5 covers a lot of casual chatting) Save the key as a plain text file if possible — typing/pasting a 100+ character string is the source of most problems in this whole process, and you may need to retype it more than once. Step 2: Get the key onto the iMac Clipboard paste between devices is unreliable on old terminal emulators — budget for this not working on the first try. The most reliable method: 1. Save the key in a plain text file on your other device (e.g. key.txt) 2. Transfer the file to the iMac via USB stick (or email-to-self, if networking allows) 3. On the iMac, load it into an environment variable directly from the file — don't rely on copy/paste into the terminal: ANTHROPIC_API_KEY=$(cat ~/Downloads/key.txt) export ANTHROPIC_API_KEY 4. Verify it actually landed (a real key is 100+ characters): echo "$ANTHROPIC_API_KEY" | wc -c If this shows 1, the variable is empty — the most common cause is a stray character (missing $, accidental backslash) when typing the export line, or running it in a fresh terminal session after the original one closed. Redo it carefully, one line at a time. Important: this variable only lasts for the current terminal session. Logging out, closing the terminal, or rebooting will wipe it. To make it permanent: echo 'export ANTHROPIC_API_KEY="'$(cat ~/Downloads/key.txt)'"' >> ~/.bashrc This writes the export line into your shell startup file, so every new terminal session has it automatically. Step 3: Install curl — or skip it entirely Try this first: sudo apt update && sudo apt install curl On Debian Ports systems mixing old snapshot archives with unstable/sid repos, this very likely fails with dependency conflicts or GPG key errors. Don't fight this with forced/unauthenticated installs — it risks destabilizing the whole system for little benefit. Instead, check for wget, which is almost always already present and works just as well for this purpose: which wget wget --version If wget exists (it almost certainly does), skip curl entirely and use the wget-based script below. Step 4: The chat script Save this as claude-chat.sh: #!/bin/bash # # claude-chat.sh — minimal terminal chat client for the Claude API # Requires only: bash, wget API_KEY="${ANTHROPIC_API_KEY:-}" MODEL="claude-sonnet-4-6" MAX_TOKENS=1024 API_URL="https://api.anthropic.com/v1/messages" if [ -z "$API_KEY" ]; then echo "No API key found." echo "Set it with: export ANTHROPIC_API_KEY=\"sk-ant-your-key-here\"" exit 1 fi HISTORY="" TMP_BODY_FILE="/tmp/claude_chat_body_$$.json" TMP_RESP_FILE="/tmp/claude_chat_resp_$$.json" cleanup() { rm -f "$TMP_BODY_FILE" "$TMP_RESP_FILE"; } trap cleanup EXIT json_escape() { local s="$1" s="${s//\\/\\\\}" s="${s//\"/\\\"}" s="${s//$'\n'/\\n}" s="${s//$'\t'/\\t}" s="${s//$'\r'/\\r}" printf '%s' "$s" } extract_json_string_field() { local json="$1" field="$2" local marker="\"${field}\":\"" local rest="${json#*$marker}" if [ "$rest" = "$json" ]; then printf ''; return; fi local out="" i=0 len=${#rest} c next while [ $i -lt $len ]; do c="${rest:$i:1}" if [ "$c" = '"' ]; then break; fi if [ "$c" = '\' ]; then next="${rest:$((i+1)):1}" case "$next" in n) out="${out}"$'\n'; i=$((i+2)); continue ;; t) out="${out}"$'\t'; i=$((i+2)); continue ;; r) out="${out}"$'\r'; i=$((i+2)); continue ;; \") out="${out}\""; i=$((i+2)); continue ;; \\) out="${out}\\"; i=$((i+2)); continue ;; *) out="${out}${c}"; i=$((i+1)); continue ;; esac fi out="${out}${c}"; i=$((i+1)) done printf '%s' "$out" } extract_text() { extract_json_string_field "$1" "text"; } extract_error() { extract_json_string_field "$1" "message"; } echo "Claude terminal chat (model: $MODEL)" echo "Type 'exit' or 'quit' to leave." echo "---------------------------------------" while true; do printf "\nYou: " IFS= read -r USER_INPUT if [ "$USER_INPUT" = "exit" ] || [ "$USER_INPUT" = "quit" ]; then echo "Goodbye."; break fi if [ -z "$USER_INPUT" ]; then continue; fi ESCAPED_INPUT=$(json_escape "$USER_INPUT") NEW_MSG="{\"role\":\"user\",\"content\":\"${ESCAPED_INPUT}\"}" if [ -z "$HISTORY" ]; then HISTORY="$NEW_MSG"; else HISTORY="${HISTORY},${NEW_MSG}"; fi BODY="{\"model\":\"${MODEL}\",\"max_tokens\":${MAX_TOKENS},\"messages\":[${HISTORY}]}" printf '%s' "$BODY" > "$TMP_BODY_FILE" wget -q -O "$TMP_RESP_FILE" \ --header="x-api-key: ${API_KEY}" \ --header="anthropic-version: 2023-06-01" \ --header="content-type: application/json" \ --post-file="$TMP_BODY_FILE" \ "$API_URL" 2>/tmp/claude_chat_err_$$.log WGET_EXIT=$? rm -f /tmp/claude_chat_err_$$.log if [ ! -s "$TMP_RESP_FILE" ]; then echo "" echo "No response received (wget exit code: $WGET_EXIT)." echo "Check your network connection and API key." continue fi RESPONSE=$(cat "$TMP_RESP_FILE") if printf '%s' "$RESPONSE" | grep -q '"type":"error"'; then ERR_MSG=$(extract_error "$RESPONSE") echo "" echo "Error: ${ERR_MSG:-unknown error}" echo "(raw response below for debugging)" echo "$RESPONSE" continue fi REPLY=$(extract_text "$RESPONSE") if [ -z "$REPLY" ]; then echo "" echo "Got an empty or unparseable response. Raw output:" echo "$RESPONSE" continue fi printf "\nClaude: %s\n" "$REPLY" ESCAPED_REPLY=$(json_escape "$REPLY") HISTORY="${HISTORY},{\"role\":\"assistant\",\"content\":\"${ESCAPED_REPLY}\"}" done Make it executable and run it: chmod +x claude-chat.sh ./claude-chat.sh Troubleshooting reference Symptom Likely cause Fix curl: command not found curl isn't installed Use the wget version of the script above apt fails with dependency/GPG errors Mismatched sid/snapshot repos Don't force it — use wget instead, skip apt Cookie banner won't dismiss on console.anthropic.com Old browser can't render the modern site Use a different device for console access entirely echo "$VAR" prints the literal variable name, not its value Typo broke the $ expansion (missing $, stray backslash, single quotes) Retype the export line carefully, test with a throwaway variable first `echo "$VAR" wc -cshows1` Variable is empty — paste failed silently, or session/terminal changed since export Stuck at a > prompt after a command Unmatched quote or parenthesis Ctrl+C to cancel, retype carefully or split into separate lines wget exit code 8 Server returned an HTTP error status Add --server-response and --content-on-error, save output to a file, and cat it to see the actual JSON error wget error: "Username/Password Authentication failed" This is wget's generic phrasing for HTTP 401 — bad/missing API key Re-check the key is correctly loaded (see the wc -c check above) HTTP 400 with body "Your credit balance is too low..." No billing credit on the account Add credit at console.anthropic.com → Plans & Billing File reappears empty / commands vanish after logging back in /tmp and exported variables don't survive logout/reboot Add the export line to ~/.bashrc so it's set automatically every session JSON test file looks subtly wrong when you cat it (missing }, swapped characters) Manual typing/pasting dropped or altered characters Use nano to write the file directly and check it carefully before using it; rely on file-based input over long pasted one-liners Notes • Conversation history in this script lives in memory only — closing the script loses it. Nothing is saved to disk. • The script makes one HTTP request per message; there's a brief pause while waiting for the full reply (no streaming). • API usage is billed by usage, not a flat subscription — keep half an eye on the Billing section if chatting heavily.

No comments: