diff --git a/.claude/memory/MEMORY.md b/.claude/memory/MEMORY.md index e54d1a2..f2fa199 100644 --- a/.claude/memory/MEMORY.md +++ b/.claude/memory/MEMORY.md @@ -41,7 +41,6 @@ - [Scheduling = coord todo, not schedulers](feedback_scheduling_via_coord_todo.md) — Defer future work as a coord todo (POST /api/coord/todos; needs text + created_by_user + created_by_machine) for a later session to pick up. NOT /schedule remote CCR agents (no vault/creds there) or local scheduled tasks. - [DMARC rua INKY only when onboarded](feedback_dmarc_rua_inky_onboarded_only.md) — Don't point a client's DMARC rua at reports-sg.inkydmarc.com unless that client is onboarded to INKY (most aren't). Use plain `p=none` with no rua otherwise. - [Use rmm-search to find machines](feedback_rmm_search_skill.md) — Find GuruRMM agents via the `rmm-search` skill (`rmm-search.sh [-c client]`), never hand-grep /api/agents (it bleeds across clients). Then hand hostname/id to `/rmm`. -- [DM wrapped command lines to Mike](feedback_dm_wrapped_command_lines.md) — Long single-line output (consent links, URLs, one-liners) gets DM'd to Mike via the `discord-dm` skill so it's copy-pasteable, not terminal-wrapped. `discord-dm.sh mike ""`. - [Attribution is read, never inferred](feedback_attribution_from_identity.md) — Who-did-what (user+machine) comes ONLY from identity.json + users.json + git authorship. Never infer from hostname patterns, the userEmail hint, or memory. The "5070" box is Mike's. sync.sh reconciles git config to identity.json; /save renders the User block via whoami-block.sh. - [D2TESTNAS SSH Access](feedback_d2testnas_ssh.md) — Use root@192.168.0.9 with Paper123!@#, not sysadmin. - [Bypass Permissions Setting](feedback_bypass_permissions_setting.md) — Set permissions.defaultMode to bypassPermissions in settings.json on all machines. @@ -143,7 +142,7 @@ - [IX WHM API access = 'ClaudeTools' token, not password](ix-whm-dns-api-access.md) — IX cPanel/WHM (ix.azcomputerguru.com:2087) DNS + all API work uses the FULL-ACCESS-root WHM API token at vault `infrastructure/ix-server` `credentials.whm-api-token` via header `Authorization: whm root:` (force curl -4). Password basic-auth on legacy json-api now 403s. Public NS ns1/ns2.acghosting.com = 52.52.94.202. - [Vault EVERY credential surfaced in-session](feedback-vault-every-credential.md) — any cred (pasted/created/discovered) -> store via the vault skill + document purpose & exact usage immediately; it's a standing job rule (reinforced in CORE CLAUDE.md). Lost IX creds wasted ~1h on 2026-06-12. - [GuruRMM install-report v1: reuse endpoint + failed-install agent](gururmm-install-report-failed-agent-v1.md) — legacy NSIS installer reuses /api/install-report (machine info + logs, success+fail); server upserts a visible "failed-install" device on failure reports (Mike: in v1); verify-connect-before-success; trend/near-fail analytics. Server side is a separate sequential SPEC after the legacy-agent branch lands. -- [DM wrapping commands to Mike in Discord](feedback_dm_wrapping_commands_to_mike.md) — long/wrapping one-liners go via Discord DM (code block copies clean), not just chat; bot token vault projects/discord-bot/bot-token, Mike uid 264814939619721216, MUST set User-Agent header or Cloudflare 403 errcode 1010; helper .claude/tmp/discord-dm.py +- [DM wrapping commands to Mike in Discord](feedback_dm_wrapping_commands_to_mike.md) — long/wrapping output (commands, consent links, URLs) goes via Discord DM (copies clean), not just chat; use the `discord-dm` skill (`discord-dm.sh mike ""`). Gotchas: bot token vault projects/discord-bot/bot-token, Mike uid 264814939619721216, MUST set User-Agent or Cloudflare 403 errcode 1010, build JSON via jq + printf|--data-binary (direct -d → 50109). - [Physical access codes -> vault + wiki pointer](feedback_physical_access_codes.md) — alarm/lockbox/door codes go in vault clients//physical-access-.sops.yaml (kind: physical-access) + a `## Physical Access` pointer section in the client wiki; never plaintext. First entry: Peaceful Spirit NW. - [CT Thoughts backlog](feedback_ct_thoughts_backlog.md) — ClaudeTools harness ideas go in docs/CT_THOUGHTS.md (trigger "ct thought:"); CT analogue of RMM_THOUGHTS. Don't build until explicit go. First entry = ClaudeTools 3.0 web co-work vision. - [AI-auth product boundary](project_ai_auth_product_boundary.md) — ClaudeTools/ClaudeTools 3.0 = internal-only, per-person subscription OAuth ok; GuruRMM = sellable, customer brings own API key (never ACG's subscription); backend dev = internal. Anthropic ToS bans subscription auth in third-party products. diff --git a/.claude/memory/feedback_365_remediation_tool.md b/.claude/memory/feedback_365_remediation_tool.md index 9a0e06f..e0450fa 100644 --- a/.claude/memory/feedback_365_remediation_tool.md +++ b/.claude/memory/feedback_365_remediation_tool.md @@ -34,4 +34,8 @@ Graph API permissions alone are NOT sufficient for privileged operations. The se ### Exchange Online REST API -For Exchange cmdlets (Get-TransportRule, Add-MailboxPermission, etc.), use scope `https://outlook.office365.com/.default` and POST to `https://outlook.office365.com/adminapi/beta/$TENANT_ID/InvokeCommand` with `{"CmdletInput":{"CmdletName":"...", "Parameters":{...}}}`. +For Exchange cmdlets (Get-TransportRule, Add-MailboxPermission, Get-MessageTraceV2, Get-DkimSigningConfig, New-Mailbox, etc.), use scope `https://outlook.office365.com/.default` and POST to `https://outlook.office365.com/adminapi/beta/$TENANT_ID/InvokeCommand` with `{"CmdletInput":{"CmdletName":"...", "Parameters":{...}}}`. (`Get-MessageTrace` is deprecated → use `Get-MessageTraceV2`.) Fresh-onboard EXO app-only auth can 401 for ~15-60 min until the Exchange Admin role + `Exchange.ManageAsApp` replicate, even though Graph already works — wait and retry, it's propagation not misconfig. + +### Sending mail + the token-audience gotcha (corrected 2026-06-15) + +Exchange Operator (`b43e7342`) ALSO holds **Graph** `Mail.Send` + `Mail.ReadWrite` + `MailboxSettings.ReadWrite` — so the suite CAN send as any mailbox in a consented tenant via Graph `POST /users//sendMail`. (This corrects an earlier "the suite has no Mail.Send" conclusion — that came from reading the wrong token.) `get-token.sh exchange-op` returns an **Exchange-Online**-audience token (outlook.office365.com) whose `roles` claim does NOT list Graph scopes; to call Graph you must mint a **Graph**-audience token with the app's client creds (`scope=https://graph.microsoft.com/.default`). **Never conclude a permission is missing from a wrong-audience token — check the app's actual Graph app-role assignments first.** [[reference_cloudflare_access]] diff --git a/.claude/memory/feedback_dm_wrapped_command_lines.md b/.claude/memory/feedback_dm_wrapped_command_lines.md deleted file mode 100644 index 5e777b6..0000000 --- a/.claude/memory/feedback_dm_wrapped_command_lines.md +++ /dev/null @@ -1,12 +0,0 @@ ---- -name: feedback-dm-wrapped-command-lines -description: DM any wrapped/long single-line output (consent links, long one-liners, URLs) to Mike in Discord so it's copy-pasteable -metadata: - type: feedback ---- - -Any wrapped command line or long single-line output meant for the user to copy — M365 admin-consent links, long CLI one-liners, URLs with query strings, enrollment/installer URLs — should be **sent to Mike as a Discord DM**, not left only in the terminal. - -**Why:** Terminal wrapping breaks long single-line items across lines, so copy-paste picks up line breaks/spaces and corrupts the link or command. A Discord DM preserves it as one clean line. (Mike, 2026-06-15.) - -**How to apply:** Use the `discord-dm` skill — `bash .claude/scripts/discord-dm.sh mike ""` (or `echo "$X" | ... mike`). Still show it inline in the response too, but the DM is the canonical copy-paste source. The skill is prepopulated with all org user IDs (mike/howard/rob/winter) and channel IDs (#bot-alerts/#dev-alerts); keep its directory in sync with `.claude/users.json`. Build payloads with `jq -nc --arg` + `printf | curl --data-binary @-` (direct `-d` mangles multiline → Discord 50109). diff --git a/.claude/memory/feedback_dm_wrapping_commands_to_mike.md b/.claude/memory/feedback_dm_wrapping_commands_to_mike.md index 6bec4dd..02f1792 100644 --- a/.claude/memory/feedback_dm_wrapping_commands_to_mike.md +++ b/.claude/memory/feedback_dm_wrapping_commands_to_mike.md @@ -1,29 +1,31 @@ --- name: feedback_dm_wrapping_commands_to_mike -description: When a command/snippet you want Mike to run is long enough to wrap in the terminal, DM it to him in Discord (code block copies cleanly) instead of only putting it in chat. +description: DM long/wrapping output (commands, consent links, URLs) to Mike in Discord via the discord-dm skill so it copies cleanly — terminal wrapping mangles long single lines on paste. metadata: type: feedback --- -Mike (2026-06-13): "For any command that wraps (like this one) DM me in discord, the -line breaks suck." Terminal line-wrapping mangles long one-liners when he copies them. +Mike (2026-06-13): "For any command that wraps (like this one) DM me in discord, the line +breaks suck." Reaffirmed 2026-06-15: any wrapped/long single-line output meant for him to +copy — long one-liners, M365 admin-consent links, URLs with query strings, installer/ +enrollment URLs — goes to Mike as a Discord DM, not left only in the terminal. Terminal +wrapping inserts breaks/spaces that corrupt the line on paste. -**How to apply:** When you produce a command/code block for Mike to run that would wrap -in the terminal (long one-liners, multi-flag commands), send it to him via Discord DM as a -```fenced code block``` (Discord copies the whole line cleanly regardless of visual wrap), -and just reference it in chat ("DM'd you the command"). Short, non-wrapping commands can -stay inline. +**How to apply:** Use the **`discord-dm` skill** (the productized mechanism — supersedes the +old ad-hoc `.claude/tmp/discord-dm.py`): `bash .claude/scripts/discord-dm.sh mike ""` +(or `echo "$X" | bash .claude/scripts/discord-dm.sh mike`). It's prepopulated with org user +IDs (mike/howard/rob/winter) + channel IDs (#bot-alerts/#dev-alerts). Still show the item +inline too, but the DM is the canonical copy-paste source. Short non-wrapping commands stay inline. -**Mechanics (verified working 2026-06-13):** +**Mechanics / gotchas (the skill handles these, but know them):** - Bot token: vault `projects/discord-bot/bot-token.sops.yaml` field `credentials.bot_token`. -- Mike's Discord user id: `264814939619721216` (Howard: `624667664501178379`). -- **MUST set a `User-Agent` header** like `DiscordBot (https://azcomputerguru.com, 1.0)` -- - Discord's API is behind Cloudflare, which returns **403 error 1010** for the default - urllib/curl UA. This is the #1 gotcha; both DM-open and message-send fail without it. -- Open a DM channel: `POST https://discord.com/api/v10/users/@me/channels {"recipient_id":}` - -> returns channel id; then `POST /channels//messages {"content": "..."}`. -- Reusable helper written this session: `.claude/tmp/discord-dm.py` (reads body from a file - or stdin; `BOT_TOKEN` from env). The bot CAN initiate DMs to Mike (mutual guild - 624663750603046913); the earlier 403 was the missing UA, not a privacy block. +- Mike's Discord uid `264814939619721216` (Howard `624667664501178379`). +- **MUST set a `User-Agent` header** (e.g. `ClaudeToolsBot (claudetools, 1.0)`) — Discord's API + is behind Cloudflare, which 403s (errcode 1010) the default urllib/curl UA. +- DM flow: `POST /users/@me/channels {"recipient_id":}` → channel id, then + `POST /channels//messages`. +- Build JSON with `jq -nc --arg` and feed curl via `printf | --data-binary @-`; a direct + `-d "$(...)"` mangles multiline content → Discord `50109 invalid JSON body`. -Related: [[reference_resource_map]] (Discord bot), the `discord-bot` project. +Related: [[feedback_ascii_only_api_payloads]] (ASCII-only in Discord/coord payload text), +[[reference_resource_map]] (Discord bot), the `discord-bot` project. diff --git a/errorlog.md b/errorlog.md index f1bee4a..b10bb09 100644 --- a/errorlog.md +++ b/errorlog.md @@ -17,6 +17,8 @@ Categories (the `[type]` tag): _(none)_ = skill/command execution failure · +2026-06-15 | GURU-5070 | memory-dream (--apply-safe) | flagged feedback_broken_backlinks_are_writeme_markers.md as an orphan and appended a DUPLICATE index line though it already had one — orphan detector likely keys on the frontmatter name: slug, not the (file.md) link target. Fix the index-line matching to compare by filename [ctx: mode=apply-safe] + 2026-06-15 | GURU-5070 | powershell/var-case | [friction] PowerShell vars are case-INSENSITIVE: $gUid silently overwrote $guid (GPO id), Set-ADObject hit a bad DN and left GPT.ini/AD versionNumber inconsistent until fixed. Never rely on case to distinguish PS variables 2026-06-15 | GURU-5070 | python/argv-limit | [friction] passed full /api/agents JSON (248 agents) as a python CLI arg -> 'Argument list too long' on Windows. Pipe large payloads via stdin, not argv