#!/usr/bin/env bash # credbridge.sh — resolve credctl credentials into env vars for steev's # personal-flow tools, then exec the CLI. Secrets NEVER echoed, logged, or # written to disk. # # Usage: credbridge.sh [args...] # tools: proton-bridge | perplexity # # Per PROFILE-DISTRIBUTION-PROTOCOL §3 (shared core, "credbridge" row) and §6 # (Conventions → Secrets), every profile distribution exposes credentials via # this verb chain: credctl set → credenv → exec, with secrets per-call only. # # This is the personal-assistant variant of the credbridge pattern. Steev's # cred surface is narrow by design: # # - proton-bridge: IMAP/SMTP user + password for the local Proton Bridge # T6 sidecar — gives Steev access to JP's Proton mail # via himalaya (cleartext on 127.0.0.1 only) # - perplexity: Perplexity API key for WebSearch toolset (lightweight # — most Steev work uses the perplexity MCP instead) # # Wave 8 (2026-05-24): google-workspace case REMOVED — Hermes builtin # google-workspace skill manages its own OAuth flow via the Hermes hub, not # the credctl vault. Vault contains no google-workspace-* entries. # # Plan B marketing platforms (WooCommerce, Mailchimp, Meta, GA4, etc.) are OUT # OF SCOPE here — that's cmo-planb's credbridge. Steev MUST NEVER resolve a # marketing platform credential. The CLAUDE.md "no access to Plan B marketing # platform credentials" rule is enforced by omission from this case statement. # # Design notes (same as cmo/credbridge.sh — shared core): # - credctl values read into local vars, exported straight to the child env # - No `echo $secret`. set +x stays off. set -euo pipefail # Paths env-overridable for portability; defaults match the JP install. CREDCTL="${CREDCTL:-/home/svrnty/workspaces/cortex/L6-svrnty.core-credentials/credctl}" STEEV_LIB="${STEEV_LIB:-/home/svrnty/.hermes/steev}" die() { printf '{"error":"%s"}\n' "$1" >&2; exit 1; } [ $# -ge 1 ] || die "usage: credbridge.sh [args...]" TOOL="$1"; shift [ -x "$CREDCTL" ] || die "credctl not found/executable at $CREDCTL" # cred_raw — print unmasked credential value to stdout. Caller captures # into a var; value never displayed. Strips the "Value:" header from credctl. cred_raw() { "$CREDCTL" get "$1" --unmask 2>/dev/null \ | sed -n '/^Value:/,$p' | sed '1s/^Value:[[:space:]]*//' } case "$TOOL" in proton-bridge) # Steev reads JP's Proton inbox via the local Proton Bridge IMAP daemon # (T6 sidecar — see PROFILE-DISTRIBUTION-PROTOCOL §4.T6). credctl stores # user + password as separate vault entries (Wave 8 aligned to vault). PB_USER="$(cred_raw proton-bridge-imap-user)" [ -n "$PB_USER" ] || die "credctl: proton-bridge-imap-user not set" PB_PASS="$(cred_raw proton-bridge-imap-pass)" [ -n "$PB_PASS" ] || die "credctl: proton-bridge-imap-pass not set" export PROTON_BRIDGE_IMAP_USER="$PB_USER" export PROTON_BRIDGE_IMAP_PASSWORD="$PB_PASS" exec "$@" ;; perplexity) # Lightweight WebSearch path. Most Steev research goes through the # perplexity MCP server (which holds its own key); this credbridge entry # exists for scripts that need a raw key (rare). Wave 8 renamed # vault entry `perplexity-api` → `perplexity`. PPL_KEY="$(cred_raw perplexity)" [ -n "$PPL_KEY" ] || die "credctl: perplexity not set" export PERPLEXITY_API_KEY="$PPL_KEY" exec "$@" ;; *) die "unknown tool: $TOOL (allowed: proton-bridge|perplexity)" ;; esac