fix(pair-agent): tunnel activation is consent-gated — and the receipt's consent claim is now real

The tunnel egress receipts have claimed consent: 'pair_agent=on' since v1.63
while no such key or gate existed — ngrok installed+authed was enough for
the CLI to auto-start an internet-facing tunnel. isPairAgentEnabled() (fail-
closed, env-overridable) now gates all three activation points: CLI
auto-start, POST /tunnel/start (refuses with the enable hint), and the
BROWSE_TUNNEL=1 startup bind. Consent-on-first-use, not silent breakage:
the /pair-agent skill asks once (one-way-door posture), sets pair_agent via
gstack-config (registered with on|off validation, default off), and never
asks again; direct API callers get the same hint in the refusal.

Adapted from the fork's gate: their reader targeted config.json, which on
main would have made the gate silently un-enableable — ours reads the
canonical ~/.gstack/config.yaml with the JSON shape as fallback, pinned by
tests either way (11 cases, gate wiring tripwires included).

Ported from time-attack/gstack (GStack 2), store adaptation ours.

Co-authored-by: Sina Matian <sina@time-attack.dev>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-08-14 12:43:24 -07:00
co-authored by Sina Matian Claude Fable 5
parent 93d92c4585
commit b141fb3511
6 changed files with 190 additions and 8 deletions
+5
View File
@@ -133,6 +133,7 @@ lookup_default() {
redact_repo_visibility) echo "" ;; # empty → fall through to gh/glab detection
redact_prepush_hook) echo "false" ;;
pair_agent) echo "off" ;; # remote tunnel consent — fail-closed until /pair-agent asks
# Brain-aware planning (v1.48 / T5+T10+T16). Defaults documented inline:
# brain_trust_policy@<endpoint-id> — unset on fresh install; setup-gbrain
# writes 'personal' for local engines,
@@ -310,6 +311,10 @@ case "${1:-}" in
echo "Warning: redact_prepush_hook '$VALUE' not recognized. Valid values: true, false. Using false." >&2
VALUE="false"
fi
if [ "$KEY" = "pair_agent" ] && [ "$VALUE" != "on" ] && [ "$VALUE" != "off" ]; then
echo "Warning: pair_agent '$VALUE' not recognized. Valid values: on, off. Using off." >&2
VALUE="off"
fi
if [ "$KEY" = "plan_tune_hooks" ] && [ "$VALUE" != "prompt" ] && [ "$VALUE" != "yes" ] && [ "$VALUE" != "no" ]; then
echo "Warning: plan_tune_hooks '$VALUE' not recognized. Valid values: prompt, yes, no. Using prompt." >&2
VALUE="prompt"