sync: auto-sync from GURU-5070 at 2026-06-15 11:20:33
Author: Mike Swanson Machine: GURU-5070 Timestamp: 2026-06-15 11:20:33
This commit is contained in:
25
.claude/commands/rmm-search.md
Normal file
25
.claude/commands/rmm-search.md
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
name: rmm-search
|
||||
description: Cleanly find machines in the GuruRMM fleet with a flexible, client-aware search (no more grepping /api/agents and hitting the wrong client's box). Front door for locating an agent before acting on it via /rmm.
|
||||
---
|
||||
|
||||
# /rmm-search — find GuruRMM machines on the first try
|
||||
|
||||
Thin entry point to the `rmm-search` skill. Engine: `.claude/scripts/rmm-search.sh`.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
/rmm-search <words...> [-c <client>] [--online] [--json] [-n N]
|
||||
/rmm-search -c <client> List ALL machines for a client
|
||||
/rmm-search --list-clients Show distinct client names
|
||||
```
|
||||
|
||||
Every query word must match some field (hostname/client/site/OS/id), so words
|
||||
**narrow** — `hyperv valleywide` returns only Valley Wide's hyperv host, never
|
||||
Dataforth's. Matching is normalized (case/space/hyphen-insensitive) with
|
||||
prefix/substring/subsequence ranking; `-c` is an explicit hard client scope and
|
||||
refuses to guess when the client name is ambiguous.
|
||||
|
||||
Use this to *find* an agent; pass the resulting hostname/id to `/rmm` to *act*.
|
||||
Full matching rules + examples: `.claude/skills/rmm-search/SKILL.md`.
|
||||
@@ -12,6 +12,7 @@
|
||||
- [Community Forum (Flarum)](reference_community_forum.md) — Flarum forum at community.azcomputerguru.com, API access, database, posting workflow.
|
||||
- [Radio Show Website](reference_radio_website.md) — Astro static site at radio.azcomputerguru.com on IX server.
|
||||
- [IX Server Access](reference_ix_server_access.md) — `ix.azcomputerguru.com` / 172.16.3.10. Reachable when Tailscale is on (no VPN). SSH currently uses sshpass with root password; key auth from GURU-5070 not configured yet (was CachyOS, now Win11 — verify).
|
||||
- [Cloudflare access](reference_cloudflare_access.md) — Cloudflare API creds in SOPS `services/cloudflare.sops.yaml` (full DNS + account tokens; azcomputerguru zone_id 1beb9917...). azcomputerguru.com DNS is on Cloudflare (not IX) — edit via Cloudflare API, not whmapi1.
|
||||
- [Matomo Analytics](reference_matomo_analytics.md) — Self-hosted analytics at analytics.azcomputerguru.com, site IDs, tracking for all 3 sites.
|
||||
- [TickTick Integration](reference_ticktick_integration.md) — OAuth API integration, MCP server, SOPS vault creds, project/task CRUD.
|
||||
- [Client Docs Structure](reference_client_docs_structure.md) — clients/<name>/docs/ layout (overview, network, servers, cloud, security, rmm). Template: clients/_client_template/.
|
||||
@@ -39,6 +40,7 @@
|
||||
- [Verify committed state before push](feedback_verify_committed_state_before_push.md) — webhook builds from origin/main: verify the COMMITTED build (git stash + build), not the working tree; bad git-add pathspec silently aborts staging. Stage by directory.
|
||||
- [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 <words> [-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 "<link>"`.
|
||||
- [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.
|
||||
|
||||
12
.claude/memory/feedback_rmm_search_skill.md
Normal file
12
.claude/memory/feedback_rmm_search_skill.md
Normal file
@@ -0,0 +1,12 @@
|
||||
---
|
||||
name: feedback-rmm-search-skill
|
||||
description: Use the rmm-search skill to find GuruRMM machines, never grep /api/agents by hand
|
||||
metadata:
|
||||
type: feedback
|
||||
---
|
||||
|
||||
To locate a machine/agent in GuruRMM, use the **`rmm-search`** skill (`bash .claude/scripts/rmm-search.sh <words> [-c <client>]`) — do NOT pull `/api/agents` and grep client-side.
|
||||
|
||||
**Why:** Hand-grepping bleeds across clients and picks the wrong box — e.g. searching `hyperv` returns both Valley Wide's and Dataforth's hyperv hosts, and it's easy to act on the wrong one. Mike built the UI Omnibox for this and asked for a CLI equivalent (2026-06-15). rmm-search treats every query word as a required filter across hostname/client/site/OS (so `hyperv valleywide` can only return Valley Wide's box), is normalized (case/space/hyphen-insensitive) with typo tolerance, and `-c <client>` hard-scopes (refuses to guess on ambiguous client names).
|
||||
|
||||
**How to apply:** `rmm-search.sh hyperv valleywide` or `... hyperv -c valleywide` to find; `--json | jq -r '.[0].id'` to get the agent id; then hand hostname/id to the [[reference_gururmm]] `rmm` skill to actually run commands. Online state is from last_seen (<5min), not the unreliable `is_connected` flag. Engine: `rmm-search.sh` + `rmm-search.py`.
|
||||
14
.claude/memory/reference_cloudflare_access.md
Normal file
14
.claude/memory/reference_cloudflare_access.md
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
name: reference-cloudflare-access
|
||||
description: Where the Cloudflare API credentials live (SOPS vault) — azcomputerguru.com DNS is on Cloudflare, not the IX nameservers
|
||||
metadata:
|
||||
type: reference
|
||||
---
|
||||
|
||||
Cloudflare API access is in the SOPS vault at **`services/cloudflare.sops.yaml`** (account "Mike@azcomputerguru.com Account", account_id `44594c346617d918bd3302a00b07e122`). Fields under `credentials`:
|
||||
- `api_token_full_account` — full-account token (`solitary-rain-773d`, added 2026-05-10, expires 2027-05-10)
|
||||
- `api_token_full_dns` — full DNS-edit token (use this for DNS record changes)
|
||||
- `api_token_legacy` — legacy token
|
||||
- `zone_id_azcomputerguru` = `1beb9917c22b54be32e5215df2c227ce`
|
||||
|
||||
**azcomputerguru.com DNS is hosted on Cloudflare** (ns mckinley/amir.ns.cloudflare.com), NOT the IX/cPanel nameservers (ns1/ns2.acghosting.com) that most CLIENT domains use. So azcomputerguru.com zone edits go through the Cloudflare API, not `whmapi1`. Pattern: `curl -H "Authorization: Bearer <api_token_full_dns>" https://api.cloudflare.com/client/v4/zones/<zone_id>/dns_records`. (Used 2026-06-15 to add the cross-domain DMARC report-authorization record `cryoweave.com._report._dmarc.azcomputerguru.com TXT "v=DMARC1;"` so client DMARC reports can be sent to rua@azcomputerguru.com.) See [[reference_ix_server_access]] for client-domain DNS (cPanel).
|
||||
138
.claude/scripts/rmm-search.py
Normal file
138
.claude/scripts/rmm-search.py
Normal file
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env python3
|
||||
"""rmm-search engine. Reads the GuruRMM agents JSON array on stdin; reads
|
||||
QUERY/CLIENT/ONLINE/JSON/LISTC/LIMIT from the environment. Flexible, forgiving
|
||||
multi-field search — see rmm-search.sh for usage. Kept as a sidecar (not a
|
||||
heredoc) because the agents payload is too large to pass as a CLI argument."""
|
||||
import sys, os, json, re
|
||||
from datetime import datetime, timezone
|
||||
|
||||
raw = sys.stdin.read()
|
||||
agents = json.loads(raw) if raw.strip() else []
|
||||
query = os.environ.get("QUERY", "").strip()
|
||||
client = os.environ.get("CLIENT", "").strip()
|
||||
online_only = os.environ.get("ONLINE") == "1"
|
||||
as_json = os.environ.get("JSON") == "1"
|
||||
list_clients = os.environ.get("LISTC") == "1"
|
||||
try:
|
||||
limit = int(os.environ.get("LIMIT", "0") or 0)
|
||||
except ValueError:
|
||||
limit = 0
|
||||
|
||||
|
||||
def norm(s):
|
||||
return re.sub(r'[^a-z0-9]', '', (s or '').lower())
|
||||
|
||||
|
||||
def is_subseq(t, v):
|
||||
it = iter(v)
|
||||
return all(c in it for c in t)
|
||||
|
||||
|
||||
def field_score(val, term, allow_subseq):
|
||||
"""How well a single query word matches one normalized field value."""
|
||||
if not val or not term:
|
||||
return 0
|
||||
if val == term:
|
||||
return 100
|
||||
if val.startswith(term):
|
||||
return 80
|
||||
if term in val:
|
||||
return 60
|
||||
if allow_subseq and len(term) >= 3 and is_subseq(term, val):
|
||||
return 25
|
||||
return 0
|
||||
|
||||
|
||||
# field -> (key, weight, allow_subsequence)
|
||||
FIELDS = [
|
||||
('hostname', 1.00, True),
|
||||
('client_name', 0.75, False),
|
||||
('site_name', 0.60, False),
|
||||
('os_type', 0.55, False),
|
||||
('id', 0.40, False),
|
||||
]
|
||||
|
||||
|
||||
def fresh(last_seen):
|
||||
if not last_seen:
|
||||
return False
|
||||
try:
|
||||
dt = datetime.fromisoformat(last_seen.replace('Z', '+00:00'))
|
||||
return (datetime.now(timezone.utc) - dt).total_seconds() < 300
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
|
||||
if list_clients:
|
||||
for n in sorted({(a.get('client_name') or 'Unassigned') for a in agents}):
|
||||
print(n)
|
||||
sys.exit(0)
|
||||
|
||||
# --- explicit hard client scope (normalized; "valleywide" -> "Valley Wide Plastering") ---
|
||||
if client:
|
||||
cn = norm(client)
|
||||
matched = sorted({(a.get('client_name') or '') for a in agents
|
||||
if cn and cn in norm(a.get('client_name'))} - {''})
|
||||
if not matched:
|
||||
print(f"[ERROR] no client matches '{client}'. Try: rmm-search.sh --list-clients", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
if len(matched) > 1:
|
||||
exact = [m for m in matched if norm(m) == cn]
|
||||
if len(exact) == 1:
|
||||
matched = exact
|
||||
else:
|
||||
print(f"[AMBIGUOUS] '{client}' matches {len(matched)} clients - narrow --client:", file=sys.stderr)
|
||||
for m in matched:
|
||||
print(" - " + m, file=sys.stderr)
|
||||
sys.exit(3)
|
||||
agents = [a for a in agents if (a.get('client_name') or '') == matched[0]]
|
||||
|
||||
if online_only:
|
||||
agents = [a for a in agents if fresh(a.get('last_seen'))]
|
||||
|
||||
terms = [t for t in (norm(x) for x in query.split()) if t]
|
||||
joined = norm(query)
|
||||
|
||||
|
||||
def score_agent(a):
|
||||
if not terms:
|
||||
return 1 # no query (e.g. "all machines for client X")
|
||||
nf = [(norm(a.get(k)), w, sub) for k, w, sub in FIELDS]
|
||||
total = 0.0
|
||||
for term in terms:
|
||||
best = max(field_score(val, term, sub) * w for val, w, sub in nf)
|
||||
if best == 0:
|
||||
return 0 # AND: every word must hit some field
|
||||
total += best
|
||||
if joined and joined in norm(a.get('hostname')):
|
||||
total += 60 # whole query lands in hostname -> clearly THE machine
|
||||
return total
|
||||
|
||||
|
||||
results = sorted(((score_agent(a), a) for a in agents),
|
||||
key=lambda t: (-t[0], (t[1].get('hostname') or '').lower()))
|
||||
results = [(s, a) for s, a in results if s > 0]
|
||||
if limit > 0:
|
||||
results = results[:limit]
|
||||
|
||||
if as_json:
|
||||
print(json.dumps([{
|
||||
'hostname': a.get('hostname'), 'id': a.get('id'), 'client': a.get('client_name'),
|
||||
'site': a.get('site_name'), 'os': a.get('os_type'),
|
||||
'online': fresh(a.get('last_seen')), 'last_seen': a.get('last_seen'), 'score': round(s, 1),
|
||||
} for s, a in results], indent=2))
|
||||
sys.exit(0)
|
||||
|
||||
scope = f" in '{(agents[0].get('client_name') if (client and agents) else client)}'" if client else ""
|
||||
if not results:
|
||||
print(f"No machines match '{query}'{scope}. (try fewer/looser words, or --list-clients)")
|
||||
sys.exit(0)
|
||||
|
||||
hdr = f"{'HOSTNAME':<22} {'CLIENT':<26} {'SITE':<16} {'OS':<8} {'STAT':<7} ID"
|
||||
print(f"{len(results)} match(es) for '{query}'{scope} (best first):")
|
||||
print(hdr)
|
||||
print("-" * len(hdr))
|
||||
for s, a in results:
|
||||
st = 'online' if fresh(a.get('last_seen')) else 'offline'
|
||||
print(f"{(a.get('hostname') or '')[:21]:<22} {(a.get('client_name') or '?')[:25]:<26} "
|
||||
f"{(a.get('site_name') or '')[:15]:<16} {(a.get('os_type') or '')[:7]:<8} {st:<7} {a.get('id')}")
|
||||
46
.claude/scripts/rmm-search.sh
Normal file
46
.claude/scripts/rmm-search.sh
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/usr/bin/env bash
|
||||
# rmm-search.sh — find machines in the GuruRMM fleet on the first try.
|
||||
#
|
||||
# Flexible, forgiving search: normalizes case/spaces/hyphens, matches across
|
||||
# HOSTNAME + CLIENT + SITE + OS, and treats every query word as a required
|
||||
# filter (AND). So a query naturally narrows and can't bleed across clients:
|
||||
# rmm-search.sh hyperv valleywide -> only Valley Wide's hyperv host
|
||||
# rmm-search.sh hyperv -> every hyperv box, each labeled by client
|
||||
# rmm-search.sh hyperv -c valleywide -> hard-scope to one client, then search
|
||||
#
|
||||
# Usage:
|
||||
# rmm-search.sh <words...> [-c|--client <name>] [--online] [--json] [-n N]
|
||||
# rmm-search.sh -c <client> # list ALL machines for a client
|
||||
# rmm-search.sh --list-clients # show distinct client names
|
||||
#
|
||||
# Online state is derived from last_seen recency (<5 min); the API is_connected
|
||||
# flag is currently unreliable (null fleet-wide). Engine: rmm-search.py.
|
||||
set -u
|
||||
ROOT="${CLAUDETOOLS_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
|
||||
|
||||
QUERY=""; CLIENT=""; ONLINE=0; JSON=0; LISTC=0; LIMIT=0
|
||||
while [ $# -gt 0 ]; do
|
||||
case "$1" in
|
||||
-c|--client) CLIENT="${2:-}"; shift 2;;
|
||||
-n|--limit) LIMIT="${2:-0}"; shift 2;;
|
||||
--online) ONLINE=1; shift;;
|
||||
--json) JSON=1; shift;;
|
||||
--list-clients) LISTC=1; shift;;
|
||||
-h|--help) sed -n '2,20p' "$0"; exit 0;;
|
||||
*) QUERY="${QUERY:+$QUERY }$1"; shift;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$QUERY" ] && [ -z "$CLIENT" ] && [ "$LISTC" -eq 0 ]; then
|
||||
echo "[ERROR] give <words>, a --client, or --list-clients" >&2
|
||||
sed -n '12,17p' "$0" >&2; exit 1
|
||||
fi
|
||||
|
||||
eval "$(bash "$ROOT/.claude/scripts/rmm-auth.sh" 2>/dev/null)" >/dev/null
|
||||
if [ -z "${TOKEN:-}" ] || [ -z "${RMM:-}" ]; then echo "[ERROR] RMM auth failed (see rmm-auth.sh)" >&2; exit 1; fi
|
||||
AGENTS=$(curl -s "$RMM/api/agents" -H "Authorization: Bearer $TOKEN")
|
||||
if [ -z "$AGENTS" ] || [ "${AGENTS:0:1}" != "[" ]; then echo "[ERROR] could not fetch agents: ${AGENTS:0:160}" >&2; exit 1; fi
|
||||
|
||||
# Pipe agents on stdin (payload too large for argv on Windows); flags via env.
|
||||
printf '%s' "$AGENTS" | QUERY="$QUERY" CLIENT="$CLIENT" ONLINE="$ONLINE" JSON="$JSON" LISTC="$LISTC" LIMIT="$LIMIT" \
|
||||
python3 "$ROOT/.claude/scripts/rmm-search.py"
|
||||
@@ -23,6 +23,7 @@ that will fail the next email task; fix it with `assign-exchange-role.sh <domain
|
||||
| cascadestucson.com | cascadestucson.com | 207fa277-e9d8-4eb7-ada1-1064d2221498 | NO | Old app only; IdentityRiskyUser not consented |
|
||||
| cclac.net | cclac.net | e8a0fafc-21ee-41e8-a5ba-f3a250a8a30e | NO | |
|
||||
| Cobalt Fine Arts | cobaltfinearts.com | 03c4d4ec-b6d3-4061-a75c-8a4250ba2b29 | NO | |
|
||||
| Cryoweave | cryoweave.com | 44705a37-b5d8-4bb1-882d-e18775612ada | YES | All apps consented 2026-06-15 (Sec Inv + Exch Op Exchange Admin, User Mgr User Admin + Auth Admin, Tenant Admin CA Admin); no MDE. Onboarded for outbound-email deliverability investigation. ACG GA `sysadmin@cryoweave.com` created (vault clients/cryoweave/m365-sysadmin + 1Password Clients). |
|
||||
| CUADRO LLC | cuadro.design | b68c7171-31d6-4b63-8243-7a2cade9caf8 | NO | |
|
||||
| Curtis Plumbing | cparizona.onmicrosoft.com | d2d7ea54-9146-42d1-b99e-0da098550bde | NO | |
|
||||
| cwconcretellc.com | NETORGFT11452752.onmicrosoft.com | dfee2224-93cd-4291-9b09-6c6ce9bb8711 | NO | |
|
||||
|
||||
74
.claude/skills/rmm-search/SKILL.md
Normal file
74
.claude/skills/rmm-search/SKILL.md
Normal file
@@ -0,0 +1,74 @@
|
||||
---
|
||||
name: rmm-search
|
||||
description: >
|
||||
Find machines/agents in the GuruRMM fleet cleanly and on the first try. Use
|
||||
this ANY time you need to locate an RMM agent by name, role, client, site, or
|
||||
OS before acting on it — instead of pulling /api/agents and grepping (which
|
||||
bleeds across clients and picks the wrong box). Flexible, forgiving, multi-field
|
||||
search with a client filter so a query like "hyperv valleywide" returns ONLY
|
||||
Valley Wide's hyperv host, never Dataforth's. Invoke on: "find the X machine",
|
||||
"which agent is", "look up <host> in RMM", "<client>'s server/DC/hyperv/file
|
||||
server", "search RMM for", "what's the agent id for". After finding the agent,
|
||||
hand its hostname/id to the `rmm` skill to run commands.
|
||||
---
|
||||
|
||||
# rmm-search — clean machine lookup in GuruRMM
|
||||
|
||||
The `/rmm` skill resolves agents by grepping `/api/agents` client-side, which is
|
||||
exactly where lookups go wrong: a bare term like `hyperv` matches every client's
|
||||
hyperv box, and it's easy to act on the wrong one. This skill does a forgiving,
|
||||
ranked, **client-aware** search instead. Use it as the front door for *finding*
|
||||
an agent; use `/rmm` for *acting* on it.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
bash .claude/scripts/rmm-search.sh <words...> [-c|--client <name>] [--online] [--json] [-n N]
|
||||
bash .claude/scripts/rmm-search.sh -c <client> # list ALL machines for a client
|
||||
bash .claude/scripts/rmm-search.sh --list-clients # distinct client names
|
||||
```
|
||||
|
||||
## How matching works (built for first-try correctness)
|
||||
|
||||
- **Normalized:** case, spaces, and hyphens are ignored — `valleywide`,
|
||||
`valley wide`, and `Valley Wide Plastering` all match the same client.
|
||||
- **Multi-field:** every query word is matched against `hostname`, `client_name`,
|
||||
`site_name`, `os_type`, and `id`.
|
||||
- **AND semantics:** *every* word must hit some field, so adding words **narrows**.
|
||||
`hyperv valleywide` → only the box that is both a hyperv host AND Valley Wide.
|
||||
This is why a query can't bleed across clients without `-c`.
|
||||
- **Ranked:** exact > prefix > substring > subsequence (typo tolerance on
|
||||
hostnames, e.g. `hyprv`→`HYPERV`). Hostname matches outrank client/site/OS.
|
||||
Best result first; a whole-query hostname hit gets a boost.
|
||||
- **`-c/--client`** is an explicit hard scope. If the client term is ambiguous
|
||||
(matches >1 client and none exactly), it **lists the candidates and stops**
|
||||
rather than guessing — narrow the value and retry.
|
||||
- **`--online`** keeps only agents seen in the last 5 minutes. Online state is
|
||||
derived from `last_seen`, NOT the API `is_connected` flag (currently null
|
||||
fleet-wide — don't trust it).
|
||||
|
||||
## Output
|
||||
|
||||
A ranked table: `HOSTNAME | CLIENT | SITE | OS | STAT | ID` (best match first).
|
||||
`--json` emits `{hostname,id,client,site,os,online,last_seen,score}` for piping
|
||||
the `id` into a `/rmm` command. `-n N` caps the rows.
|
||||
|
||||
## Examples
|
||||
|
||||
```bash
|
||||
rmm-search.sh hyperv valleywide # VWP-HYPERV1 only (not DF-HYPERV-B)
|
||||
rmm-search.sh dc -c dataforth # Dataforth's domain controllers
|
||||
rmm-search.sh vwp fil # VWP-FILES (partial words ok)
|
||||
rmm-search.sh -c "peaceful spirit" # every Peaceful Spirit machine
|
||||
rmm-search.sh files valleywide --json | jq -r '.[0].id' # id -> feed to /rmm
|
||||
```
|
||||
|
||||
## Implementation
|
||||
|
||||
- Wrapper `.claude/scripts/rmm-search.sh` (arg parsing + auth via `rmm-auth.sh` +
|
||||
fetch `/api/agents`), engine `.claude/scripts/rmm-search.py` (scoring/filter).
|
||||
- The agents payload is piped to the engine on **stdin** — it's too large to pass
|
||||
as a CLI argument on Windows ("Argument list too long").
|
||||
- Read-only. To run a command on a found agent, pass its hostname/id to `/rmm`.
|
||||
- Ranking heuristic mirrors the dashboard Omnibox (`scoreMatch`) in spirit but is
|
||||
deliberately looser (multi-field + subsequence) to favor first-try hits.
|
||||
@@ -0,0 +1,77 @@
|
||||
## User
|
||||
- **User:** Mike Swanson (mike)
|
||||
- **Machine:** GURU-5070
|
||||
- **Role:** admin
|
||||
|
||||
## Session Summary
|
||||
|
||||
First documented ClaudeTools record for **Russo Law Firm** (Tucson personal-injury / law
|
||||
practice). Triggered by a pre-sales question: the client wants to **move ~6.5 TB of data from
|
||||
ACG-hosted Seafile into Microsoft 365 SharePoint**, and Mike needs the cost to quote them. A
|
||||
phone meeting is being scheduled; as of 2026-06-15 the client has not responded to the meeting
|
||||
request. Pulled their live Syncro record, evaluated the SharePoint storage economics, vaulted a
|
||||
plaintext M365 admin credential found in the Syncro notes, and built the initial wiki article.
|
||||
|
||||
## Syncro snapshot (customer 23331699)
|
||||
|
||||
- **Business:** Russo Law Firm — 3505 N Campbell Ave, Tucson, AZ 85719 — main 520-529-1515
|
||||
- **Account contact:** Shannon Trionfo (stever@rrs-law.com on the account; mobile 520-248-0244)
|
||||
- **Contacts:** Steve Russo (520-975-9024), Carolyn Russo (520-591-4303),
|
||||
Pat Broom (pebroom@rrs-law.com, 520-850-6832), Shannon Trionfo (520-248-0244)
|
||||
- **Prepaid:** 12.0 hrs (notes: moved 13.5 hrs to Syncro 1/16/26; 17.5 hrs to AT 8/15/25)
|
||||
- **Recurring billing:**
|
||||
- Sched **509659** "GPS + AV / data backup / Hosting / Office subs" — Monthly, next 2026-06-24,
|
||||
**$543.50/mo** (this is the managed bundle; the "Hosting" line is the ACG-hosted Seafile data store)
|
||||
- Sched **499925** "OIT Phone bill" — Monthly, next 2026-07-01, **$45.44/mo** (OITVOIP/PacketDial)
|
||||
- **Email:** Microsoft 365, tenant **rrs-law.com** (~3 seats, Exchange Online)
|
||||
- Note: Russo schedule 224454 was deleted during Syncro API research 2026-05-26 and recreated as 509659.
|
||||
|
||||
## SharePoint storage cost evaluation (the deliverable for the call)
|
||||
|
||||
Client scenario: **3 M365 seats, ~6.5 TB of data, currently in Seafile**, wants to move to SharePoint.
|
||||
|
||||
M365 SharePoint pooled storage = 1 TB base + 10 GB/licensed user. At 3 seats that's only ~1.03 TB
|
||||
included, so almost all 6.5 TB is billable overage.
|
||||
|
||||
Microsoft-only options (per client request to keep the comparison MS-only):
|
||||
| Option | Rate | ~Monthly for 6.5 TB (3 seats) | Notes |
|
||||
|---|---|---|---|
|
||||
| **SharePoint Online "Extra File Storage"** | $0.20/GB/mo | **~$1,120/mo (~$13.4K/yr)** | live storage; ~5.47 TB billable after the ~1.03 TB pool. CSP-monthly +20% => ~$1,345/mo |
|
||||
| **Microsoft 365 Archive** | ~$0.05/GB/mo | ~$333/mo + retrieval fees | cold/inactive sites only; not for live working data |
|
||||
| **Self-hosted SharePoint Server (Subscription Edition)** | storage = disk (RBS) | licensing + infra + labor | storage escapes the $0.20/GB tax, but SharePoint Server + SQL + Windows licensing + SA + heavy ongoing ops is wildly disproportionate for 3 users (~$8-15K one-time licensing + hardware + setup + monthly maintenance). Not recommended. |
|
||||
|
||||
Context / alternatives (for ACG's internal read, not necessarily quoted MS-only):
|
||||
- They already have what they need in **Seafile** (ACG-hosted, part of the $543.50/mo bundle).
|
||||
- Backblaze B2 wholesale (~$0.006/GB) = ~$40/mo for 6.5 TB at ACG cost; a managed-archive resell
|
||||
at e.g. $0.03/GB (~$200/mo) is ~5-6x cheaper than Microsoft and still healthy margin.
|
||||
|
||||
**Recommendation / talking points for the call:**
|
||||
1. Ask **why SharePoint** — a specific feature (Office co-authoring, Teams), or just "we want SharePoint"? Decides everything.
|
||||
2. Moving all 6.5 TB live into SharePoint is a **~$1,100/mo new line item (~$13.4K/yr)** — roughly
|
||||
triples their current ACG monthly. Make sure they understand that before committing.
|
||||
3. Better path if they want the SharePoint UX: put the **working subset in SharePoint Online**
|
||||
(fits in/near the included pool) and **keep the 6.5 TB bulk in Seafile, linked** from SharePoint.
|
||||
4. If they just want files-with-web-access, they already have Seafile — no change needed.
|
||||
5. Self-hosting SharePoint Server is not worth it for 3 seats.
|
||||
|
||||
## Credentials & Secrets
|
||||
|
||||
- **M365 Global Admin (rrs-law.com tenant):** `guru@rrs-law.com` — found in PLAINTEXT in the Russo
|
||||
Syncro customer note. **Vaulted** at `clients/russo-law/m365-admin.sops.yaml` (username/password
|
||||
under `credentials:`; MFA/2FA held by Mike). **TODO: scrub the plaintext password from the Syncro
|
||||
customer note** now that it's vaulted.
|
||||
|
||||
## Pending / Incomplete Tasks
|
||||
|
||||
- Client has **not responded** to the meeting request (phone meeting being scheduled).
|
||||
- Scrub the M365 admin password from the Syncro customer 23331699 note (now vaulted).
|
||||
- On the call: confirm the "why SharePoint", present the cost, steer toward the hybrid (SP Online
|
||||
working set + Seafile bulk) unless a hard requirement forces a full move.
|
||||
|
||||
## Reference Information
|
||||
|
||||
- Syncro customer: https://computerguru.syncromsp.com/customers/23331699
|
||||
- Schedules: 509659 (managed $543.50/mo), 499925 (OIT phone $45.44/mo)
|
||||
- M365 tenant: rrs-law.com (~3 seats, Exchange Online)
|
||||
- Generic MS storage pricing used: SharePoint pool = 1 TB + 10 GB/user; Extra File Storage
|
||||
$0.20/GB/mo (CSP-monthly ~$0.24/GB); M365 Archive ~$0.05/GB + retrieval. Verify current MS pricing.
|
||||
@@ -0,0 +1,69 @@
|
||||
## User
|
||||
- **User:** Mike Swanson (mike)
|
||||
- **Machine:** GURU-5070
|
||||
- **Role:** admin
|
||||
|
||||
## Session Summary
|
||||
|
||||
Fixed Valley Wide Plastering's copier/scanner (Brother MFC-L3780CDW) scan-to-folder, which had
|
||||
stopped working after the 2026-06-13 G: file-share migration. Root cause: **VWP-FILES was
|
||||
multi-homed** (`192.168.0.20` on VLAN 2 + `172.16.9.132` on the new net), and a Windows host
|
||||
realistically supports one default gateway — only the `.132` NIC had one (`172.16.9.1`). So
|
||||
cross-VLAN traffic to `.20` arrived on the `.20` NIC but the reply egressed via the `.132`
|
||||
default-gateway path, and the UDM dropped the asymmetric return. `.20` was therefore unreachable
|
||||
from any other VLAN (proved: `.20` was silent from the VPN pool while `.25`/`.132` answered). The
|
||||
copier reaches `\192.168.0.20\<share>` across a VLAN boundary, so it broke.
|
||||
|
||||
The previously-documented "VWP-FILES Dual-NIC / asymmetric-routing / same-VLAN-only" note was a
|
||||
**flawed workaround, not a real constraint** — the UDM routes every VLAN natively (Mike's
|
||||
correction). Fix: **single-home VWP-FILES on `.20`** and drop `.132`.
|
||||
|
||||
## What was done
|
||||
|
||||
Diagnostics (read-only): from ADSRVR (a VLAN-2 server) confirmed every VLAN was reachable, SMB
|
||||
tcp/445 up on `.20`, the `scanner` AD account enabled/unlocked, and all 19 shares present
|
||||
(`\.20\SCANS` + per-person scan shares). Confirmed the **real VLAN-2 gateway is `192.168.0.1`**
|
||||
(ADSRVR's gateway) — the wiki said `172.16.9.1`, which was wrong.
|
||||
|
||||
Attempt 1 (in-guest via RMM): set `.20` gw `192.168.0.1` + DNS, disabled `.132`. Disabling the
|
||||
NIC dropped the agent's own connection -> command came back `interrupted`, script killed before
|
||||
confirming success, and the 7-minute **auto-rollback** task fired and re-enabled `.132` (box
|
||||
healthy, but back to dual-homed). Lesson: an in-guest NIC change self-interrupts the RMM agent.
|
||||
|
||||
Final fix (host-side, per Mike's Hyper-V suggestion): via the **VWP-HYPERV1** RMM agent,
|
||||
`Disconnect-VMNetworkAdapter` on VWP-FILES's `.132` vNIC (`Network Adapter`, MAC `00155D090501`),
|
||||
keeping the `.20` vNIC (`OldNet-VLAN2`, MAC `00155D090502`). Completed cleanly (the host's RMM
|
||||
connection is unaffected by the guest's NIC change). Then cleaned the guest's stale `.132`
|
||||
IP/route and `ipconfig /registerdns`.
|
||||
|
||||
Verified: `.20` now **reachable cross-subnet from the VPN** (was failing pre-fix); single default
|
||||
route `.20 -> 192.168.0.1`; `VWP-FILES` resolves to `.20` only; 19 shares intact.
|
||||
|
||||
## Key facts / corrections
|
||||
|
||||
- **VWP-FILES is now single-homed: `192.168.0.20`, gw `192.168.0.1`, DNS `172.16.9.2` + `192.168.0.25`.**
|
||||
- The `.132` vNIC is **DISCONNECTED at the Hyper-V host (reversible), not removed.**
|
||||
- **Old Net (VLAN 2, `192.168.0.0/24`) gateway is `192.168.0.1`** — the wiki's `172.16.9.1` was wrong.
|
||||
- Scanner = **Brother MFC-L3780CDW** (vault `clients/vwp/brother-mfc-l3780cdw`); scans to
|
||||
`\192.168.0.20\<share>` (shares map under `G:\SCANS\...`).
|
||||
- RMM agents: VWP-FILES `8e02fbbc`, VWP-HYPERV1 `bdc3e142`.
|
||||
|
||||
## Gotchas learned
|
||||
|
||||
- **In-guest RMM NIC changes self-interrupt** (the change drops the agent connection). For VM NIC
|
||||
changes, drive it **host-side** (`Disconnect-VMNetworkAdapter` / PowerShell Direct) so the host
|
||||
connection survives. Keep an auto-rollback timer either way.
|
||||
- `/rmm` hostname match `"hyperv"` hit **DF-HYPERV-B** (Dataforth) first — must resolve
|
||||
`VWP-HYPERV1` explicitly. The script's guard aborted safely on the wrong host (no changes).
|
||||
|
||||
## Pending / Incomplete
|
||||
|
||||
- Have someone **confirm an actual scan** from the Brother copier (only thing not driveable remotely).
|
||||
- Decide whether to **fully remove** the disconnected `.132` vNIC or leave it (currently left, reversible).
|
||||
- Harmless leftover on VWP-FILES: `C:\Windows\Temp\vwp-net-rollback.ps1` (from attempt 1).
|
||||
|
||||
## Reference
|
||||
|
||||
- VWP-FILES: single-homed `192.168.0.20`, gw `192.168.0.1`. RMM agent `8e02fbbc`.
|
||||
- VWP-HYPERV1: `172.16.9.184`, RMM agent `bdc3e142`. VWP-FILES vNICs: `Network Adapter`(.132,
|
||||
disconnected) / `OldNet-VLAN2`(.20).
|
||||
@@ -62,8 +62,16 @@ CryoWeave manufactures custom cryogenic cable assemblies (millikelvin to 300K) f
|
||||
### Email & Identity
|
||||
|
||||
- **Domain:** cryoweave.com
|
||||
- **Email:** Hosted externally [unverified — mail provider not documented]
|
||||
- **Greg's email:** greg@cryoweave.com
|
||||
- **Email:** **Microsoft 365 / Exchange Online** (confirmed 2026-06-15 — MX `cryoweave-com.mail.protection.outlook.com`, `autodiscover` → `autodiscover.outlook.com`). The IX/cPanel box only hosts the website, not mail.
|
||||
- **M365 tenant:** Cryoweave | Tenant ID `44705a37-b5d8-4bb1-882d-e18775612ada` | initial domain `cryoweave.onmicrosoft.com`
|
||||
- **Remediation suite:** onboarded 2026-06-15 (all ComputerGuru apps consented + roles; no MDE). ACG Global Admin `sysadmin@cryoweave.com` created (creds: SOPS `clients/cryoweave/m365-sysadmin.sops.yaml` + 1Password Clients). MFA not yet registered on it.
|
||||
- **Greg's email:** greg@cryoweave.com (Greg Schickling, owner/GA)
|
||||
- **DNS (zone on ns1/ns2.acghosting.com / IX) as of 2026-06-15:**
|
||||
- **SPF** OK: `v=spf1 +a +mx +ip4:72.194.62.5 +ip4:162.248.93.233 +ip4:162.248.93.81 +include:spf.protection.outlook.com -all` (authorizes M365, aligned).
|
||||
- **DMARC** `_dmarc` → `v=DMARC1; p=quarantine; sp=quarantine; fo=1; rua=mailto:rua@azcomputerguru.com` (hardened from p=none to **p=quarantine** 2026-06-15; **promote to p=reject** after ~1 week of clean aggregate reports confirm all legit senders — incl. the IX website/contact form — align). Cross-domain report authorization published on the azcomputerguru.com Cloudflare zone: `cryoweave.com._report._dmarc.azcomputerguru.com TXT "v=DMARC1;"` (2026-06-15). `rua@azcomputerguru.com` **shared mailbox created** in ACG's tenant (DisplayName "DMARC Reports", GUID 46b898f8-cfac-4b81-8980-e681b13fb833, mike@ FullAccess+automap) — full reporting chain live; aggregate reports arrive within ~24h. (NB: a single `*._report._dmarc` wildcard does NOT cover a 2-label reported domain; add one per-client record on the azcomputerguru.com Cloudflare zone.)
|
||||
- **DKIM** (M365 selector1/2): CNAMEs published + **signing ENABLED 2026-06-15** (`Get-DkimSigningConfig`: Enabled=True, Status=Valid, 2048-bit). Targets `selector1-cryoweave-com._domainkey.cryoweave.w-v1.dkim.mail.microsoft` (+ selector2).
|
||||
- Stale `mail.cryoweave.com` CNAME → old Neptune (67.206.163.124) **removed**.
|
||||
- **Outbound-email issue (open):** Greg reports mail not reaching recipients. SPF passes/aligns, so auth isn't hard-failing; pending **message trace** (EXO app-only access still propagating after onboarding) + Greg's NDR to pinpoint restriction/reject/junk. DKIM+DMARC gaps were the most likely junking cause.
|
||||
|
||||
### Network
|
||||
|
||||
|
||||
96
wiki/clients/russo-law.md
Normal file
96
wiki/clients/russo-law.md
Normal file
@@ -0,0 +1,96 @@
|
||||
---
|
||||
type: client
|
||||
name: russo-law
|
||||
display_name: Russo Law Firm
|
||||
last_compiled: 2026-06-15
|
||||
compiled_by: GURU-5070/claude-main
|
||||
sources:
|
||||
- clients/russo-law/session-logs/2026-06/2026-06-15-mike-russo-sharepoint-storage-eval.md
|
||||
---
|
||||
|
||||
# Russo Law Firm
|
||||
|
||||
> Tucson law practice; ACG managed-services client (GPS + AV + backup + Seafile hosting + Office
|
||||
> subs, $543.50/mo) + OITVOIP phone. First documented 2026-06-15 around a pre-sales question:
|
||||
> moving ~6.5 TB of data from ACG-hosted Seafile into Microsoft 365 SharePoint.
|
||||
|
||||
## Overview
|
||||
|
||||
- **Business:** Russo Law Firm (law practice), Tucson AZ.
|
||||
- **Address:** 3505 N Campbell Ave, Tucson, AZ 85719.
|
||||
- **Main phone:** 520-529-1515.
|
||||
- **Billing model:** managed services (recurring) + 12 prepaid hours on account.
|
||||
- **Syncro customer ID:** 23331699.
|
||||
|
||||
## Contacts
|
||||
|
||||
| Name | Phone | Email | Role |
|
||||
|---|---|---|---|
|
||||
| Steve Russo | 520-975-9024 | — | Principal (firm namesake) |
|
||||
| Carolyn Russo | 520-591-4303 | — | — |
|
||||
| Shannon Trionfo | 520-248-0244 | (account contact) | Account / billing contact |
|
||||
| Pat Broom | 520-850-6832 | pebroom@rrs-law.com | — |
|
||||
|
||||
Account email on file: stever@rrs-law.com. (Email domain: rrs-law.com.)
|
||||
|
||||
## Cloud / M365
|
||||
|
||||
- **Microsoft 365** tenant **rrs-law.com**, ~3 seats, Exchange Online (email).
|
||||
- **Global Admin:** `guru@rrs-law.com` — vaulted at `clients/russo-law/m365-admin.sops.yaml`
|
||||
(MFA/2FA held by Mike). [WARNING] The password was found in plaintext in the Syncro customer
|
||||
note; it is now vaulted and should be scrubbed from the Syncro note.
|
||||
|
||||
## Data / Storage
|
||||
|
||||
- **Primary data store:** ACG-hosted **Seafile** (~6.5 TB), billed under the "Hosting" line of the
|
||||
managed bundle. This is the system the client is considering moving off of.
|
||||
|
||||
## Billing (Syncro recurring schedules)
|
||||
|
||||
| Schedule | What | Frequency | Amount |
|
||||
|---|---|---|---|
|
||||
| 509659 | GPS + AV + data backup + **Hosting (Seafile)** + Office subs | Monthly | **$543.50** |
|
||||
| 499925 | OIT phone bill (OITVOIP / PacketDial) | Monthly | $45.44 |
|
||||
|
||||
Prepaid: 12.0 hrs on account. (History: 13.5 hrs moved to Syncro 1/16/26; 17.5 hrs to AT 8/15/25.)
|
||||
Note: schedule 224454 was deleted during Syncro API research 2026-05-26 and recreated as 509659.
|
||||
|
||||
## Active Question — SharePoint storage move (2026-06)
|
||||
|
||||
The client wants to move **~6.5 TB from Seafile into Microsoft 365 SharePoint**. A phone meeting is
|
||||
being scheduled (client had not responded as of 2026-06-15). Cost analysis, kept Microsoft-only at
|
||||
the client's request:
|
||||
|
||||
SharePoint pooled storage = 1 TB base + 10 GB/licensed user. At **3 seats** only ~1.03 TB is
|
||||
included, so nearly all 6.5 TB is billable overage.
|
||||
|
||||
| Option | Rate | ~Monthly for 6.5 TB | Notes |
|
||||
|---|---|---|---|
|
||||
| **SharePoint Online Extra File Storage** | $0.20/GB/mo | **~$1,120/mo (~$13.4K/yr)** | live storage; ~5.47 TB billable after the pool. CSP-monthly +20% -> ~$1,345/mo |
|
||||
| **Microsoft 365 Archive** | ~$0.05/GB/mo | ~$333/mo + retrieval | cold/inactive sites only; not for live working data |
|
||||
| **Self-hosted SharePoint Server** | storage -> disk via RBS | licensing + infra + labor | escapes the $0.20/GB tax but SharePoint Server + SQL + Windows + SA + heavy ops is disproportionate for 3 users (~$8-15K one-time + maintenance). Not recommended. |
|
||||
|
||||
For ACG's internal read (cheaper alternatives, not necessarily quoted MS-only): they already have
|
||||
Seafile; Backblaze B2 wholesale (~$0.006/GB) is ~$40/mo for 6.5 TB at ACG cost, and a managed-archive
|
||||
resell (~$0.03/GB ≈ $200/mo) would be ~5-6x cheaper than Microsoft with margin.
|
||||
|
||||
### Recommendation / call talking points
|
||||
1. **Ask why SharePoint** (specific feature like Office co-authoring / Teams, or just "we want it"?).
|
||||
2. A full live move is a **~$1,100/mo new line item (~$13.4K/yr)** — roughly triples their current
|
||||
ACG monthly. Set that expectation before they commit.
|
||||
3. Preferred path: **SharePoint Online for the working subset + keep the 6.5 TB bulk in Seafile,
|
||||
linked** — gets the SharePoint UX without the storage tax.
|
||||
4. If they only want files-with-web-access, Seafile already does it — no change needed.
|
||||
5. Don't self-host SharePoint Server for 3 seats.
|
||||
|
||||
## Open Items
|
||||
|
||||
- Client has not responded to the meeting request (phone meeting pending).
|
||||
- Scrub the M365 admin password from the Syncro customer note (now vaulted).
|
||||
- Deliver the SharePoint cost picture on the call; steer toward the hybrid unless a hard
|
||||
requirement forces a full move.
|
||||
|
||||
## Backlinks
|
||||
|
||||
- [[internal-infrastructure]] — ACG hosting infra (Seafile runs on Jupiter).
|
||||
- [[msp-pricing]] — GPS / hosting pricing basis.
|
||||
@@ -61,7 +61,7 @@ Plastering / stucco subcontractor based in Arizona. Active ACG client. Primary w
|
||||
| VWP-QBS | 172.16.9.169 | QuickBooks server + RDS/RemoteApp host | Windows Server 2022 Standard | **Physical Dell server** (NOT a VM). Has DRAC. Runs IIS (RD Web Access). WinRM on 5985. Reach from ADSRVR via `Invoke-Command -ComputerName VWP-QBS -Credential` with `vwp\sysadmin` PSCredential. |
|
||||
| Dell DRAC (VWP-QBS) | [undocumented] | Out-of-band management for VWP-QBS Dell | — | DRAC functional as of 2026-04-22. IP not yet documented. Vault: `clients/valleywide/quickbooks-server-idrac`. |
|
||||
| VWP-HYPERV1 | 172.16.9.184 | Hyper-V host — primary VM host for new infrastructure | Windows Server 2025 | Dell R740, 112 vCPU / 255 GB RAM, C: 10.7 TB. One external vSwitch on Intel 10G NIC. VHDs in `C:\VHD`. GuruRMM agent `bdc3e142-...`. Added 2026-06-13. |
|
||||
| VWP-FILES | 172.16.9.132 (primary) + 192.168.0.20 (VLAN 2) | G: file share server (19 SMB shares) | Windows Server 2019 Gen2 VM on VWP-HYPERV1 | Block-migrated from SERVER3 G: VDI (100 GB, ~88 GB used). Dual-homed: primary on 172.16.9.0/24; secondary vNIC tagged VLAN 2 holds 192.168.0.20 for IP-based stragglers (see Patterns). DNS registration disabled on the .20 NIC. GuruRMM enrolled (site Main Office, agent `8e02fbbc-...`). MSP360 backup running green. |
|
||||
| VWP-FILES | 192.168.0.20 (single-homed, VLAN 2; gw 192.168.0.1) | G: file share server (19 SMB shares) | Windows Server 2019 Gen2 VM on VWP-HYPERV1 | Block-migrated from SERVER3 G: VDI (100 GB, ~88 GB used). **Single-homed on 192.168.0.20 since 2026-06-15** — the former 172.16.9.132 vNIC was disconnected at the Hyper-V host to fix cross-VLAN scan-to-folder (the Brother copier hard-codes `\\192.168.0.20`; the multi-homed config had a gateway only on the .132 NIC, so replies to off-subnet clients were dropped — see Patterns). The .132 vNIC is DISCONNECTED at the host (reversible), not removed. DNS registers .20 only. GuruRMM enrolled (site Main Office, agent `8e02fbbc-...`). MSP360 backup running green. |
|
||||
| XenServer | 192.168.0.104 | VM hypervisor — hosts remaining VMs | XenServer 7.6 (PowerEdge R720) | SERVER3 VM (the old "server 2003", upgraded in-place to 2008) is now **powered off and retired**; snapshots retained for rollback. Vault: `clients/vwp/xenserver`. |
|
||||
| WINFileSvr | 192.168.0.35 | File server — serves **O:** (`Office_Archive`, ~570 GB / 138K files) + **P:** (`Estimating Archive` = F: root, ~545 GB / 142K files), both GPO-mapped to all staff; actively used daily | Windows Server 2019 | Old Net (VLAN 2). **VMware VM on the ESXi host (VMID 11, `WINFilrSrvr`)** — see ESXi inventory. ~1.1 TB live data. Holds `F:\Darv\Darv.rar` (51 GB Darv dev-machine backup) + `F:\Darv\Darv-rar` (extract, trimmed 135→26 GB on 2026-06-14). GuruRMM `62db0264-...`. Candidate to consolidate into VWP-FILES (retire the VM). Do not delete `Darv.rar` until VB6 source verified to compile. |
|
||||
|
||||
@@ -113,8 +113,8 @@ and let the VM be retired.
|
||||
- **Firewall / Router:** UniFi Dream Machine at 172.16.9.1
|
||||
- **VPN:** OpenVPN on UDM. Client pool: `192.168.4.0/24`. Pushes routes for `172.16.9.0/24`, `192.168.0.0/24`, `192.168.3.0/24`. DNS pushed as `192.168.4.1` (UDM).
|
||||
- **Subnets:**
|
||||
- `172.16.9.0/24` — primary internal network (new servers, VWP-QBS, UDM, iLO, HYPERV1, VWP-FILES primary NIC); untagged
|
||||
- `192.168.0.0/24` — **"Old Net" = VLAN 2 on UDM** (gw 172.16.9.1, DHCP .100-.199, DNS → 192.168.0.25 + 8.8.8.8). Hosts: VWP_ADSRVR (.25), WINFileSvr (.35), XenServer (.104), Yealink phones (.17/.54/.130/.140/.222), VWP-FILES secondary NIC (.20). **[WARNING: conflicts with IMC's LAN — verify client context when switching VPNs.]**
|
||||
- `172.16.9.0/24` — primary internal network (new servers, VWP-QBS, UDM, iLO, HYPERV1); untagged
|
||||
- `192.168.0.0/24` — **"Old Net" = VLAN 2 on UDM** (gw 192.168.0.1, DHCP .100-.199, DNS → 192.168.0.25 + 8.8.8.8). Hosts: VWP_ADSRVR (.25), WINFileSvr (.35), XenServer (.104), Yealink phones (.17/.54/.130/.140/.222), VWP-FILES (.20, single-homed 2026-06-15). **[WARNING: conflicts with IMC's LAN — verify client context when switching VPNs.]**
|
||||
- `192.168.3.0/24` — Management VLAN 99
|
||||
- `192.168.4.0/24` — OpenVPN client pool
|
||||
- **Static DNS (UDM):** `vwp-qbs.vwp.us` → `172.16.9.169` (typo `qwp-qbs` fixed 2026-04-16)
|
||||
@@ -190,9 +190,23 @@ Same double-hop constraint applies to GPMC (`Get-GPO`/`Set-GPO`) — fails `0x80
|
||||
|
||||
VWP's Old Net (VLAN 2, `192.168.0.0/24`) is the same RFC1918 range as IMC (another ACG client). When switching between client VPN contexts, verify which 192.168.0.x addresses are targeted. This is a silent risk.
|
||||
|
||||
### VWP-FILES Dual-NIC / Asymmetric Routing
|
||||
### VWP-FILES single-homed on 192.168.0.20 (resolved 2026-06-15)
|
||||
|
||||
VWP-FILES is dual-homed: 172.16.9.132 (primary, new net) + 192.168.0.20 (VLAN 2, Old Net — for IP-based stragglers whose UNC paths hard-code `.20`). DNS registration is **disabled** on the .20 NIC so that name resolution always returns .132. Asymmetric routing applies: cross-subnet or VPN clients cannot reach .20 (VWP-FILES replies via its .132 NIC); only same-VLAN Old Net devices can use .20 directly. Use 172.16.9.132 for all management and file pulls from outside Old Net.
|
||||
VWP-FILES is **single-homed on 192.168.0.20** (VLAN 2 / Old Net, gw 192.168.0.1). The Brother
|
||||
MFC-L3780CDW copier and other stragglers hard-code `\\192.168.0.20` for scan-to-folder, so the
|
||||
server must own that address with a working gateway.
|
||||
|
||||
History / why this note exists: the server was briefly **dual-homed** (172.16.9.132 primary +
|
||||
192.168.0.20 secondary). Only the .132 NIC had a default gateway, so the server could not reply
|
||||
to off-subnet clients arriving on .20 — replies tried to egress via the .132 default route and
|
||||
were dropped (multi-homed asymmetric routing). That silently broke scan-to-folder for the copier
|
||||
after the 2026-06-13 cutover. **The UDM routes between all VLANs natively** — any host on any VLAN
|
||||
can reach any other — so the earlier "only same-VLAN devices can reach .20" theory was wrong; the
|
||||
real defect was the single-default-gateway asymmetry on a multi-homed host. Fix: drop to one NIC on
|
||||
.20 with gw 192.168.0.1. Done host-side via `Disconnect-VMNetworkAdapter` on VWP-HYPERV1 (an
|
||||
in-guest NIC change dropped the RMM agent and auto-rolled-back). The .132 vNIC is left
|
||||
**disconnected** at the Hyper-V host (reversible — reconnect it in Hyper-V if .132 is ever needed),
|
||||
not removed. Full procedure: 2026-06-15 session log.
|
||||
|
||||
### Syncro Billing for Prepaid Block Emergency
|
||||
|
||||
@@ -265,6 +279,7 @@ Power outage caused HP ProLiant NVRAM corruption (BIOS/iLO factory reset). VWP-Q
|
||||
| 2026-06-13 | SERVER3 (XenServer "server 2003" VM, upgraded to 2008 in-place) retired. G: file share (100 GB) block-migrated via VDI export→VHDX to new **VWP-FILES** (Gen2 Server 2019 on **VWP-HYPERV1** 172.16.9.184). 19 SMB shares recreated; **MappedDrives GPO** repointed to `\\VWP-FILES\G-drive`. IP takeover: VWP-FILES holds 192.168.0.20 (VLAN 2) for IP-based stragglers. SERVER3 snapshotted and powered off. VWP-FILES enrolled in GuruRMM (site Main Office) + MSP360 backup green. Billed 3.5 h on #32418 (prepay 24.0→20.5). |
|
||||
| 2026-06-13 | VB6 Orders source **fully recovered** from `F:\Darv\Darv.rar` on WINFileSvr (192.168.0.35). 12.2 MB staged to repo (`source-code/Orders-VWP_Current-2020/`). VB Decompiler Pro no longer needed. See [[projects/valleywide-orders-modernization]]. |
|
||||
| 2026-06-13 | **Syncro** and **Datto RMM Agent** deployment GPOs disabled (`AllSettingsDisabled`, flags=3) via LDAP on VWP_ADSRVR. Existing agents not yet uninstalled — awaiting direction. |
|
||||
| 2026-06-15 | **VWP-FILES scan-to-folder fix.** Copier scan-to-`\\192.168.0.20` broke after the 2026-06-13 cutover — root cause was the dual-homed server having a default gateway only on the 172.16.9.132 NIC, so replies on the .20 NIC to off-subnet clients were dropped (not a VLAN-routing limit; the UDM routes all VLANs). Fix: single-homed VWP-FILES on 192.168.0.20 (gw 192.168.0.1) by disconnecting the .132 vNIC host-side via `Disconnect-VMNetworkAdapter` on VWP-HYPERV1 (in-guest change dropped the RMM agent + auto-rolled-back). .132 vNIC left disconnected (reversible), not removed. Scanner = Brother MFC-L3780CDW (vault `clients/vwp/brother-mfc-l3780cdw`). |
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -57,6 +57,7 @@ Run `/wiki-lint` to check for stale entries and broken backlinks.
|
||||
| [Gonzvar Tax Services](clients/gonzvar-tax-services.md) | Tax services firm; Syncro 1830740 ("Gonzvar Tax Service", break-fix, ~$175/hr); 6 machines in GuruRMM (GTS.local AD, 2 servers + 4 workstations); open security findings from 2026-06-06 onboarding baseline; QuickBooks RemoteApp + Tailscale VPN pending | 2026-06-12 |
|
||||
| [Tohono O'odham Nation DoIT](clients/tohono-oodham-doit.md) | Tribal government IT dept; Syncro 33069069; Starlink reseller client — 2x Check Point 1550 field sites on Starlink Roam (CGNAT); break-fix $175/hr; VPN design (IPsec vs Tailscale) pending | 2026-05-27 |
|
||||
| [Tucson Golden Corral](clients/tucson-golden-corral.md) | Restaurant (Tucson AZ); Syncro 3859123; prepaid block 12.75 hrs; email on Neptune Exchange; WS2016 single-box DC/RDS/Hyper-V/SQL + Sage 100 ERP (TGC-SERVER colocated at ACG main office); architecture concerns outstanding | 2026-05-26 |
|
||||
| [Russo Law Firm](clients/russo-law.md) | Tucson law practice; Syncro 23331699; managed $543.50/mo (GPS+AV+backup+Seafile hosting+Office) + OIT phone $45.44/mo; 12 prepaid hrs; M365 rrs-law.com (~3 seats, admin guru@ vaulted); **active pre-sales 2026-06: wants to move ~6.5 TB from Seafile to SharePoint — full live move ~$1,120/mo (~$13.4K/yr), recommend hybrid (SP Online working set + Seafile bulk); phone meeting pending, client not yet responded** | 2026-06-15 |
|
||||
|
||||
## Projects
|
||||
|
||||
|
||||
Reference in New Issue
Block a user