feat(iknowyou): add SSH tunnel script, Synap space header, and hydration fix

- Add tunnel.sh for SSH tunneling to vLLM server (port 8000 is
  firewalled, tunnel required for local dev)
- Add dev:tunnel script to package.json for running dev with tunnel
- Add SYNAP_SPACE env var support to synap.ts, sends X-Memory-Space-Id
  header when set
- Fix SSR hydration mismatch in person-switcher by deferring personId
  render until client mount

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Alan Kahn 2026-02-26 11:15:39 -05:00
parent 51ac377376
commit 8b39409901
4 changed files with 34 additions and 1 deletions

View File

@ -6,10 +6,10 @@ import { useChatStore } from "@/lib/store";
export function PersonSwitcher() { export function PersonSwitcher() {
const personId = useChatStore((s) => s.personId); const personId = useChatStore((s) => s.personId);
const switchPerson = useChatStore((s) => s.switchPerson); const switchPerson = useChatStore((s) => s.switchPerson);
const [mounted, setMounted] = useState(false);
// Defer personId render to avoid SSR/client hydration mismatch // Defer personId render to avoid SSR/client hydration mismatch
// (server generates a fresh UUID, client rehydrates from localStorage) // (server generates a fresh UUID, client rehydrates from localStorage)
const [mounted, setMounted] = useState(false);
useEffect(() => setMounted(true), []); useEffect(() => setMounted(true), []);
const short = mounted ? personId.slice(0, 8) : "\u00A0"; const short = mounted ? personId.slice(0, 8) : "\u00A0";

View File

@ -1,6 +1,7 @@
const SYNAP_URL = const SYNAP_URL =
process.env.SYNAP_URL ?? "https://api.synap.orchard9.ai"; process.env.SYNAP_URL ?? "https://api.synap.orchard9.ai";
const SYNAP_API_KEY = process.env.SYNAP_API_KEY ?? ""; const SYNAP_API_KEY = process.env.SYNAP_API_KEY ?? "";
const SYNAP_SPACE = process.env.SYNAP_SPACE ?? "";
// --- Response types --- // --- Response types ---
@ -68,6 +69,7 @@ async function request<T>(
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json",
Authorization: `Bearer ${SYNAP_API_KEY}`, Authorization: `Bearer ${SYNAP_API_KEY}`,
...(SYNAP_SPACE ? { "X-Memory-Space-Id": SYNAP_SPACE } : {}),
...init?.headers, ...init?.headers,
}, },
}); });

View File

@ -6,6 +6,7 @@
"dev": "next dev -p 59521", "dev": "next dev -p 59521",
"dev:engine": "cargo run -p iknowyou-engine --bin server --features synap-aux", "dev:engine": "cargo run -p iknowyou-engine --bin server --features synap-aux",
"dev:all": "sh -c 'npm run dev:engine & npm run dev'", "dev:all": "sh -c 'npm run dev:engine & npm run dev'",
"dev:tunnel": "./tunnel.sh && next dev -p 59521",
"build": "next build", "build": "next build",
"start": "next start -p 59521", "start": "next start -p 59521",
"lint": "next lint" "lint": "next lint"

30
applications/iknowyou/tunnel.sh Executable file
View File

@ -0,0 +1,30 @@
#!/bin/bash
# Ensure SSH tunnel to msd5685 vLLM (Qwen3-8B) is running on localhost:8000
# Port 8000 is firewalled on the box — tunnel is required.
HOST="msd5685.mjhst.com"
LOCAL_PORT=8000
REMOTE_PORT=8000
SSH_KEY="$HOME/.ssh/id_rsa"
SSH_USER="ubuntu"
# Check if tunnel is already up
if lsof -i ":$LOCAL_PORT" -sTCP:LISTEN &>/dev/null; then
echo "vLLM tunnel already active on localhost:$LOCAL_PORT"
else
echo "Starting SSH tunnel to $HOST..."
ssh -f -N -L "$LOCAL_PORT:localhost:$REMOTE_PORT" \
-i "$SSH_KEY" \
-o StrictHostKeyChecking=no \
-o UserKnownHostsFile=/dev/null \
-o ServerAliveInterval=60 \
-o ServerAliveCountMax=3 \
"$SSH_USER@$HOST"
if lsof -i ":$LOCAL_PORT" -sTCP:LISTEN &>/dev/null; then
echo "vLLM tunnel active → localhost:$LOCAL_PORT"
else
echo "Failed to start tunnel" >&2
exit 1
fi
fi