1:"$Sreact.fragment"
4d:X
52:X
52:C
0:{"buildId":"mtyNzNz9MUZ_ooV9pJSu1","data":[{"rsc":["$","$1","c",{"children":[["$","div",null,{"className":"docs-article-layout","children":[["$","main",null,{"id":"support-content","className":"docs-article","children":[["$","nav",null,{"className":"docs-breadcrumb","aria-label":"Breadcrumb","children":[["$","a",null,{"href":"/support/","children":"Support"}],["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","width":13,"height":13,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-chevron-right","aria-hidden":"true","children":[["$","path","mthhwq",{"d":"m9 18 6-6-6-6"}],"$undefined"]}],["$","span",null,{"children":"Setup & support"}]]}],["$","div",null,{"className":"docs-article-meta","children":[["$","span",null,{"children":"Setup & support"}],["$","span",null,{"children":[["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","width":13,"height":13,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-clock","aria-hidden":"true","children":[["$","circle","1mglay",{"cx":"12","cy":"12","r":"10"}],["$","path","mmk7yg",{"d":"M12 6v6l4 2"}],"$undefined"]}],15," min read"]}]]}],["$","h1",null,{"children":"Set up your Mac & iPhone"}],["$","p",null,{"className":"docs-article-description","children":"The complete setup recipe, with commands for your host and private connection, plus native iPhone speech."}],["$","details",null,{"className":"docs-mobile-toc","children":[["$","summary",null,{"children":"On this page"}],["$","nav",null,{"aria-label":"On this page","children":[["$","a","copy-this-request-to-your-coding-agent",{"href":"#copy-this-request-to-your-coding-agent","className":"","children":"Copy this request to your coding agent"}],["$","a","1-check-what-already-exists",{"href":"#1-check-what-already-exists","className":"","children":"1. Check what already exists"}],["$","a","2-install-dependencies-and-sign-in",{"href":"#2-install-dependencies-and-sign-in","className":"","children":"2. Install dependencies and sign in"}],["$","a","3-create-private-configuration",{"href":"#3-create-private-configuration","className":"","children":"3. Create private configuration"}],["$","a","4-start-the-host",{"href":"#4-start-the-host","className":"","children":"4. Start the host"}],["$","a","keep-it-running-as-a-macos-login-service",{"href":"#keep-it-running-as-a-macos-login-service","className":"docs-toc-nested","children":"Keep it running as a macOS login service"}],["$","a","5-give-the-iphone-a-private-https-host",{"href":"#5-give-the-iphone-a-private-https-host","className":"","children":"5. Give the iPhone a private HTTPS host"}],["$","a","show-a-scannable-connection-code",{"href":"#show-a-scannable-connection-code","className":"docs-toc-nested","children":"Show a scannable connection code"}],["$","a","offer-named-projects-on-the-phone",{"href":"#offer-named-projects-on-the-phone","className":"docs-toc-nested","children":"Offer named projects on the phone"}],["$","a","6-pair-and-test-on-the-phone",{"href":"#6-pair-and-test-on-the-phone","className":"","children":"6. Pair and test on the phone"}],["$","a","troubleshooting",{"href":"#troubleshooting","className":"","children":"Troubleshooting"}],["$","a","optional-host-speech-for-the-browser-companion",{"href":"#optional-host-speech-for-the-browser-companion","className":"","children":"Optional: host speech for the browser companion"}]]}]]}],["$","div",null,{"className":"docs-prose","children":[["$","p","p-0",{"children":["This guide sets up a ",["$","strong","strong-0",{"children":"real Claude host"}]," and a private HTTPS connection through Tailscale. The installed iPhone app transcribes speech and speaks replies on device; it needs no desktop Whisper model or speech server. The iPhone app or hosted browser companion supplies the interface. Neither runs the host for you. Earlier automated tests use disposable services; passing tests does not mean a host is still running."]}],"\n",["$","p","p-1",{"children":["Prefer a visual walkthrough? ","$L2"," shows the desktop handoff, QR page and phone screens. After pairing, ","$L3"," walks through work with screenshots."]}],"\n","$L4","\n","$L5","\n","$L6","\n","$L7","\n","$L8","\n","$L9","\n","$La","\n","$Lb","\n","$Lc","\n","$Ld","\n","$Le","\n","$Lf","\n","$L10","\n","$L11","\n","$L12","\n","$L13","\n","$L14","\n","$L15","\n","$L16","\n","$L17","\n","$L18","\n","$L19","\n","$L1a","\n","$L1b","\n","$L1c","\n","$L1d","\n","$L1e","\n","$L1f","\n","$L20","\n","$L21","\n","$L22","\n","$L23","\n","$L24","\n","$L25","\n","$L26","\n","$L27","\n","$L28","\n","$L29","\n","$L2a","\n","$L2b","\n","$L2c","\n","$L2d","\n","$L2e","\n","$L2f","\n","$L30","\n","$L31","\n","$L32","\n","$L33","\n","$L34","\n","$L35","\n","$L36","\n","$L37","\n","$L38","\n","$L39","\n","$L3a","\n","$L3b","\n","$L3c","\n","$L3d","\n","$L3e","\n","$L3f","\n","$L40","\n","$L41","\n","$L42","\n","$L43","\n","$L44","\n","$L45","\n","$L46","\n","$L47"]}],"$L48"]}],"$L49"]}],["$L4a"],"$L4b"]}],"isPartial":"$@4c","staleTime":"$4d","varyParams":null},{"rsc":"$L4e","isPartial":"$@4f","staleTime":"$4d","varyParams":null},{"rsc":"$L50","isPartial":"$@51","staleTime":"$4d","varyParams":"$52"},{"rsc":"$L53","isPartial":"$@54","staleTime":"$4d","varyParams":null},{"rsc":"$L55","isPartial":"$@56","staleTime":"$4d","varyParams":null}],"isUpgradeableISRFallback":false,"a":"$@57","rootVaryParams":null,"needsRuntimeRequest":"$@58"}
5b:I[97367,["/_next/static/chunks/3fntmmi971322.js"],"OutletBoundary"]
5c:"$Sreact.suspense"
5e:I[97367,["/_next/static/chunks/3fntmmi971322.js"],"ViewportBoundary"]
5f:I[97367,["/_next/static/chunks/3fntmmi971322.js"],"MetadataBoundary"]
60:I[27201,["/_next/static/chunks/3fntmmi971322.js"],"IconMark"]
61:I[39756,["/_next/static/chunks/3fntmmi971322.js"],"default"]
62:I[37457,["/_next/static/chunks/3fntmmi971322.js"],"default"]
63:I[59973,["/_next/static/chunks/3fntmmi971322.js","/_next/static/chunks/2v6icscw2u98x.js"],"SupportNavigation"]
:HL["/_next/static/chunks/0b-w4aydaoki1.css","style"]
:HL["/_next/static/chunks/0uu3n70ekaa_q.css","style"]
2:["$","a","a-0",{"href":"/support/connect-computer/","children":"Connect your computer"}]
3:["$","a","a-1",{"href":"/support/first-conversation/","children":"Your first conversation"}]
4:["$","h2","h2-0",{"id":"copy-this-request-to-your-coding-agent","children":["Copy this request to your coding agent",["$","a",null,{"className":"docs-heading-link","href":"#copy-this-request-to-your-coding-agent","aria-label":"Link to this section","children":"#"}]]}]
59:T972,Set up this Handsfree checkout on my Mac so I can test the real app on my iPhone.
Read docs/LOCAL-SETUP.md, host/README.md and applicable repository instructions.

