sync: auto-sync from GURU-BEAST-ROG at 2026-06-02 10:44:23
Author: Mike Swanson Machine: GURU-BEAST-ROG Timestamp: 2026-06-02 10:44:23
This commit is contained in:
@@ -48,6 +48,7 @@
|
|||||||
- [Paste-safe command formatting (Howard)](feedback_command_formatting.md) — Two clauses, one root cause: (a) multi-line scripts not semicolon one-liners (wrap breaks paste), (b) all code at column 0 inside fences (indentation breaks PowerShell paste).
|
- [Paste-safe command formatting (Howard)](feedback_command_formatting.md) — Two clauses, one root cause: (a) multi-line scripts not semicolon one-liners (wrap breaks paste), (b) all code at column 0 inside fences (indentation breaks PowerShell paste).
|
||||||
- [Autonomous infra/build setup](feedback_autonomous_infra_setup.md) — During infra/build/CI/dev setup, just install prerequisites and push through routine steps; reserve check-ins for genuine decisions (forks, destructive/outward, client/prod).
|
- [Autonomous infra/build setup](feedback_autonomous_infra_setup.md) — During infra/build/CI/dev setup, just install prerequisites and push through routine steps; reserve check-ins for genuine decisions (forks, destructive/outward, client/prod).
|
||||||
- [Check patterns before asking](feedback_check_patterns_before_asking.md) — Before asking how to do something repeat-style (sync, save, sweep, billing), study existing artifacts and workflow docs first; reach for similar past artifacts as the template.
|
- [Check patterns before asking](feedback_check_patterns_before_asking.md) — Before asking how to do something repeat-style (sync, save, sweep, billing), study existing artifacts and workflow docs first; reach for similar past artifacts as the template.
|
||||||
|
- [Pricing verification — no guessing](policy_pricing_verification.md) — ANY cost presented to the team or a client MUST be verified via live web lookup (WebFetch/WebSearch, fallback to headless Chrome). Never estimate from training data. Cite source + date inline. If unreachable, say so — do NOT substitute a guess.
|
||||||
- [Client communication tone](feedback_client_tone.md) — How to write client-facing Syncro comments — expert partner, not intake questionnaire.
|
- [Client communication tone](feedback_client_tone.md) — How to write client-facing Syncro comments — expert partner, not intake questionnaire.
|
||||||
- [Add Mike as owner on all Entra apps](feedback_entra_app_owner.md) — Apps created via management SP have no user owner — must add Mike manually or publisher verification fails.
|
- [Add Mike as owner on all Entra apps](feedback_entra_app_owner.md) — Apps created via management SP have no user owner — must add Mike manually or publisher verification fails.
|
||||||
- [No TOML/config file approach for endpoints](feedback_no_toml_config_endpoints.md) — User explicitly prohibits TOML or config-file-based endpoint configuration — this will never be approved.
|
- [No TOML/config file approach for endpoints](feedback_no_toml_config_endpoints.md) — User explicitly prohibits TOML or config-file-based endpoint configuration — this will never be approved.
|
||||||
|
|||||||
30
.claude/memory/policy_pricing_verification.md
Normal file
30
.claude/memory/policy_pricing_verification.md
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# Policy: Pricing Verification — No Guessing
|
||||||
|
|
||||||
|
**Set by:** Mike Swanson, 2026-06-02
|
||||||
|
|
||||||
|
Any time a cost or price is presented to the team or a client, it MUST be verified via a
|
||||||
|
live web lookup before being stated. Never use training-data recollections or estimates.
|
||||||
|
|
||||||
|
## Rules
|
||||||
|
|
||||||
|
- Hardware, parts, software licensing, SaaS pricing, labor rates — all require real-time verification.
|
||||||
|
- Use WebFetch / WebSearch first. Fall back to headless Chrome (`web-fetch-chrome.py`) if bot-blocked.
|
||||||
|
- Always cite source and date inline: `[$X.XX — Amazon, 2026-06-02]`
|
||||||
|
- If no pricing source is reachable, state that explicitly. Do NOT substitute a guess or a range
|
||||||
|
"from memory."
|
||||||
|
- Applies in all contexts: Discord bot, main session, any Claude session in this repo.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
Applies to ALL estimate types:
|
||||||
|
- Computer hardware (drives, RAM, CPUs, etc.)
|
||||||
|
- Replacement parts and peripherals
|
||||||
|
- Software licensing and subscriptions
|
||||||
|
- Third-party service quotes
|
||||||
|
- Repair labor estimates (when citing market rates)
|
||||||
|
|
||||||
|
## Why
|
||||||
|
|
||||||
|
Hardware prices fluctuate significantly. A "budget" vs "mid-range" vs "quality" drive can
|
||||||
|
vary by 2-3x depending on current market. Presenting stale or fabricated numbers to clients
|
||||||
|
damages trust and can result in inaccurate quotes.
|
||||||
117
clients/glaztech/session-logs/2026-06-02-session.md
Normal file
117
clients/glaztech/session-logs/2026-06-02-session.md
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
# Session Log — 2026-06-02 — Glaz-Tech Industries
|
||||||
|
|
||||||
|
## User
|
||||||
|
- **User:** Mike Swanson (mike)
|
||||||
|
- **Machine:** GURU-BEAST-ROG
|
||||||
|
- **Role:** admin
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Session Summary
|
||||||
|
|
||||||
|
Mike requested a transport rule in the Glaztech Exchange Online tenant to allow messages from MailProtector as `noreply@azcomputerguru.com` through spam filtering. These are MailProtector quarantine digest notifications sent to Glaztech users on behalf of ACG's no-reply address.
|
||||||
|
|
||||||
|
Before creating the rule, a message trace was pulled (via `Get-MessageTraceV2`) for `noreply@azcomputerguru.com` over the past 10 days to verify that messages were in fact being filtered by Microsoft. The trace confirmed the issue: the vast majority of digest messages delivered successfully, but some recipients were hitting `FilteredAsSpam` status (e.g., `tshaw@glaztech.com` on 2026-06-02 at 3:07 PM). The `gtimail@glaztech.com` address showed `Failed` status on every daily send — this is caused by the existing "GTIMail No-Reply - Reject Inbound" transport rule (Priority 1, `SentToPredicate` → `RejectMessageAction`) and is a separate, pre-existing issue noted for follow-up.
|
||||||
|
|
||||||
|
Authentication to Exchange Online used the ComputerGuru Exchange Operator multi-tenant app (`b43e7342`) with certificate-based credentials from the vault. The token was acquired via `get-token.sh` for the `exchange-op` tier against the Glaztech tenant (`82931e3c-de7a-4f74-87f7-fe714be1f160`) and passed to `Connect-ExchangeOnline -AccessToken` with EXO PowerShell V3 (3.9.2).
|
||||||
|
|
||||||
|
A new transport rule was created: **"SCL Bypass - noreply@azcomputerguru.com (MailProtector digests)"** at Priority 4, condition `From: noreply@azcomputerguru.com`, action `SetSCL -1`. This bypasses all spam and junk folder filtering for these digests. The rule was verified active immediately after creation.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Key Decisions
|
||||||
|
|
||||||
|
- **SCL = -1 rather than domain-level bypass:** The sender address `noreply@azcomputerguru.com` is specific enough that setting SCL=-1 on it carries minimal risk. A domain-level bypass (`azcomputerguru.com`) was considered but rejected — too broad, would cover all ACG-origin mail.
|
||||||
|
- **Priority 4:** Placed below the existing SCL bypass rules (Priority 2–3) since no conflict exists; priority ordering doesn't matter for non-overlapping senders. Placed above any catch-all rules that might exist in the future.
|
||||||
|
- **Did not restrict by connector:** The "Inbound Spam Filter" connector has no SenderIPAddresses restriction (per prior decision — avoids blocking calendar invites from external M365 tenants). Adding a connector-based condition to the rule was avoided for the same reason.
|
||||||
|
- **gtimail@glaztech.com not addressed:** The daily `Failed` delivery to `gtimail@glaztech.com` is caused by the pre-existing "GTIMail No-Reply - Reject Inbound" rule. Mike did not request any change to that rule; flagged for separate review.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Problems Encountered
|
||||||
|
|
||||||
|
- **`Get-MessageTrace` deprecated:** Initial call to `Get-MessageTrace` returned a deprecation warning and failed. Switched to `Get-MessageTraceV2`. Note: `Get-MessageTraceV2` does not accept `-PageSize` — that parameter does not exist on the V2 cmdlet.
|
||||||
|
- **`New-TransportRule -SenderAddresses` not valid:** First attempt used `-SenderAddresses` which is not a valid parameter. Correct parameter is `-From` for explicit sender address matching.
|
||||||
|
- **Cert not in Windows cert store:** Exchange Operator cert (`A615823DE1CAF15229027DEC075AFE32B900D82C`) is not installed in LocalMachine\My or CurrentUser\My on BEAST. Used `get-token.sh` cert-based JWT flow instead, passing the resulting bearer token to `Connect-ExchangeOnline -AccessToken`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Configuration Changes
|
||||||
|
|
||||||
|
- **Exchange Online transport rule created** in `glaztechindustries.onmicrosoft.com`:
|
||||||
|
- Name: `SCL Bypass - noreply@azcomputerguru.com (MailProtector digests)`
|
||||||
|
- Condition: `From = noreply@azcomputerguru.com`
|
||||||
|
- Action: `SetSCL -1`
|
||||||
|
- Priority: 4
|
||||||
|
- State: Enabled
|
||||||
|
- Comments: "Bypass spam filtering for MailProtector quarantine digest emails sent as noreply@azcomputerguru.com. Created 2026-06-02 by ACG."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Credentials & Secrets
|
||||||
|
|
||||||
|
- **Vault path used:** `msp-tools/computerguru-exchange-operator.sops.yaml`
|
||||||
|
- App: ComputerGuru - Exchange Operator
|
||||||
|
- Client ID: `b43e7342-5b4b-492f-890f-bb5a4f7f40e9`
|
||||||
|
- Cert thumbprint: `A615823DE1CAF15229027DEC075AFE32B900D82C`
|
||||||
|
- Token acquired via: `bash .claude/skills/remediation-tool/scripts/get-token.sh <tenant-id> exchange-op`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Infrastructure & Servers
|
||||||
|
|
||||||
|
- **Glaztech tenant:** `glaztechindustries.onmicrosoft.com`
|
||||||
|
- **Tenant ID:** `82931e3c-de7a-4f74-87f7-fe714be1f160`
|
||||||
|
- **Inbound mail filter:** MailProtector — `glaztech-com.inbound.emailservice.io`
|
||||||
|
- **Inbound connector:** "Inbound Spam Filter" — Partner type, RequireTls=True, no IP restriction (intentional — preserves calendar invite delivery)
|
||||||
|
- **EXO PowerShell module:** ExchangeOnlineManagement 3.9.2
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Commands & Outputs
|
||||||
|
|
||||||
|
```powershell
|
||||||
|
# Connect to Glaztech EXO with app-only token
|
||||||
|
$token = bash .claude/skills/remediation-tool/scripts/get-token.sh 82931e3c-de7a-4f74-87f7-fe714be1f160 exchange-op
|
||||||
|
Connect-ExchangeOnline -AccessToken $token -Organization 'glaztechindustries.onmicrosoft.com' -ShowBanner:$false
|
||||||
|
|
||||||
|
# Message trace (last 10 days) — confirmed FilteredAsSpam occurrences
|
||||||
|
Get-MessageTraceV2 -SenderAddress 'noreply@azcomputerguru.com' -StartDate (Get-Date).AddDays(-10) -EndDate (Get-Date)
|
||||||
|
# Key finding: tshaw@glaztech.com → FilteredAsSpam (2026-06-02 3:07 PM)
|
||||||
|
# Key finding: gtimail@glaztech.com → Failed daily (pre-existing rule, separate issue)
|
||||||
|
|
||||||
|
# Create rule
|
||||||
|
New-TransportRule `
|
||||||
|
-Name 'SCL Bypass - noreply@azcomputerguru.com (MailProtector digests)' `
|
||||||
|
-From 'noreply@azcomputerguru.com' `
|
||||||
|
-SetSCL -1 `
|
||||||
|
-Priority 4 `
|
||||||
|
-Comments 'Bypass spam filtering for MailProtector quarantine digest emails sent as noreply@azcomputerguru.com. Created 2026-06-02 by ACG.' `
|
||||||
|
-Enabled $true
|
||||||
|
```
|
||||||
|
|
||||||
|
**Final transport rule list (Glaztech):**
|
||||||
|
```
|
||||||
|
Priority 0 Pensky Allow Enabled
|
||||||
|
Priority 1 GTIMail No-Reply - Reject Inbound Enabled
|
||||||
|
Priority 2 SCL Bypass - hartsglass + olemons (SHVSALES) Enabled
|
||||||
|
Priority 3 SCL Bypass - aaaglassinc.com (SHVSALES) Enabled
|
||||||
|
Priority 4 SCL Bypass - noreply@azcomputerguru.com (MailProtector digests) Enabled
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Pending / Incomplete Tasks
|
||||||
|
|
||||||
|
- **gtimail@glaztech.com failing daily:** The "GTIMail No-Reply - Reject Inbound" rule (Priority 1) rejects all inbound mail to `gtimail@glaztech.com`. This causes the daily MailProtector digest to fail for that address. Confirm with Steve Eastman whether `gtimail@glaztech.com` should receive digests (i.e., whether the reject rule should have an exception or be modified).
|
||||||
|
- **Exchange Operator cert not in BEAST cert store:** If cert-based PowerShell connections are needed without `get-token.sh` (e.g., for interactive EXO sessions), the cert will need to be imported to the machine store. Not urgent — token flow works fine for bot-driven operations.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Reference Information
|
||||||
|
|
||||||
|
- **Syncro customer ID:** 143932
|
||||||
|
- **EXO rule created:** `SCL Bypass - noreply@azcomputerguru.com (MailProtector digests)` — Priority 4
|
||||||
|
- **EXO PowerShell V2 deprecation note:** `Get-MessageTrace` deprecated Sept 1 2025; use `Get-MessageTraceV2` (no `-PageSize` parameter)
|
||||||
|
- **Vault:** `msp-tools/computerguru-exchange-operator.sops.yaml`
|
||||||
|
- **Token cache:** `/tmp/remediation-tool/82931e3c-de7a-4f74-87f7-fe714be1f160/exchange-op.jwt`
|
||||||
@@ -73,6 +73,23 @@ drive the human's interactive session.
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Pricing — Always Verify, Never Guess
|
||||||
|
|
||||||
|
**MANDATORY:** Before presenting any cost to Mike, Howard, or a client, verify current pricing
|
||||||
|
via live web lookup. Never estimate or recall costs from training data — hardware prices,
|
||||||
|
software licensing, and labor rates change constantly.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
- Any dollar amount for hardware, parts, software, or services requires a real-time lookup
|
||||||
|
before being stated.
|
||||||
|
- Use WebFetch / WebSearch first. Fall back to headless Chrome if bot-blocked.
|
||||||
|
- Cite the source and date: `[$X.XX — Amazon, 2026-06-02]`
|
||||||
|
- If you cannot reach a pricing source, say so explicitly — do NOT substitute a guess.
|
||||||
|
- Applies to all estimate types: parts, repair quotes, hardware refreshes, software licensing,
|
||||||
|
labor, and third-party services.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Task Loop
|
## Task Loop
|
||||||
|
|
||||||
For every request, work this loop:
|
For every request, work this loop:
|
||||||
@@ -86,13 +103,41 @@ For every request, work this loop:
|
|||||||
4. **Offer Syncro** — once they have nothing else, ask whether to log the work in Syncro
|
4. **Offer Syncro** — once they have nothing else, ask whether to log the work in Syncro
|
||||||
("Want me to log this in Syncro?"). If yes, invoke `/syncro` to create or update the ticket.
|
("Want me to log this in Syncro?"). If yes, invoke `/syncro` to create or update the ticket.
|
||||||
5. **Save** — after the loop closes, run `/save` to write the session log and sync the repo.
|
5. **Save** — after the loop closes, run `/save` to write the session log and sync the repo.
|
||||||
|
6. **Kill the thread** — after `/save` completes successfully, delete the thread:
|
||||||
|
```bash
|
||||||
|
bash C:/Users/guru/ClaudeTools/projects/discord-bot/scripts/delete-thread.sh <Thread ID>
|
||||||
|
```
|
||||||
|
The Thread ID is in every `[DISCORD_CONTEXT]` block as `Thread ID: <id>`. Do not delete
|
||||||
|
if `/save` failed or errored. Do not post a closing message — the deletion is immediate.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Thread Lifecycle — Auto-Delete on Completion
|
||||||
|
|
||||||
|
Every `[DISCORD_CONTEXT]` block now includes `Thread ID: <id>` (injected by the bot after
|
||||||
|
the thread is resolved or created). This ID is what you pass to the delete script.
|
||||||
|
|
||||||
|
**When to delete:** Only after step 6 of the Task Loop — `/save` succeeded, session log
|
||||||
|
committed and pushed. The thread is the conversation record; don't kill it before the log lands.
|
||||||
|
|
||||||
|
**When NOT to delete:**
|
||||||
|
- `/save` failed or sync errored
|
||||||
|
- The user said "yes" to continuing (open follow-up items remain)
|
||||||
|
- The thread is an ongoing informational channel, not a single-task session
|
||||||
|
|
||||||
|
**Script:**
|
||||||
|
```bash
|
||||||
|
bash C:/Users/guru/ClaudeTools/projects/discord-bot/scripts/delete-thread.sh <Thread ID>
|
||||||
|
```
|
||||||
|
Source: `projects/discord-bot/scripts/delete-thread.sh` — reads bot token from `.env`, calls
|
||||||
|
`DELETE /channels/{id}` on the Discord API. Exits 0 on HTTP 200/204, 1 on error.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Who Is Asking: Discord User Identity
|
## Who Is Asking: Discord User Identity
|
||||||
|
|
||||||
Every message is prefixed with a `[DISCORD_CONTEXT]` block containing the sender's Discord
|
Every message is prefixed with a `[DISCORD_CONTEXT]` block containing the sender's Discord
|
||||||
username, display name, and user ID. Always read this block to determine who is asking.
|
username, display name, user ID, and Thread ID. Always read this block to determine who is asking.
|
||||||
|
|
||||||
### Known Team Members — Full Access
|
### Known Team Members — Full Access
|
||||||
|
|
||||||
|
|||||||
@@ -41,7 +41,18 @@ class MessageHandler:
|
|||||||
await message.reply("Hey! How can I help?")
|
await message.reply("Hey! How can I help?")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Resolve or create the thread first so we can include its ID in context.
|
||||||
|
if isinstance(message.channel, discord.Thread):
|
||||||
|
thread = message.channel
|
||||||
|
else:
|
||||||
|
name = self._thread_name(user_text) if user_text else "Attachment"
|
||||||
|
thread = await message.create_thread(
|
||||||
|
name=name,
|
||||||
|
auto_archive_duration=1440,
|
||||||
|
)
|
||||||
|
|
||||||
# Build caller-identity header so the agent always knows who is asking.
|
# Build caller-identity header so the agent always knows who is asking.
|
||||||
|
# Thread ID is included so the agent can delete the thread on completion.
|
||||||
author = message.author
|
author = message.author
|
||||||
display = getattr(author, "display_name", author.name)
|
display = getattr(author, "display_name", author.name)
|
||||||
guild_name = message.guild.name if message.guild else "DM"
|
guild_name = message.guild.name if message.guild else "DM"
|
||||||
@@ -51,20 +62,11 @@ class MessageHandler:
|
|||||||
f"User: @{author.name}"
|
f"User: @{author.name}"
|
||||||
+ (f" (display: {display})" if display != author.name else "")
|
+ (f" (display: {display})" if display != author.name else "")
|
||||||
+ f" | ID: {author.id}\n"
|
+ f" | ID: {author.id}\n"
|
||||||
f"Channel: #{channel_name} | Guild: {guild_name}\n"
|
f"Channel: #{channel_name} | Thread ID: {thread.id} | Guild: {guild_name}\n"
|
||||||
"[/DISCORD_CONTEXT]\n\n"
|
"[/DISCORD_CONTEXT]\n\n"
|
||||||
)
|
)
|
||||||
content = (discord_ctx + user_text).strip()
|
content = (discord_ctx + user_text).strip()
|
||||||
|
|
||||||
if isinstance(message.channel, discord.Thread):
|
|
||||||
thread = message.channel
|
|
||||||
else:
|
|
||||||
name = self._thread_name(user_text) if user_text else "Attachment"
|
|
||||||
thread = await message.create_thread(
|
|
||||||
name=name,
|
|
||||||
auto_archive_duration=1440,
|
|
||||||
)
|
|
||||||
|
|
||||||
attachment_paths = await self._download_attachments(message, thread.id)
|
attachment_paths = await self._download_attachments(message, thread.id)
|
||||||
if attachment_paths:
|
if attachment_paths:
|
||||||
lines = "\n".join(f"- {p}" for p in attachment_paths)
|
lines = "\n".join(f"- {p}" for p in attachment_paths)
|
||||||
|
|||||||
56
projects/discord-bot/scripts/delete-thread.sh
Normal file
56
projects/discord-bot/scripts/delete-thread.sh
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# delete-thread.sh <thread_id>
|
||||||
|
#
|
||||||
|
# Deletes a Discord thread (channel) via the Discord REST API.
|
||||||
|
# Reads the bot token from projects/discord-bot/.env (DISCORD_TOKEN=...).
|
||||||
|
#
|
||||||
|
# Exit codes:
|
||||||
|
# 0 — deleted (HTTP 200 or 204)
|
||||||
|
# 1 — bad args, token not found, or API error
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
THREAD_ID="${1:-}"
|
||||||
|
if [ -z "$THREAD_ID" ]; then
|
||||||
|
echo "[ERROR] Usage: delete-thread.sh <thread_id>" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
ENV_FILE="$SCRIPT_DIR/../.env"
|
||||||
|
|
||||||
|
if [ ! -f "$ENV_FILE" ]; then
|
||||||
|
echo "[ERROR] .env not found at $ENV_FILE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Extract token — handles quoted and unquoted values, ignores inline comments
|
||||||
|
DISCORD_TOKEN=$(grep '^DISCORD_TOKEN=' "$ENV_FILE" \
|
||||||
|
| head -1 \
|
||||||
|
| sed 's/^DISCORD_TOKEN=//' \
|
||||||
|
| sed "s/[\"']//g" \
|
||||||
|
| sed 's/#.*//' \
|
||||||
|
| tr -d '[:space:]')
|
||||||
|
|
||||||
|
if [ -z "$DISCORD_TOKEN" ]; then
|
||||||
|
echo "[ERROR] DISCORD_TOKEN not found or empty in $ENV_FILE" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
RESP=$(curl -s -o /tmp/discord_delete_resp.txt -w "%{http_code}" \
|
||||||
|
-X DELETE \
|
||||||
|
"https://discord.com/api/v10/channels/${THREAD_ID}" \
|
||||||
|
-H "Authorization: Bot ${DISCORD_TOKEN}" \
|
||||||
|
-H "Content-Type: application/json")
|
||||||
|
|
||||||
|
HTTP_CODE="$RESP"
|
||||||
|
BODY=$(cat /tmp/discord_delete_resp.txt 2>/dev/null || echo "")
|
||||||
|
rm -f /tmp/discord_delete_resp.txt
|
||||||
|
|
||||||
|
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "204" ]; then
|
||||||
|
echo "[OK] Thread ${THREAD_ID} deleted (HTTP ${HTTP_CODE})"
|
||||||
|
exit 0
|
||||||
|
else
|
||||||
|
echo "[ERROR] Delete failed: HTTP ${HTTP_CODE} — ${BODY}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
@@ -2,11 +2,13 @@
|
|||||||
type: client
|
type: client
|
||||||
name: glaztech
|
name: glaztech
|
||||||
display_name: Glaz-Tech Industries
|
display_name: Glaz-Tech Industries
|
||||||
last_compiled: 2026-05-24
|
last_compiled: 2026-06-02
|
||||||
compiled_by: DESKTOP-0O8A1RL/claude-main
|
compiled_by: DESKTOP-0O8A1RL/claude-main
|
||||||
sources:
|
sources:
|
||||||
- clients/glaztech/session-logs/2026-04-20-session.md
|
- clients/glaztech/session-logs/2026-04-20-session.md
|
||||||
- clients/glaztech/session-logs/2026-04-21-session.md
|
- clients/glaztech/session-logs/2026-04-21-session.md
|
||||||
|
- clients/glaztech/session-logs/2026-05-28-session.md
|
||||||
|
- clients/glaztech/session-logs/2026-06-02-session.md
|
||||||
- clients/glaztech/reports/2026-04-17-phishing-incident-report.md
|
- clients/glaztech/reports/2026-04-17-phishing-incident-report.md
|
||||||
- clients/glaztech/PROJECT_STATE.md
|
- clients/glaztech/PROJECT_STATE.md
|
||||||
- clients/glaztech/README.md
|
- clients/glaztech/README.md
|
||||||
@@ -43,12 +45,39 @@ No dedicated on-premises server infrastructure documented. Multi-site Windows en
|
|||||||
- **Tenant ID:** 82931e3c-de7a-4f74-87f7-fe714be1f160
|
- **Tenant ID:** 82931e3c-de7a-4f74-87f7-fe714be1f160
|
||||||
- **Primary domain:** glaztech.com
|
- **Primary domain:** glaztech.com
|
||||||
- **Inbound mail filter:** MailProtector — `glaztech-com.inbound.emailservice.io` (MX 5, sole MX as of 2026-04-17)
|
- **Inbound mail filter:** MailProtector — `glaztech-com.inbound.emailservice.io` (MX 5, sole MX as of 2026-04-17)
|
||||||
|
- **MailProtector IPs (EFSkipIPs on inbound connector):** 162.248.93.233, 162.248.93.81, 65.113.52.82
|
||||||
- **DMARC:** p=reject; sp=reject (hardened 2026-04-17, was p=none)
|
- **DMARC:** p=reject; sp=reject (hardened 2026-04-17, was p=none)
|
||||||
- **DKIM:** CNAME records exist for selector1/selector2 — active status unverified [WARNING: confirm DKIM is active in M365]
|
- **DKIM:** CNAME records exist for selector1/selector2 — active status unverified [WARNING: confirm DKIM is active in M365]
|
||||||
- **MFA status:** [WARNING] DISABLED as of 2026-04-21. Security Defaults off. No Conditional Access (requires Entra P1, not licensed). ~160 users with password-only sign-in. MFA rollout is open work item — do not enable Security Defaults until service account audit is complete (see Active Work).
|
- **MFA status:** [WARNING] DISABLED as of 2026-04-21. Security Defaults off. No Conditional Access (requires Entra P1, not licensed). ~160 users with password-only sign-in. MFA rollout is open work item — do not enable Security Defaults until service account audit is complete (see Active Work).
|
||||||
- **Licensing:** Basic M365 (no Entra P1 / Business Premium). Per-user MFA or Security Defaults are the available free options.
|
- **Licensing:** Basic M365 (no Entra P1 / Business Premium). Per-user MFA or Security Defaults are the available free options.
|
||||||
- **Mailbox forwarding (internal, low risk):** Payroll@glaztech.com → carmen@glaztech.com; TUCCSR@glaztech.com → bryce@glaztech.com
|
- **Mailbox forwarding (internal, low risk):** Payroll@glaztech.com → carmen@glaztech.com; TUCCSR@glaztech.com → bryce@glaztech.com
|
||||||
- **OAuth consent grants:** 38 grants — not audited as of last session
|
- **OAuth consent grants:** 38 grants — not audited as of last session
|
||||||
|
- **EXO PowerShell:** ExchangeOnlineManagement 3.9.2. `Get-MessageTrace` deprecated Sept 2025 — use `Get-MessageTraceV2` (no `-PageSize` parameter).
|
||||||
|
|
||||||
|
### Exchange Online Transport Rules
|
||||||
|
|
||||||
|
Full transport rule list as of 2026-06-02:
|
||||||
|
|
||||||
|
| Priority | Name | Condition | Action | State |
|
||||||
|
|---|---|---|---|---|
|
||||||
|
| 0 | Pensky Allow | [unknown] | [unknown] | Enabled |
|
||||||
|
| 1 | GTIMail No-Reply - Reject Inbound | SentTo: gtimail@glaztech.com | RejectMessageAction | Enabled |
|
||||||
|
| 2 | SCL Bypass - hartsglass + olemons (SHVSALES) | From: hartsglass@centurytel.net, olemons@eastexglass.com, SSales@arkglass.com, bossier@glassservices.com | SetSCL -1 | Enabled |
|
||||||
|
| 3 | SCL Bypass - aaaglassinc.com (SHVSALES) | SenderDomainIs: aaaglassinc.com | SetSCL -1 | Enabled |
|
||||||
|
| 4 | SCL Bypass - noreply@azcomputerguru.com (MailProtector digests) | From: noreply@azcomputerguru.com | SetSCL -1 | Enabled |
|
||||||
|
|
||||||
|
Rule GUIDs: Priority 2 = 482c714a-8780-4c62-ae0a-0b6da9ca9d52; Priority 3 = 7e0c01a8-ec22-43fe-b600-796c0f295aa5. GUIDs for Priority 0, 1, 4 not recorded.
|
||||||
|
|
||||||
|
Note on Priority 1: The "GTIMail No-Reply - Reject Inbound" rule rejects ALL inbound mail to gtimail@glaztech.com, which causes the daily MailProtector digest for that address to fail. This is a pre-existing rule — review with Steve is pending (see Active Work).
|
||||||
|
|
||||||
|
### Inbound Connector
|
||||||
|
|
||||||
|
- **Name:** "Inbound Spam Filter"
|
||||||
|
- **Type:** Partner
|
||||||
|
- **RequireTls:** True
|
||||||
|
- **EFSkipIPs:** 162.248.93.233, 162.248.93.81, 65.113.52.82 (MailProtector IPs)
|
||||||
|
- **SCLMinusOne:** null (EOP re-evaluates all mail; do NOT change to true — too broad)
|
||||||
|
- **SenderIPAddresses restriction:** None (intentional — avoids blocking calendar invites from external M365 tenants)
|
||||||
|
|
||||||
### Network
|
### Network
|
||||||
|
|
||||||
@@ -61,11 +90,13 @@ No dedicated on-premises server infrastructure documented. Multi-site Windows en
|
|||||||
|
|
||||||
- **Remediation tool:** ComputerGuru apps consented in tenant (Exchange Operator, Security Investigator, Tenant Admin, Defender Add-on)
|
- **Remediation tool:** ComputerGuru apps consented in tenant (Exchange Operator, Security Investigator, Tenant Admin, Defender Add-on)
|
||||||
- **Exchange Operator App ID:** b43e7342-5b4b-492f-890f-bb5a4f7f40e9
|
- **Exchange Operator App ID:** b43e7342-5b4b-492f-890f-bb5a4f7f40e9
|
||||||
|
- **Exchange Operator cert thumbprint:** A615823DE1CAF15229027DEC075AFE32B900D82C (not in Windows cert store on BEAST — use `get-token.sh` bearer token flow)
|
||||||
- **Remediation tool app (AI):** fabb3421-8b34-484b-bc17-e46de9703418
|
- **Remediation tool app (AI):** fabb3421-8b34-484b-bc17-e46de9703418
|
||||||
- **Exchange Admin role:** Assigned to ACG service principal in Entra
|
- **Exchange Admin role:** Assigned to ACG service principal in Entra
|
||||||
- **Global Admin account:** admin@glaztechindustries.onmicrosoft.com (ACG admin only — external GA from tomakkglass.com removed 2026-04-21)
|
- **Global Admin account:** admin@glaztechindustries.onmicrosoft.com (ACG admin only — external GA from tomakkglass.com removed 2026-04-21)
|
||||||
- **Vault path:** `clients/glaztech/` [no SOPS credential file documented — remediation tool uses MSP-wide app credentials]
|
- **Vault path:** `clients/glaztech/` [no SOPS credential file documented — remediation tool uses MSP-wide app credentials]
|
||||||
- **Exchange Operator vault:** `msp-tools/computerguru-exchange-operator.sops.yaml`
|
- **Exchange Operator vault:** `msp-tools/computerguru-exchange-operator.sops.yaml`
|
||||||
|
- **Token acquisition:** `bash .claude/skills/remediation-tool/scripts/get-token.sh <tenant-id> exchange-op` → `Connect-ExchangeOnline -AccessToken $token -Organization 'glaztechindustries.onmicrosoft.com'`
|
||||||
- **DNS access:** `root@172.16.3.10` (IX server)
|
- **DNS access:** `root@172.16.3.10` (IX server)
|
||||||
- **Deploy (endpoints):** ScreenConnect or GuruRMM
|
- **Deploy (endpoints):** ScreenConnect or GuruRMM
|
||||||
|
|
||||||
@@ -73,9 +104,14 @@ No dedicated on-premises server infrastructure documented. Multi-site Windows en
|
|||||||
|
|
||||||
- **Phishing via direct-to-M365 MX bypass:** Two phishing campaigns in April 2026 succeeded because DNS had a secondary MX record (`glaztech-com.mail.protection.outlook.com` at priority 10) that bypassed MailProtector. Hardened: MX 10 removed, DMARC to p=reject, Enhanced Filtering for Connectors enabled. Do not re-add a secondary MX record.
|
- **Phishing via direct-to-M365 MX bypass:** Two phishing campaigns in April 2026 succeeded because DNS had a secondary MX record (`glaztech-com.mail.protection.outlook.com` at priority 10) that bypassed MailProtector. Hardened: MX 10 removed, DMARC to p=reject, Enhanced Filtering for Connectors enabled. Do not re-add a secondary MX record.
|
||||||
- **Inbound connector IP restriction:** Do NOT restrict `SenderIPAddresses` on the "Inbound Spam Filter" connector — blocks legitimate calendar invites from external M365 tenants (learned from Dataforth incident). EFSkipIPs are set to MailProtector IPs instead.
|
- **Inbound connector IP restriction:** Do NOT restrict `SenderIPAddresses` on the "Inbound Spam Filter" connector — blocks legitimate calendar invites from external M365 tenants (learned from Dataforth incident). EFSkipIPs are set to MailProtector IPs instead.
|
||||||
|
- **Do NOT set SCLMinusOne=true on connector:** This would trust MailProtector's verdict for all inbound mail — too broad. Use targeted transport rules for specific senders instead.
|
||||||
|
- **DMARC-rejecting vendor senders:** With Enhanced Filtering enabled, EOP looks past MailProtector to the original sender's SPF/DKIM/DMARC. Vendors with `p=reject` domains (e.g., centurytel.net, eastexglass.com) get hard 550 5.7.509 NDR rejections. Fix: SCL=-1 transport rule scoped to the specific sender address or domain. Transport rules evaluate before DMARC enforcement in EOP.
|
||||||
|
- **EXO transport rule name limit:** 64-character maximum. Plan names accordingly.
|
||||||
|
- **EXO REST API:** Direct `/TransportRule` REST endpoints 404 in this tenant. Use `InvokeCommand` pattern: `POST /adminapi/beta/{tenant}/InvokeCommand` with `{"CmdletInput": {"CmdletName": "New-TransportRule", "Parameters": {...}}}`.
|
||||||
- **Service accounts need audit before MFA rollout:** Shoretel, mitel, Gti-FaxFinder, GTIMail, GTIQUOTE, CAS1944, clerk — all need SMTP/auth method confirmation before Security Defaults can be enabled.
|
- **Service accounts need audit before MFA rollout:** Shoretel, mitel, Gti-FaxFinder, GTIMail, GTIQUOTE, CAS1944, clerk — all need SMTP/auth method confirmation before Security Defaults can be enabled.
|
||||||
- **PDF preview broken (MOTW):** Windows KB5066791/KB5066835 broke PDF preview on network shares via Mark of the Web. Fix scripts are ready in `clients/glaztech/` — deployment is pending (as of 2026-03-30).
|
- **PDF preview broken (MOTW):** Windows KB5066791/KB5066835 broke PDF preview on network shares via Mark of the Web. Fix scripts are ready in `clients/glaztech/` — deployment is pending (as of 2026-03-30).
|
||||||
- **clearcutglass.com DMARC history:** Corena Spottsville (clearcutglass.com) emails to seastman and zulema were rejected. Temporary transport rule (SCL=-1) was set and removed on 2026-04-21. SPF ~all weakness noted to Team Logic IT (Jordan Fox, jfox@tlit60302.com); recommend they harden to -all and confirm DKIM.
|
- **clearcutglass.com DMARC history:** Corena Spottsville (clearcutglass.com) emails to seastman and zulema were rejected. Temporary transport rule (SCL=-1) was set and removed on 2026-04-21. SPF ~all weakness noted to Team Logic IT (Jordan Fox, jfox@tlit60302.com); recommend they harden to -all and confirm DKIM.
|
||||||
|
- **glassservices.com SPF broken:** `bossier@glassservices.com` publishes `v=spf1 -all` — rejected by all mail providers. SCL=-1 rule covers this as a workaround. Steve should notify vendor to fix SPF.
|
||||||
- **Client tone:** ACG has managed GlazTech ~15 years. Steve Eastman is a trusted internal IT partner. Comments and communication should lead with what we know, state findings and actions taken, ask only one targeted question if needed — not open-ended discovery.
|
- **Client tone:** ACG has managed GlazTech ~15 years. Steve Eastman is a trusted internal IT partner. Comments and communication should lead with what we know, state findings and actions taken, ask only one targeted question if needed — not open-ended discovery.
|
||||||
- **Unlicensed accounts (pending Steve confirmation):** Chauntelle@glaztech.com, Denouser1@glaztech.com, Gti-FaxFinder@glaztech.com.
|
- **Unlicensed accounts (pending Steve confirmation):** Chauntelle@glaztech.com, Denouser1@glaztech.com, Gti-FaxFinder@glaztech.com.
|
||||||
|
|
||||||
@@ -103,6 +139,10 @@ Waiting on Steve's reply to:
|
|||||||
|
|
||||||
MFA rollout plan: Phase 1 — user communication (install Authenticator); Phase 2 — enable enforcement; Phase 3 — follow-up stragglers; Phase 4 (future/P1) — Conditional Access with trusted IPs for office locations.
|
MFA rollout plan: Phase 1 — user communication (install Authenticator); Phase 2 — enable enforcement; Phase 3 — follow-up stragglers; Phase 4 (future/P1) — Conditional Access with trusted IPs for office locations.
|
||||||
|
|
||||||
|
### gtimail@glaztech.com Daily Digest Failure (Pending — review with Steve)
|
||||||
|
|
||||||
|
The "GTIMail No-Reply - Reject Inbound" transport rule (Priority 1) rejects all inbound mail to `gtimail@glaztech.com`, causing the daily MailProtector digest for that address to fail every day. This is a pre-existing rule and was not modified during the 2026-06-02 session. Confirm with Steve Eastman whether `gtimail@glaztech.com` should receive MailProtector digests — if so, the rule needs an exception or the recipient needs to be removed from the MailProtector digest list.
|
||||||
|
|
||||||
### Pending follow-ups
|
### Pending follow-ups
|
||||||
|
|
||||||
- Audit 38 OAuth consent grants (not done as of 2026-04-21)
|
- Audit 38 OAuth consent grants (not done as of 2026-04-21)
|
||||||
@@ -110,7 +150,9 @@ MFA rollout plan: Phase 1 — user communication (install Authenticator); Phase
|
|||||||
- Monitor DMARC aggregate reports (rua=noreply@glaztech.com — should be a monitored mailbox or reporting service)
|
- Monitor DMARC aggregate reports (rua=noreply@glaztech.com — should be a monitored mailbox or reporting service)
|
||||||
- Security awareness training for staff (multiple employees forwarded and replied to obvious phishing in April 2026)
|
- Security awareness training for staff (multiple employees forwarded and replied to obvious phishing in April 2026)
|
||||||
- Review whether any user clicked phishing links (check sign-in logs for suspicious auth attempts post-April 17)
|
- Review whether any user clicked phishing links (check sign-in logs for suspicious auth attempts post-April 17)
|
||||||
- Confirm test email clean delivery from clearcutglass.com after DMARC fix
|
- Notify Steve: glassservices.com vendor needs to fix their SPF record (`v=spf1 -all`)
|
||||||
|
- Harts Glass original rejected emails need to be resent by sender — our SCL bypass is live but NDR'd messages do not auto-retry
|
||||||
|
- Consider creating retroactive Syncro ticket for 2026-05-28 SHVSALES email delivery work
|
||||||
|
|
||||||
## History Highlights
|
## History Highlights
|
||||||
|
|
||||||
@@ -119,6 +161,8 @@ MFA rollout plan: Phase 1 — user communication (install Authenticator); Phase
|
|||||||
- **2026-04-17** — Two phishing campaigns bypassed MailProtector via direct-to-M365 MX bypass. 32 messages purged across 8 users. Hardened: MX 10 removed, DMARC p=reject, Enhanced Filtering Connectors enabled. Remediation tool onboarded (admin consent, Exchange Admin role). Forensic evidence preserved in `clients/glaztech/reports/`.
|
- **2026-04-17** — Two phishing campaigns bypassed MailProtector via direct-to-M365 MX bypass. 32 messages purged across 8 users. Hardened: MX 10 removed, DMARC p=reject, Enhanced Filtering Connectors enabled. Remediation tool onboarded (admin consent, Exchange Admin role). Forensic evidence preserved in `clients/glaztech/reports/`.
|
||||||
- **2026-04-20** — Exchange transport rule created to allow clearcutglass.com mail (DMARC bypass, SCL=-1) while Team Logic IT fixed their DNS. Ticket #32176 created.
|
- **2026-04-20** — Exchange transport rule created to allow clearcutglass.com mail (DMARC bypass, SCL=-1) while Team Logic IT fixed their DNS. Ticket #32176 created.
|
||||||
- **2026-04-21** — clearcutglass.com DNS fixed by Team Logic IT (Jordan Fox). Transport rule removed. External Global Admin (glaztechadmin from tomakkglass.com / Team Logic IT) removed from tenant. M365 security review surfaced: no MFA, 38 OAuth grants, unlicensed accounts, service account audit needed. Ticket #32186 opened for MFA implementation. Feedback: use expert-partner tone with Steve, not open-ended discovery questions.
|
- **2026-04-21** — clearcutglass.com DNS fixed by Team Logic IT (Jordan Fox). Transport rule removed. External Global Admin (glaztechadmin from tomakkglass.com / Team Logic IT) removed from tenant. M365 security review surfaced: no MFA, 38 OAuth grants, unlicensed accounts, service account audit needed. Ticket #32186 opened for MFA implementation. Feedback: use expert-partner tone with Steve, not open-ended discovery questions.
|
||||||
|
- **2026-05-28** — SHVSALES@glaztech.com vendor email delivery failure. Root cause: vendors (centurytel.net, eastexglass.com) publish DMARC p=reject; Enhanced Filtering re-evaluates past MailProtector relay, producing 550 5.7.509 NDR. Fix: two SCL=-1 transport rules created (Priority 2: specific addresses for hartsglass, olemons, SSales, bossier; Priority 3: aaaglassinc.com domain). glassservices.com SPF broken (`-all`) — workaround only, vendor must fix.
|
||||||
|
- **2026-06-02** — MailProtector quarantine digest messages from `noreply@azcomputerguru.com` confirmed hitting `FilteredAsSpam` for some recipients (e.g., tshaw@glaztech.com). Transport rule created: "SCL Bypass - noreply@azcomputerguru.com (MailProtector digests)" at Priority 4 (From=noreply@azcomputerguru.com, SetSCL=-1). Message trace via `Get-MessageTraceV2` also revealed `gtimail@glaztech.com` failing daily due to pre-existing Priority 1 reject rule — flagged for Steve review.
|
||||||
|
|
||||||
## Backlinks
|
## Backlinks
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ Run `/wiki-lint` to check for stale entries and broken backlinks.
|
|||||||
| [ACG Internal Infrastructure](clients/internal-infrastructure.md) | ACG's own hosting infra — Neptune Exchange (cert expires 2026-05-31, DkimSigner disabled), IX server, Cloudflare tunnel workaround, ACG M365 tenant gaps | 2026-05-24 |
|
| [ACG Internal Infrastructure](clients/internal-infrastructure.md) | ACG's own hosting infra — Neptune Exchange (cert expires 2026-05-31, DkimSigner disabled), IX server, Cloudflare tunnel workaround, ACG M365 tenant gaps | 2026-05-24 |
|
||||||
| [BirthBiologic](clients/birth-biologic.md) | Bio/healthcare; BB-SERVER (WS2016) GuruRMM enrolled; Datto→SharePoint migration incomplete; M365 apps partially consented | 2026-05-24 |
|
| [BirthBiologic](clients/birth-biologic.md) | Bio/healthcare; BB-SERVER (WS2016) GuruRMM enrolled; Datto→SharePoint migration incomplete; M365 apps partially consented | 2026-05-24 |
|
||||||
| [CryoWeave](clients/cryoweave.md) | Custom cryogenic cable assemblies; cPanel on IX; website redesign + SEO project in progress; Syncro ID not documented | 2026-05-24 |
|
| [CryoWeave](clients/cryoweave.md) | Custom cryogenic cable assemblies; cPanel on IX; website redesign + SEO project in progress; Syncro ID not documented | 2026-05-24 |
|
||||||
| [Glaz-Tech Industries](clients/glaztech.md) | ~200 users, 9 locations; M365; two phishing campaigns bypassed MailProtector via secondary MX (removed); no MFA enforcement yet | 2026-05-24 |
|
| [Glaz-Tech Industries](clients/glaztech.md) | ~200 users, 9 locations; M365; two phishing campaigns bypassed MailProtector via secondary MX (removed); no MFA enforcement yet; SCL bypass rules for vendor DMARC failures + MailProtector digests | 2026-06-02 |
|
||||||
| [Grabb & Durando Law Office](clients/grabb-durando.md) | Personal injury law firm; GND-SERVER GuruRMM enrolled; AI demand review app scoped ($4K–$7K); website migration pending; plaintext DB password in README needs vaulting | 2026-05-24 |
|
| [Grabb & Durando Law Office](clients/grabb-durando.md) | Personal injury law firm; GND-SERVER GuruRMM enrolled; AI demand review app scoped ($4K–$7K); website migration pending; plaintext DB password in README needs vaulting | 2026-05-24 |
|
||||||
| [Pavon](clients/pavon.md) | Former/archive client; GeoVision NVR surveillance; OwnCloud at 172.16.3.22 backed by Uranus; cron stacking fixed; Nextcloud migration deferred 3–6 months | 2026-05-24 |
|
| [Pavon](clients/pavon.md) | Former/archive client; GeoVision NVR surveillance; OwnCloud at 172.16.3.22 backed by Uranus; cron stacking fixed; Nextcloud migration deferred 3–6 months | 2026-05-24 |
|
||||||
| [Rednour Law Offices](clients/rednour.md) | Law firm; M365 rednourlaw.com (tenant 4a4ca18a) fully onboarded 2026-05-31; all 5 ComputerGuru SPs consented; no MDE license; 3 workstations GuruRMM enrolled (FRONTDESKRECEPT/LEGALASST/REDNOURCARRIEVI); Carla Skinner renamed from Emma; prior MSP agents (ScreenConnect/Splashtop/Datto) still present; shared-drive access for Nick Pafford deferred | 2026-06-02 |
|
| [Rednour Law Offices](clients/rednour.md) | Law firm; M365 rednourlaw.com (tenant 4a4ca18a) fully onboarded 2026-05-31; all 5 ComputerGuru SPs consented; no MDE license; 3 workstations GuruRMM enrolled (FRONTDESKRECEPT/LEGALASST/REDNOURCARRIEVI); Carla Skinner renamed from Emma; prior MSP agents (ScreenConnect/Splashtop/Datto) still present; shared-drive access for Nick Pafford deferred | 2026-06-02 |
|
||||||
|
|||||||
Reference in New Issue
Block a user