From 07404759f3a2bdb3c4bb841e2647904c761fa162 Mon Sep 17 00:00:00 2001 From: Winter Williams Date: Fri, 10 Jul 2026 10:07:09 -0700 Subject: [PATCH] sync: auto-sync from GURU-BEAST-ROG at 2026-07-10 10:06:22 Author: Mike Swanson Machine: GURU-BEAST-ROG Timestamp: 2026-07-10 10:06:22 --- .claude/commands/syncro.md | 11 +++--- .claude/scripts/syncro-env.sh | 39 +++++++++++++++---- ...-discord-bot-tpm-pc-ace-association-fix.md | 26 +++++++++++++ 3 files changed, 64 insertions(+), 12 deletions(-) diff --git a/.claude/commands/syncro.md b/.claude/commands/syncro.md index fbeff45a..09158610 100644 --- a/.claude/commands/syncro.md +++ b/.claude/commands/syncro.md @@ -96,12 +96,13 @@ Every Syncro API call is attributed to the **owner of the API key**. Comments, l - **Correcting mis-billed labor** (a debug action) must keep the ORIGINAL tech's `user_id` (their commission). `update_line_item` preserves the existing `user_id`; a remove+add defaults the new line to the API-key owner — so set `user_id` to the original tech on `add_line_item`, or PUT it afterward. Determine the original tech from `.ticket.user_id` and the line's `.user_id`. Don't take a tech's commission just because the math was fixed by someone else. - **Ticket ownership:** adding notes/labor or changing status does NOT change the ticket owner. Multiple techs routinely work one ticket. Only PUT a ticket's `user_id` (reassign owner) when explicitly asked; status PUTs send only `status`. -| identity.json user | Syncro user | user_id | -|---|---|---| -| `mike` | Michael Swanson | 1735 | -| `howard` | Howard Enos | 1750 | +| identity.json user | Syncro user | user_id | vault path | +|---|---|---|---| +| `mike` | Michael Swanson | 1735 | `msp-tools/syncro` | +| `howard` | Howard Enos | 1750 | `msp-tools/syncro-howard` | +| `winter` | Winter Williams | 1737 | `msp-tools/syncro-winter` | -Keys are baked into the skill below. To add a new user: generate a token in Syncro → Admin → API Tokens, add a case to the key-select block, and store a backup copy in the vault at `msp-tools/syncro-.sops.yaml`. +Keys live in the SOPS vault and are resolved by `.claude/scripts/syncro-env.sh` (source it — never hardcode a key). When `CLAUDETOOLS_REQUESTER_USER` is set (Discord bot threads), syncro-env.sh prefers the REQUESTER's key if one is vaulted, so bot-driven Syncro actions attribute to the person who asked; it falls back to the identity.json user otherwise. To add a new user: generate a token in Syncro → Admin → API Tokens, vault it at `msp-tools/syncro-.sops.yaml`, and add a case to `_syncro_key_path()` in syncro-env.sh. ### Get API key diff --git a/.claude/scripts/syncro-env.sh b/.claude/scripts/syncro-env.sh index d0afee79..02089b5f 100644 --- a/.claude/scripts/syncro-env.sh +++ b/.claude/scripts/syncro-env.sh @@ -35,12 +35,36 @@ export VAULT_ROOT=$(jq -r '.vault_path // empty' "$_identity" 2>/dev/null) export SYNCRO_USER=$(jq -r '.user // empty' "$_identity" 2>/dev/null) # Per-user token — attribution in Syncro depends on using the right one. -case "$SYNCRO_USER" in - mike) _vault_path="msp-tools/syncro" ;; - howard) _vault_path="msp-tools/syncro-howard" ;; - *) echo "[WARNING] No Syncro key vaulted for user '$SYNCRO_USER' — enrichment skipped" >&2 - return 1 2>/dev/null || exit 1 ;; -esac +# Map a user name to their vaulted key path; empty = no key vaulted for them. +_syncro_key_path() { + case "$1" in + mike) echo "msp-tools/syncro" ;; + howard) echo "msp-tools/syncro-howard" ;; + winter) echo "msp-tools/syncro-winter" ;; + *) echo "" ;; + esac +} + +# Prefer the requester's key when running on someone's behalf (e.g. the Discord +# bot sets CLAUDETOOLS_REQUESTER_USER per thread) so Syncro attributes actions +# to the person who asked. Fall back to the identity.json user when the +# requester has no vaulted key (e.g. rob) — never hard-fail on the preference. +_vault_path="" +_requester="${CLAUDETOOLS_REQUESTER_USER:-}" +if [ -n "$_requester" ] && [ "$_requester" != "$SYNCRO_USER" ]; then + _req_path=$(_syncro_key_path "$_requester") + if [ -n "$_req_path" ]; then + _vault_path="$_req_path" + export SYNCRO_USER="$_requester" + echo "[INFO] Using Syncro key for requester '$_requester' (attribution)" >&2 + fi +fi +[ -z "$_vault_path" ] && _vault_path=$(_syncro_key_path "$SYNCRO_USER") + +if [ -z "$_vault_path" ]; then + echo "[WARNING] No Syncro key vaulted for user '$SYNCRO_USER' — enrichment skipped" >&2 + return 1 2>/dev/null || exit 1 +fi SYNCRO_API_KEY=$(bash "$CLAUDETOOLS_ROOT/.claude/scripts/vault.sh" \ get-field "$_vault_path" credentials.credential 2>/dev/null | tail -1) @@ -53,5 +77,6 @@ if [ -z "$SYNCRO_API_KEY" ]; then return 1 2>/dev/null || exit 1 fi -unset _syncro_env_dir _identity _root_override _vault_path +unset _syncro_env_dir _identity _root_override _vault_path _requester _req_path +unset -f _syncro_key_path return 0 2>/dev/null || true diff --git a/clients/mineralogical-record/session-logs/2026-07/2026-07-10-discord-bot-tpm-pc-ace-association-fix.md b/clients/mineralogical-record/session-logs/2026-07/2026-07-10-discord-bot-tpm-pc-ace-association-fix.md index 03b83f23..05956a83 100644 --- a/clients/mineralogical-record/session-logs/2026-07/2026-07-10-discord-bot-tpm-pc-ace-association-fix.md +++ b/clients/mineralogical-record/session-logs/2026-07/2026-07-10-discord-bot-tpm-pc-ace-association-fix.md @@ -137,3 +137,29 @@ On TPM-PC (Mineralogical Record): (ticket #32511, invoice #67997 — original Ace file-level removal) - Discord thread: 1525175952261382354 (#tech-department) - SFTA tool: https://github.com/DanysysTeam/PS-SFTA + +## Update: 10:05 AZ — Winter's per-user Syncro API key vaulted + wired + +Follow-up in the same thread: I had noted the ticket comment/billing on #32529 were +attributed to Mike's API key (only line-item user_id could be set to Winter). Winter +generated and provided her own Syncro API token. Actions taken: + +- Verified the token live against GET /me — authenticates as Winter Williams, user_id + 1737, admin, full permissions. (Note: /me response is FLAT — user_id/user_email at top + level, not under .user.) +- Vaulted it: `msp-tools/syncro-winter.sops.yaml` (kind api-key, owner winter, + syncro_user_id 1737), SOPS-encrypted, round-trip verified via vault.sh get-field. + Vault commit 6cb2639, pushed. +- Wired into `.claude/scripts/syncro-env.sh`: refactored the key-select case into + `_syncro_key_path()` (mike/howard/winter), and added a **requester preference** — when + `CLAUDETOOLS_REQUESTER_USER` is set (Discord bot threads) and that user has a vaulted + key, syncro-env.sh uses THEIR key and sets SYNCRO_USER accordingly, so bot-driven + Syncro actions attribute to the person who asked. Falls back to the identity.json user + when the requester has no key (e.g. rob) — never hard-fails on the preference. + Tested: `CLAUDETOOLS_REQUESTER_USER=winter` resolves Winter's key (Td75b771c601...). +- Updated `.claude/commands/syncro.md`: user table now includes winter (1737) + vault + paths column; removed the stale "keys are baked into the skill" note (Howard's + 2f351638 refactor moved keys to the vault). + +Effect: all future Syncro comments/tickets/invoices created through the bot in Winter's +threads are attributed to Winter in Syncro, not Mike.