First inspect existing processes, ports, private host configuration, Claude login,
Tailscale status and Tailscale Serve routes. Reuse a healthy setup. Distinguish a
demo gateway from a real host; do not assume previous test fixtures are running.

I authorize installing the local dependencies, starting the real Claude host, and configuring Tailscale Serve for private HTTPS access.
Start in a disposable project. I understand that HF_ALLOW_UNATTENDED=true permits
Claude tools as my OS user; allowed project roots are not a filesystem sandbox.
Keep credentials, logs and host state outside Git in a private directory.
Use scripts/run-local.mjs with a private Node environment file. Configure separate
gateway/daemon state and exact browser origins. Install a user LaunchAgent so the
host starts at login; preserve any existing services and Tailscale routes.

Help me sign in to Tailscale on both devices using the same account. If an account
requires administrator approval, explain that blocker and help me choose between
approval and a personal account. Let me complete login, consent and OS prompts.
Use Tailscale Serve, never Funnel, and never disable certificate validation.

Verify demo:false health, authenticated WebSocket pairing, a real Claude response,
and confirm the host negotiates clientSpeech.v1 for native iPhone clients. Revoke temporary
test device tokens and rotate a fresh pairing code for me after verification.
Verify HTTPS/WSS through the actual Tailscale hostname once connected. A green
/health alone does not prove Claude works. Do not install host speech for iPhone use.
The phone uses Apple on-device English recognition and its native voice.

