harness: read Syncro keys from vault, stop hardcoding the repo root

Two defects found while running /wiki-compile, both from tooling assuming
a path instead of resolving it.

1. Plaintext Syncro API keys. Mike's and Howard's live PSA keys were
   copy-pasted into three command files, a script, and two catalog docs --
   despite both already being vaulted at msp-tools/syncro and
   msp-tools/syncro-howard. Replaced with reads from the SOPS vault via a
   new sourced helper, .claude/scripts/syncro-env.sh. Write paths fail
   closed; read-only paths degrade to skipped enrichment rather than a
   wrong key. Per-user mapping is unchanged, so Syncro attribution is too.

2. Hardcoded repo root. wiki-compile/wiki-lint/inject-standards and
   gen_b64.py hardcoded D:/claudetools; this machine is C:/claudetools.
   syncro.md also read ~/.claude/identity.json before the repo copy -- the
   same bug that made remediation-tool's consent-audit report a fully
   consented tenant as RED. Root now resolves from the script's own
   location, with identity.json claudetools_root as the override.

get-identity.sh had a chicken-and-egg bug: it read ${CLAUDETOOLS_ROOT:-.}
but never set it, so every caller had to already know the root. It now
self-resolves and exports CLAUDETOOLS_ROOT + VAULT_ROOT.

Verified: all three command setup blocks authenticate against live Syncro;
get-identity.sh works from any cwd and honors a pre-set root; gps-rmm
autoenroll resolves its key from the vault. security-review: no findings.

NOTE: both keys remain valid in git history. Rotation in the Syncro portal
is the required follow-up -- this commit does not resolve that exposure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-10 07:56:25 -07:00
parent c34cdb3a39
commit 2f35163866
10 changed files with 125 additions and 61 deletions

View File

@@ -36,17 +36,18 @@ want a periodic clean rebuild. For "I just did some work, capture it," use plain
## Phase 0 — Setup
```bash
CLAUDETOOLS_ROOT="D:/claudetools"
VAULT="$CLAUDETOOLS_ROOT/.claude/scripts/vault.sh"
# Resolves CLAUDETOOLS_ROOT + VAULT_ROOT from identity.json and reads the caller's
# per-user Syncro key from the SOPS vault. Never hardcode a repo path or an API key.
# Exports: CLAUDETOOLS_ROOT, VAULT_ROOT, SYNCRO_USER, SYNCRO_BASE, SYNCRO_API_KEY.
source "$(git rev-parse --show-toplevel)/.claude/scripts/syncro-env.sh" || true
# Syncro auth (read-only operations only — GET requests ONLY in this skill)
BASE="https://computerguru.syncromsp.com/api/v1"
USER_ID=$(jq -r '.user // empty' "$CLAUDETOOLS_ROOT/.claude/identity.json")
case "$USER_ID" in
mike) API_KEY="T259810e5c9917386b-52c2aeea7cdb5ff41c6685a73cebbeb3" ;;
howard) API_KEY="Tde5174a6e9e312d14-02fd5bfe0f0ee40c87d027507c680e18" ;;
*) echo "[WARNING] Unknown user — Syncro enrichment skipped" ; API_KEY="" ;;
esac
VAULT="$CLAUDETOOLS_ROOT/.claude/scripts/vault.sh"
BASE="$SYNCRO_BASE" # read-only: GET requests ONLY in this skill
API_KEY="$SYNCRO_API_KEY" # empty if no key vaulted for this user -> skip enrichment
if [ -z "$API_KEY" ]; then
echo "[WARNING] No Syncro key for user '${SYNCRO_USER:-unknown}' — Syncro enrichment skipped"
fi
```
**Synthesis engine:** seed/full article drafting is done by a **Sonnet subagent** (Agent tool, `model: "sonnet"`), not Ollama. The main agent gathers sources + Syncro data, delegates the draft, then reviews it before writing. No Ollama dependency.