Configure named project choices with HF_PROJECTS. Open a fresh desktop QR code
using host/pair.mjs with this host environment and exact HTTPS URL. Give me the
pairing command to refresh it,
the selected project, private configuration/log paths, and start/stop/restart
commands. Walk me through the iPhone health check, pairing, a text request, then
voice. Keep Mac-only verification separate from physical-phone results. Finish
all independent setup work if my login or phone interaction is still pending.
Do not deploy the website or publish changes as part of this local setup.
5:["$","pre","pre-0",{"tabIndex":0,"children":["$","code","code-0",{"className":"language-text","children":"$59"}]}]
6:["$","h2","h2-1",{"id":"1-check-what-already-exists","children":["1. Check what already exists",["$","a",null,{"className":"docs-heading-link","href":"#1-check-what-already-exists","aria-label":"Link to this section","children":"#"}]]}]
7:["$","p","p-2",{"children":"Run these from the checkout root:"}]
8:["$","pre","pre-1",{"tabIndex":0,"children":["$","code","code-0",{"className":"language-sh","children":"node --version\ncommand -v node npm claude\nclaude auth status\nlsof -nP -iTCP:8042 -iTCP:9878 -iTCP:9879 -sTCP:LISTEN\ncurl --fail --max-time 5 http://127.0.0.1:9879/health\n"}]}]
9:["$","p","p-3",{"children":["Node 22+ is required. Port 8042 is the optional local web interface, 9878 the private daemon, and 9879 the gateway. A real gateway returns ",["$","code","code-0",{"children":"{\"ok\":true,\"protocolVersion\":2,\"demo\":false}"}],". ",["$","code","code-1",{"children":"demo:true"}]," is simulated. Health intentionally does not report provider readiness. Inspect the process command and its configuration before replacing a listener; other checkouts may use different ports."]}]
a:["$","p","p-4",{"children":["On macOS, the standalone Tailscale app includes a CLI even if ",["$","code","code-0",{"children":"tailscale"}]," is absent from PATH:"]}]
b:["$","pre","pre-2",{"tabIndex":0,"children":["$","code","code-0",{"className":"language-sh","children":"TS=/Applications/Tailscale.app/Contents/MacOS/Tailscale\n\"$TS\" status\n\"$TS\" serve status\n"}]}]
c:["$","p","p-5",{"children":["If it is already configured, preserve other routes. Do not use ",["$","code","code-0",{"children":"serve reset"}]," to make room for this project."]}]
d:["$","h2","h2-2",{"id":"2-install-dependencies-and-sign-in","children":["2. Install dependencies and sign in",["$","a",null,{"className":"docs-heading-link","href":"#2-install-dependencies-and-sign-in","aria-label":"Link to this section","children":"#"}]]}]
e:["$","pre","pre-3",{"tabIndex":0,"children":["$","code","code-0",{"className":"language-sh","children":"npm ci\nnpm --prefix host ci\nclaude auth status\n# If Claude is not already authenticated:\nclaude auth login\n"}]}]
f:["$","p","p-6",{"children":["Install the ",["$","a","a-0",{"href":"https://tailscale.com/docs/install/mac","children":"standalone Tailscale Mac app"}],", open it and complete the macOS network-extension/VPN prompts. Install Tailscale on the iPhone and sign in with the ",["$","strong","strong-0",{"children":"same account"}],". A work email may belong to an administered tailnet. “User approval required” needs that tailnet's administrator; repeatedly logging in will not fix it. A personal account is an alternative for your own devices."]}]
10:["$","p","p-7",{"children":[["$","strong","strong-0",{"children":"iPhone speech needs no Mac speech dependencies."}]," Allow Microphone and Speech Recognition on the iPhone when you tap Start talking. The app requires on-device English recognition and offers typing when that is unavailable. Browser voice is a separate, optional setup described below."]}]
11:["$","h2","h2-3",{"id":"3-create-private-configuration","children":["3. Create private configuration",["$","a",null,{"className":"docs-heading-link","href":"#3-create-private-configuration","aria-label":"Link to this section","children":"#"}]]}]
12:["$","p","p-8",{"children":["The following block creates a new setup and ",["$","strong","strong-0",{"children":"refuses to overwrite"}]," an existing environment file. Run from the checkout root. If a setup already exists, inspect and update it deliberately instead."]}]
13:["$","pre","pre-4",{"tabIndex":0,"children":["$","code","code-0",{"className":"language-sh","children":"python3 - <<'PY'\nfrom pathlib import Path\nbase = Path.home() / '.handsfree-local'\nbase.mkdir(mode=0o700, exist_ok=True)\nfor name in ['project', 'gateway', 'agent', 'logs']:\n    (base / name).mkdir(mode=0o700, exist_ok=True)\nvalues = {\n    'HF_ALLOW_UNATTENDED': 'true',\n    'HOST': '127.0.0.1',\n    'PORT': '9879',\n    'AGENTD_PORT': '9878',\n    'AGENTD_URL': 'ws://127.0.0.1:9878',\n    'WORKDIR': str(base / 'project'),\n    'HF_ALLOWED_ROOTS': str(base / 'project'),\n    'HF_DATA_DIR': str(base / 'gateway'),\n    'AGENTD_DATA_DIR': str(base / 'agent'),\n    'AGENTD_PID_FILE': str(base / 'agent.pid'),\n    'HF_ALLOWED_ORIGINS': 'http://localhost:8042,http://127.0.0.1:8042,https://ioshandsfree.vercel.app,capacitor://localhost',\n    'HF_DISABLE_TITLES': 'true',\n}\nconfig = base / 'host.env'\nwith config.open('x') as f:\n    config.chmod(0o600)\n    for key, value in values.items():\n        f.write(f'{key}=\"{value}\"\\n')\nprint(config)\nPY\n"}]}]
14:["$","p","p-9",{"children":[["$","code","code-0",{"children":"HF_ALLOW_UNATTENDED=true"}]," authorizes Claude's tools under your OS account. ",["$","code","code-1",{"children":"HF_ALLOWED_ROOTS"}]," restricts session directories, ",["$","strong","strong-0",{"children":"not everything a tool can access"}],". This guide starts in a disposable directory. To work on another project, stop the host, change ",["$","code","code-2",{"children":"WORKDIR"}]," and ",["$","code","code-3",{"children":"HF_ALLOWED_ROOTS"}]," to the intended absolute paths, restart, and select a new session. Existing sessions retain their original directories. Multiple roots use ",["$","code","code-4",{"children":":"}]," on macOS."]}]
15:["$","p","p-10",{"children":"The supervisor generates a shared daemon token in memory on every launch. Nothing needs to be copied to the phone except the short-lived pairing code. Keep provider keys and durable device tokens out of Git and support tickets."}]
16:["$","h2","h2-4",{"id":"4-start-the-host","children":["4. Start the host",["$","a",null,{"className":"docs-heading-link","href":"#4-start-the-host","aria-label":"Link to this section","children":"#"}]]}]
17:["$","pre","pre-5",{"tabIndex":0,"children":["$","code","code-0",{"className":"language-sh","children":"node --env-file=\"$HOME/.handsfree-local/host.env\" scripts/run-local.mjs\n"}]}]
18:["$","p","p-11",{"children":["This starts the gateway/daemon supervisor. Speech stays on the iPhone; the launcher starts a Mac speech helper only when ",["$","code","code-0",{"children":"HF_HOST_SPEECH=true"}]," is explicitly configured. It prints a random single-use pairing code valid for ten minutes. ",["$","code","code-1",{"children":"Ctrl-C"}]," stops all services. The launcher forces the gateway onto ",["$","code","code-2",{"children":"127.0.0.1"}],"; it fails if the chosen gateway or daemon port is occupied. Set both ",["$","code","code-3",{"children":"AGENTD_PORT"}]," and ",["$","code","code-4",{"children":"AGENTD_URL"}]," consistently if changing ports."]}]
19:["$","p","p-12",{"children":["For a quick Mac-only check, start ",["$","code","code-0",{"children":"npm run dev"}]," in another terminal (or reuse the interface already on 8042). Open ",["$","a","a-0",{"href":"http://localhost:8042","children":"localhost:8042"}],". The desktop page checks the standard gateway at ",["$","code","code-1",{"children":"localhost:9879"}]," and connects directly; no QR code or pairing command is needed. This shortcut is accepted only when both the page and network connection are loopback. If the host uses a nonstandard port, choose ",["$","strong","strong-0",{"children":"Connect to a different host"}]," and enter its details. An iPhone cannot use the Mac's loopback address."]}]
1a:["$","p","p-13",{"children":["For deeper optional provider verification, ",["$","code","code-0",{"children":"node scripts/test-real-voice.mjs --run-real-claude"}]," exercises actual speech and Claude in isolated temporary services. That test does not verify the running host or your Tailscale route; also test those directly."]}]
1b:["$","h3","h3-0",{"id":"keep-it-running-as-a-macos-login-service","children":["Keep it running as a macOS login service",["$","a",null,{"className":"docs-heading-link","href":"#keep-it-running-as-a-macos-login-service","aria-label":"Link to this section","children":"#"}]]}]
1c:["$","p","p-14",{"children":["Stop the foreground launcher first. This creates a user LaunchAgent with the current Node executable and PATH. It starts at user login and runs after Terminal closes. LaunchAgent itself does not restart a failed launcher. The host supervisor may restart its own children; consult ",["$","code","code-0",{"children":"host/README.md"}]," for its current recovery behavior and review uncertain work before resubmitting it. No root service is installed."]}]
5a:T483,python3 - <<'PY'
from pathlib import Path
import os, plistlib, shutil
root = Path.cwd()
base = Path.home() / '.handsfree-local'
node = shutil.which('node')
assert node and (root / 'scripts/run-local.mjs').is_file()
assert (base / 'host.env').is_file()
for name in ['host.log', 'host-error.log']:
    p = base / 'logs' / name
    p.touch(mode=0o600, exist_ok=True)
    p.chmod(0o600)
config = {
    'Label': 'com.handsfree.local',
    'ProgramArguments': [node, '--env-file=' + str(base / 'host.env'),
                         str(root / 'scripts/run-local.mjs')],
    'WorkingDirectory': str(root),
    'EnvironmentVariables': {'PATH': os.environ['PATH']},
    'RunAtLoad': True,
    'KeepAlive': False,
    'ExitTimeOut': 15,
    'ProcessType': 'Background',
    'StandardOutPath': str(base / 'logs/host.log'),
    'StandardErrorPath': str(base / 'logs/host-error.log'),
}
p = Path.home() / 'Library/LaunchAgents/com.handsfree.local.plist'
p.parent.mkdir(parents=True, exist_ok=True)
with p.open('xb') as f:
    plistlib.dump(config, f)
p.chmod(0o600)
print(p)
PY
launchctl bootstrap "gui/$(id -u)" "$HOME/Library/LaunchAgents/com.handsfree.local.plist"
1d:["$","pre","pre-6",{"tabIndex":0,"children":["$","code","code-0",{"className":"language-sh","children":"$5a"}]}]
1e:["$","p","p-15",{"children":"Inspect status and read the current pairing code locally:"}]
1f:["$","pre","pre-7",{"tabIndex":0,"children":["$","code","code-0",{"className":"language-sh","children":"launchctl print \"gui/$(id -u)/com.handsfree.local\"\ncurl --fail http://127.0.0.1:9879/health\ntail -20 \"$HOME/.handsfree-local/logs/host.log\"\ntail -20 \"$HOME/.handsfree-local/logs/host-error.log\"\n"}]}]
20:["$","p","p-16",{"children":"Rotate a fresh code without interrupting active work (then read the last printed pairing-code line):"}]
21:["$","pre","pre-8",{"tabIndex":0,"children":["$","code","code-0",{"className":"language-sh","children":"launchctl kill SIGUSR1 \"gui/$(id -u)/com.handsfree.local\"\ntail -f \"$HOME/.handsfree-local/logs/host.log\"\n"}]}]
22:["$","p","p-17",{"children":[["$","code","code-0",{"children":"Ctrl-C"}]," exits log viewing; the service stays running. Logs contain conversation excerpts and fresh codes, so keep them private. Codes expire after ten minutes and work once. Browser reloads require pairing again; the native app retains its credential in Keychain."]}]
23:["$","p","p-18",{"children":"Stop, then start again after reviewing active work:"}]
24:["$","pre","pre-9",{"tabIndex":0,"children":["$","code","code-0",{"className":"language-sh","children":"launchctl bootout \"gui/$(id -u)/com.handsfree.local\"\n# After shutdown completes:\nlaunchctl bootstrap \"gui/$(id -u)\" \"$HOME/Library/LaunchAgents/com.handsfree.local.plist\"\n"}]}]
25:["$","p","p-19",{"children":["Stopping interrupts any running agent turn. To start an installed but crashed service, use ",["$","code","code-0",{"children":"launchctl kickstart \"gui/$(id -u)/com.handsfree.local\""}],". After moving the checkout or removing the pinned Node version, unload and update the LaunchAgent paths before loading it again. To remove automatic startup, unload it and remove only ",["$","code","code-1",{"children":"com.handsfree.local.plist"}],"; preserve the private state unless you intend to erase histories and device pairings."]}]
26:["$","h2","h2-5",{"id":"5-give-the-iphone-a-private-https-host","children":["5. Give the iPhone a private HTTPS host",["$","a",null,{"className":"docs-heading-link","href":"#5-give-the-iphone-a-private-https-host","aria-label":"Link to this section","children":"#"}]]}]
27:["$","p","p-20",{"children":["Once ",["$","code","code-0",{"children":"tailscale status"}]," shows the correct connected account, inspect existing routes and then add this gateway:"]}]
28:["$","pre","pre-10",{"tabIndex":0,"children":["$","code","code-0",{"className":"language-sh","children":"TS=/Applications/Tailscale.app/Contents/MacOS/Tailscale\n\"$TS\" status\n\"$TS\" serve status\n\"$TS\" serve --bg --https=443 http://127.0.0.1:9879\n\"$TS\" serve status\n"}]}]
29:["$","p","p-21",{"children":[["$","a","a-0",{"href":"https://tailscale.com/docs/features/tailscale-serve","children":"Tailscale Serve"}]," shares the gateway privately inside the tailnet and handles HTTPS. If it prints an enablement URL, complete the HTTPS approval there and rerun the command. Keep MagicDNS enabled. Do not use Funnel, which publishes a service to the internet. If HTTPS port 443 already has a route, preserve it and choose an unused Serve HTTPS port such as 8443; include ",["$","code","code-0",{"children":":8443"}]," in the app's host URL. See the ",["$","a","a-1",{"href":"https://tailscale.com/docs/reference/tailscale-cli/serve","children":"Serve CLI reference"}]," for current options."]}]
2a:["$","p","p-22",{"children":["The enablement page may preselect optional Funnel: ",["$","strong","strong-0",{"children":"uncheck Funnel before enabling HTTPS"}],". HTTPS certificates place the device hostname in public certificate-transparency logs; the service itself remains private. First certificate issuance can take a minute: if ",["$","code","code-0",{"children":"tailscale status"}]," says it is fetching a TLS certificate, wait and retry the health request without bypassing validation."]}]
2b:["$","p","p-23",{"children":["Serve prints a URL shaped like ",["$","code","code-0",{"children":"https://your-mac.your-tailnet.ts.net"}],". Use the exact printed origin; do not invent a hostname or use the raw Tailscale IP with HTTPS. The certificate must match the hostname. Serve proxies the entire gateway including ",["$","code","code-1",{"children":"/pair"}],", ",["$","code","code-2",{"children":"/health"}],", and WebSocket upgrades at ",["$","code","code-3",{"children":"/ws"}],". Leave the daemon and speech listeners private on loopback."]}]
2c:["$","pre","pre-11",{"tabIndex":0,"children":["$","code","code-0",{"className":"language-sh","children":"# Replace the example with the exact URL printed by Serve:\ncurl --fail --max-time 15 https://your-mac.your-tailnet.ts.net/health\n"}]}]
2d:["$","p","p-24",{"children":[["$","code","code-0",{"children":"--bg"}]," persists the Serve route independently of the host process. It does not keep the Mac awake or launch Handsfree. To remove only this setup's route, use ",["$","code","code-1",{"children":"\"$TS\" serve --https=443 off"}]," (or your chosen port), only after confirming that route still belongs to Handsfree."]}]
2e:["$","h3","h3-1",{"id":"show-a-scannable-connection-code","children":["Show a scannable connection code",["$","a",null,{"className":"docs-heading-link","href":"#show-a-scannable-connection-code","aria-label":"Link to this section","children":"#"}]]}]
2f:["$","p","p-25",{"children":"With the updated host running, run this from the repository on your Mac. Replace the example with the exact HTTPS origin from Serve:"}]
30:["$","pre","pre-12",{"tabIndex":0,"children":["$","code","code-0",{"className":"language-sh","children":"node --env-file=\"$HOME/.handsfree-local/host.env\" host/pair.mjs --url https://your-mac.your-tailnet.ts.net\n"}]}]
31:["$","p","p-26",{"children":["A local connection page opens with a large QR code. On the phone, tap ",["$","strong","strong-0",{"children":"Scan my computer’s code"}],". Both the long hostname and pairing code are supplied automatically. Scanning does not replace the private network connection: keep both devices connected to Tailscale."]}]
32:["$","p","p-27",{"children":"The helper remembers the address privately. To refresh an expired or used code, run:"}]
33:["$","pre","pre-13",{"tabIndex":0,"children":["$","code","code-0",{"className":"language-sh","children":"node --env-file=\"$HOME/.handsfree-local/host.env\" host/pair.mjs\n"}]}]
34:["$","p","p-28",{"children":["Each code works once and lasts ten minutes. Refreshing it preserves existing pairings and active work. The QR page is a private file in ",["$","code","code-0",{"children":"HF_DATA_DIR"}],", not a public website. It contains no durable token or provider keys. Use the same environment file as the running host. ",["$","code","code-1",{"children":"--no-open"}]," generates the page without opening it. Alternatively, set ",["$","code","code-2",{"children":"HF_PUBLIC_URL"}]," in the host environment; that setting takes precedence over the remembered address."]}]
35:["$","p","p-29",{"children":[["$","strong","strong-0",{"children":"Update both the iPhone app and host for native speech."}]," After pulling the updated checkout, wait for active work to finish and restart the host service. The iPhone checks for ",["$","code","code-0",{"children":"clientSpeech.v1"}]," and shows an update message if the host is too old. Pairing records and sessions are retained. Older running hosts also need an update for the QR helper. Wait for active work to finish before restarting. An older installed iPhone build can use the page's manual details; the new in-app scanner requires the updated native build. The current web companion supports camera scanning too."]}]
36:["$","h3","h3-2",{"id":"offer-named-projects-on-the-phone","children":["Offer named projects on the phone",["$","a",null,{"className":"docs-heading-link","href":"#offer-named-projects-on-the-phone","aria-label":"Link to this section","children":"#"}]]}]
37:["$","p","p-30",{"children":["Open Handsfree on the computer at ",["$","code","code-0",{"children":"http://localhost:8042"}],", open ",["$","strong","strong-0",{"children":"Projects"}]," in the sidebar (or ",["$","strong","strong-1",{"children":"Host connection → Manage projects"}],"), and select ",["$","strong","strong-2",{"children":"Browse…"}],". Add, rename, remove, or reorder the folders that should appear on the phone. Changes are saved in the private host data directory and sent to connected phones immediately; the host does not need to restart. Removing a project only removes the choice—it never deletes files or conversations."]}]
38:["$","p","p-31",{"children":["Only the local browser on that computer can manage the catalog. A phone or remotely paired browser can select approved projects but cannot add, rename, reorder, or remove them. On macOS, ",["$","strong","strong-0",{"children":"Browse…"}]," opens the native folder picker. An absolute path field remains available for headless or non-macOS setup."]}]
39:["$","p","p-32",{"children":["For scripted first-run setup, ",["$","code","code-0",{"children":"HF_PROJECTS"}]," remains available as a bootstrap value. Add a JSON array to the private ",["$","code","code-1",{"children":"host.env"}],", using real folders within ",["$","code","code-2",{"children":"HF_ALLOWED_ROOTS"}],":"]}]
3a:["$","pre","pre-14",{"tabIndex":0,"children":["$","code","code-0",{"className":"language-sh","children":"HF_PROJECTS='[{\"name\":\"Website\",\"path\":\"/Users/you/Projects/website\"},{\"name\":\"iPhone app\",\"path\":\"/Users/you/Projects/mobile-app\"}]'\n"}]}]
3b:["$","p","p-33",{"children":["Restart the host after changing its environment. On a new host data directory, these entries seed the persistent catalog. Without ",["$","code","code-0",{"children":"HF_PROJECTS"}],", the catalog starts with the default folder plus accessible folders from saved sessions. Later edits should use the desktop Projects screen. One project skips the picker. Configuring a project does not clone files or create an isolated checkout."]}]
3c:["$","h2","h2-6",{"id":"6-pair-and-test-on-the-phone","children":["6. Pair and test on the phone",["$","a",null,{"className":"docs-heading-link","href":"#6-pair-and-test-on-the-phone","aria-label":"Link to this section","children":"#"}]]}]
3d:["$","ol","ol-0",{"children":["\n",["$","li","li-0",{"children":["Connect Tailscale on both devices using the same account. Keep the Mac awake and online; a powered sleeping Mac may still be unreachable. For a temporary test, run ",["$","code","code-0",{"children":"caffeinate -i"}]," in a Mac terminal and leave it open, with the lid open."]}],"\n",["$","li","li-1",{"children":["In iPhone Safari, open the exact host HTTPS URL with ",["$","code","code-0",{"children":"/health"}]," appended. Expect ",["$","code","code-1",{"children":"demo:false"}]," and no certificate warning. Fix routing, VPN, DNS or TLS before trying codes if this fails."]}],"\n",["$","li","li-2",{"children":["Open Handsfree and tap ",["$","strong","strong-0",{"children":"Scan my computer’s code"}]," to scan the desktop QR. Manual entry is available under ",["$","strong","strong-1",{"children":"Enter details instead"}],"; use the host origin ",["$","strong","strong-2",{"children":["without ",["$","code","code-0",{"children":"/health"}]]}],". Allow local-network access if prompted."]}],"\n",["$","li","li-3",{"children":["Choose ",["$","strong","strong-0",{"children":"New session"}],", select the disposable project, then ",["$","strong","strong-1",{"children":"Type instead"}]," and send: “Reply with exactly phone connection confirmed. Do not use tools.” Check the actual response."]}],"\n",["$","li","li-4",{"children":["Allow microphone and Speech Recognition access, try ",["$","strong","strong-0",{"children":"Record once → Finish recording"}],", then explicitly tap ",["$","strong","strong-1",{"children":"Start talking"}],". Confirm that your iPhone transcribes, the host receives text, and the iPhone speaks the reply. No recordings should reach the host."]}],"\n",["$","li","li-5",{"children":"Keep Handsfree open. Its screen stays awake while talking or speaking. Test Done for now, Stop agent and returning after another app: listening pauses while away and resumes only when you tap Start talking. Recognition quality remains a physical-iPhone check."}],"\n"]}]
3e:["$","p","p-34",{"children":["The ",["$","a","a-0",{"href":"https://ioshandsfree.vercel.app/","children":"hosted browser companion"}]," uses the same HTTPS host but its own fresh pairing. Its exact origin is included in the example environment. Allow the browser's Local Network Access permission if prompted. Both clients are designed for foreground use. Browser voice still requires the optional host speech setup below; browser typing does not."]}]
3f:["$","h2","h2-7",{"id":"troubleshooting","children":["Troubleshooting",["$","a",null,{"className":"docs-heading-link","href":"#troubleshooting","aria-label":"Link to this section","children":"#"}]]}]
40:["$","div","table-0",{"className":"docs-table-scroll","role":"region","aria-label":"Comparison table","tabIndex":0,"children":["$","table",null,{"children":[["$","thead","thead-0",{"children":["$","tr","tr-0",{"children":[["$","th","th-0",{"children":"Symptom"}],["$","th","th-1",{"children":"Next check"}]]}]}],["$","tbody","tbody-0",{"children":[["$","tr","tr-0",{"children":[["$","td","td-0",{"children":"No listener / connection refused"}],["$","td","td-1",{"children":"Inspect the LaunchAgent and private error log; install dependencies and verify Node paths."}]]}],["$","tr","tr-1",{"children":[["$","td","td-0",{"children":"Address already in use"}],["$","td","td-1",{"children":["Identify the owner with ",["$","code","code-0",{"children":"lsof"}],"; reuse it or choose different ports, rather than killing arbitrary processes."]}]]}],["$","tr","tr-2",{"children":[["$","td","td-0",{"children":"Health works but Claude never replies"}],["$","td","td-1",{"children":["Check ",["$","code","code-0",{"children":"claude auth status"}],", daemon port/token configuration and private error log. Health checks the gateway only."]}]]}],["$","tr","tr-3",{"children":[["$","td","td-0",{"children":"An existing store requires migration provenance"}],["$","td","td-1",{"children":["For a store known to come from the earlier real Claude host, add ",["$","code","code-0",{"children":"HF_LEGACY_STORE_MODE=claude"}]," to its private environment and restart. Use ",["$","code","code-1",{"children":"demo"}]," only for known demo state; never guess. The migration backs up the original store."]}]]}],["$","tr","tr-4",{"children":[["$","td","td-0",{"children":"“User approval required” in Tailscale"}],["$","td","td-1",{"children":"Obtain tailnet admin approval or deliberately switch both devices to a personal account."}]]}],["$","tr","tr-5",{"children":[["$","td","td-0",{"children":"Safari cannot open host health"}],["$","td","td-1",{"children":"Confirm both Tailscale connections, correct hostname/Serve port, HTTPS enablement, tailnet access rules and Mac wakefulness."}]]}],["$","tr","tr-6",{"children":[["$","td","td-0",{"children":"Pairing rejected"}],["$","td","td-1",{"children":"Generate another code; use it within ten minutes, once. Never paste a demo code into the real host."}]]}],["$","tr","tr-7",{"children":[["$","td","td-0",{"children":"Web works locally but hosted companion fails"}],["$","td","td-1",{"children":"Use HTTPS/WSS and the exact origin allowlist; allow the browser's local-network permission."}]]}],["$","tr","tr-8",{"children":[["$","td","td-0",{"children":"Text works but transcription fails"}],["$","td","td-1",{"children":"On iPhone, check Microphone and Speech Recognition permissions and on-device English availability. For browser voice, check the optional host speech service."}]]}],["$","tr","tr-9",{"children":[["$","td","td-0",{"children":"Speech response is silent"}],["$","td","td-1",{"children":"On iPhone, check volume and the selected output route; replies use the iPhone voice. For browser voice, check host TTS and browser playback permissions."}]]}],["$","tr","tr-10",{"children":[["$","td","td-0",{"children":"Setup stopped after reboot/logout/sleep"}],["$","td","td-1",{"children":"A LaunchAgent starts at user login, and a sleeping Mac cannot reliably serve the phone. Check Tailscale also reconnects."}]]}]]}]]}]}]
41:["$","p","p-35",{"children":["Do not share ",["$","code","code-0",{"children":".env"}]," files, pairing codes, tokens, full logs or private project contents in support reports. Report separately whether loopback health, Tailscale HTTPS health, authenticated agent execution and physical-phone voice passed."]}]
42:["$","h2","h2-8",{"id":"optional-host-speech-for-the-browser-companion","children":["Optional: host speech for the browser companion",["$","a",null,{"className":"docs-heading-link","href":"#optional-host-speech-for-the-browser-companion","aria-label":"Link to this section","children":"#"}]]}]
43:["$","p","p-36",{"children":"Skip this section for the installed iPhone app. The browser still sends recordings to the host for transcription and can receive host-generated voice audio. To use that path on a Mac:"}]
44:["$","pre","pre-15",{"tabIndex":0,"children":["$","code","code-0",{"className":"language-sh","children":"brew install whisper-cpp ffmpeg\nmkdir -p \"$HOME/.handsfree-local/models\"\ncurl --fail --location --retry 3 \\\n  https://huggingface.co/ggerganov/whisper.cpp/resolve/main/ggml-base.en.bin \\\n  -o \"$HOME/.handsfree-local/models/ggml-base.en.bin\"\n"}]}]
45:["$","p","p-37",{"children":["Add these entries to your existing private ",["$","code","code-0",{"children":"host.env"}]," (use your actual absolute path):"]}]
46:["$","pre","pre-16",{"tabIndex":0,"children":["$","code","code-0",{"className":"language-sh","children":"HF_HOST_SPEECH=true\nHF_WHISPER_MODEL=/Users/you/.handsfree-local/models/ggml-base.en.bin\n"}]}]
47:["$","p","p-38",{"children":["Restart the service after active work finishes. The launcher now starts its loopback speech helper: whisper.cpp ",["$","code","code-0",{"children":"base.en"}]," for recognition and macOS ",["$","code","code-1",{"children":"say"}]," with Samantha for replies. This helper is for personal local use; expose only the authenticated gateway through Tailscale. Custom ",["$","code","code-2",{"children":"WHISPER_URL"}]," / ",["$","code","code-3",{"children":"TTS_URL"}]," services are described in the host reference. Native iPhone connections continue to use device speech even when browser speech is enabled."]}]
48:["$","nav",null,{"className":"docs-pagination","aria-label":"Article navigation","children":[["$","a",null,{"href":"/support/first-conversation/","children":[["$","small",null,{"children":[["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","width":13,"height":13,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-arrow-left","aria-hidden":"true","children":[["$","path","1l729n",{"d":"m12 19-7-7 7-7"}],["$","path","x3x0zl",{"d":"M19 12H5"}],"$undefined"]}]," Previous article"]}],["$","strong",null,{"children":"Your first conversation"}]]}],["$","a",null,{"href":"/support/connection-help/","className":"docs-next","children":[["$","small",null,{"children":["Next article ",["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","width":13,"height":13,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-arrow-right","aria-hidden":"true","children":[["$","path","1ays0h",{"d":"M5 12h14"}],["$","path","xquz4c",{"d":"m12 5 7 7-7 7"}],"$undefined"]}]]}],["$","strong",null,{"children":"Connection & troubleshooting"}]]}]]}]
49:["$","aside",null,{"className":"docs-toc","children":[["$","p",null,{"children":"On this page"}],"$0:data:0:rsc:props:children:0:props:children:0:props:children:4:props:children:1"]}]
4a:["$","script","script-0",{"src":"/_next/static/chunks/2bw5iof_b--vu.js","async":true}]
4b:["$","$L5b",null,{"children":["$","$5c",null,{"name":"Next.MetadataOutlet","children":"$@5d"}]}]
4e:["$","$1","h",{"children":[null,["$","$L5e",null,{"children":[["$","meta","0",{"charSet":"utf-8"}],["$","meta","1",{"name":"viewport","content":"width=device-width, initial-scale=1, viewport-fit=cover"}],["$","meta","2",{"name":"theme-color","content":"#f7f8f5"}]]}],["$","div",null,{"hidden":true,"children":["$","$L5f",null,{"children":["$","$5c",null,{"name":"Next.Metadata","children":[["$","title","0",{"children":"Set up your Mac & iPhone · Handsfree support"}],["$","meta","1",{"name":"description","content":"The complete setup recipe, with commands for your host and private connection, plus native iPhone speech."}],["$","meta","2",{"name":"application-name","content":"Handsfree"}],["$","link","3",{"rel":"icon","href":"/icon.svg"}],["$","link","4",{"rel":"apple-touch-icon","href":"/icon.svg"}],["$","$L60","5",{}]]}]}]}],null]}]
50:["$","$1","c",{"children":[null,["$","$L61",null,{"parallelRouterKey":"children","template":["$","$L62",null,{}]}]]}]
53:["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/0b-w4aydaoki1.css","precedence":"next"}],["$","script","script-0",{"src":"/_next/static/chunks/2v6icscw2u98x.js","async":true}]],["$","div",null,{"className":"docs-shell","children":[["$","a",null,{"className":"docs-skip","href":"#support-content","children":"Skip to content"}],["$","header",null,{"className":"docs-header","children":[["$","a",null,{"href":"/support/","className":"docs-brand","aria-label":"Handsfree support home","children":[["$","span",null,{"children":["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","width":22,"height":22,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-audio-lines","aria-hidden":"true","children":[["$","path","1fnikh",{"d":"M2 10v3"}],["$","path","11sgs0",{"d":"M6 6v11"}],["$","path","yhl04a",{"d":"M10 3v18"}],["$","path","3a1oy3",{"d":"M14 8v7"}],["$","path","123xd1",{"d":"M18 5v13"}],["$","path","154ddg",{"d":"M22 10v3"}],"$undefined"]}]}],"handsfree",["$","span",null,{"className":"docs-brand-period","children":"."}],["$","i",null,{}],["$","small",null,{"children":"Support"}]]}],["$","a",null,{"href":"/","className":"docs-open-app","children":["Open Handsfree ",["$","svg",null,{"xmlns":"http://www.w3.org/2000/svg","width":16,"height":16,"viewBox":"0 0 24 24","fill":"none","stroke":"currentColor","strokeWidth":2,"strokeLinecap":"round","strokeLinejoin":"round","className":"lucide lucide-arrow-up-right","aria-hidden":"true","children":[["$","path","1tivn9",{"d":"M7 7h10v10"}],["$","path","1vkiza",{"d":"M7 17 17 7"}],"$undefined"]}]]}]]}],["$","div",null,{"className":"docs-layout","children":[["$","$L63",null,{}],["$","$L61",null,{"parallelRouterKey":"children","template":["$","$L62",null,{}]}]]}],["$","footer",null,{"className":"docs-footer","children":[["$","span",null,{"children":["Handsfree support ",["$","span",null,{"aria-hidden":"true","children":"·"}]," Your computer. Your context."]}],["$","a",null,{"href":"/privacy/","children":"Privacy"}]]}]]}]]}]
55:["$","$1","c",{"children":[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/chunks/0uu3n70ekaa_q.css","precedence":"next"}],["$","script","script-0",{"src":"/_next/static/chunks/3fntmmi971322.js","async":true}]],["$","html",null,{"lang":"en","children":["$","body",null,{"children":["$","$L61",null,{"parallelRouterKey":"children","template":["$","$L62",null,{}],"notFound":[[["$","title",null,{"children":"404: This page could not be found."}],["$","div",null,{"style":{"fontFamily":"system-ui,\"Segoe UI\",Roboto,Helvetica,Arial,sans-serif,\"Apple Color Emoji\",\"Segoe UI Emoji\"","height":"100vh","textAlign":"center","display":"flex","flexDirection":"column","alignItems":"center","justifyContent":"center"},"children":["$","div",null,{"children":[["$","style",null,{"dangerouslySetInnerHTML":{"__html":"body{color:#000;background:#fff;margin:0}.next-error-h1{border-right:1px solid rgba(0,0,0,.3)}@media (prefers-color-scheme:dark){body{color:#fff;background:#000}.next-error-h1{border-right:1px solid rgba(255,255,255,.3)}}"}}],["$","h1",null,{"className":"next-error-h1","style":{"display":"inline-block","margin":"0 20px 0 0","padding":"0 23px 0 0","fontSize":24,"fontWeight":500,"verticalAlign":"top","lineHeight":"49px"},"children":404}],["$","div",null,{"style":{"display":"inline-block"},"children":["$","h2",null,{"style":{"fontSize":14,"fontWeight":400,"lineHeight":"49px","margin":0},"children":"This page could not be found."}]}]]}]}]],[]]}]}]}]]}]
5d:null
58:true
4d:300
4d:C
57:0
4f:"$undefined"
51:"$undefined"
54:"$undefined"
56:"$undefined"
4c:"$undefined"
