WRKLOG-2026-06-10

Summary

Wednesday. Highest-output day of Q2. Ghost-Sender P0 investigation with InfoGuard research — 3 mitigations documented, Victor deployed transport rule via console, Sarah’s SaaS risk question answered (ESA trust boundary), dual IP maintenance concern documented. CHLXSYSLOG01 rsyslog → Monad pipeline went live — configured TLS forwarding (3 config iterations, 2 errors resolved), installed gawk/jq/neovim on production Debian, ran full analysis command suite in front of the team. TCP clocks connectivity review. MS Log Forwarder working session. Abnormal weekly. CISO quarterly development review. Created d001 root assemblers for SIEM + Abnormal projects. 500+ line rsyslog operational command reference with ISE/ASA/FMC/switch per-source analysis. Ghost-Sender validation commands with email hygiene audit (SCL/BCL/SFV). Quarterly summary covering 8 domains across Q2.

URGENT - All Domains

Carryover Backlog (CRITICAL)

Task Details Origin Days Status

MSCHAPv2 Migration Report

Report due. 6-sheet Standard Report (exec summary, trend, waves, device detail, stale, policy match). Sheet 6 added 05-14: policy match by protocol for removal planning + anonymous identity validation. Migration window 2026-05-04 to 2026-05-30. ~6,227 devices, 5 waves.

2026-04-17

57

P0 - DUE — run report this week

Abnormal Security — ✅ COMPLETE

CR-2026-05-07-abnormal-read-write. CAB approved 2026-05-12. Implemented successfully 2026-05-13. Read/write enabled for pilot group. Post-deployment validation pending.

2026-05-07

37

✅ IMPLEMENTED — post-validation pending

SIEM QRadar → Sentinel Migration

Lead role. Monad console error RESOLVED 2026-05-12 — secrets configured in CHLA production tenant. ISE secure syslog integration in progress — cert imported, remote logging target configured, streaming errors under investigation. Blocking: DCR not created (Rule ID + Stream Name). Azure private network policy unresolved. Victor + Mauricio action.

2026-04-10

64

P0 - ACTIVE — ISE syslog + DCR blocking

Monad Pipeline Evaluation

Sentinel output connector. Console error resolved. 3 of 6 values configured. Remaining: Endpoint URL (have it), Rule ID + Stream Name (need DCR). ISE Remote Logging Target configured 2026-05-18 — TLS cert imported, secure syslog target created. Streaming errors in Monad console under investigation.

2026-03-11

94

P0 - ACTIVE — ISE integration in progress

Guest Redirect ACL

Guest redirect ACL work needed. Related to Mandiant remediation findings.

2026-05-12

32

P0 - TODO

ISE Patch 10 (CVE-2026-20147 CVSS 9.9)

ISE 3.2 Patch 10. Supersedes Patch 9. 61 days on a CVSS 9.9 — schedule maintenance window. Write CR if needed.

2026-03-12

93

P0 - OVERDUE — schedule immediately

k3s NAT verification

NAT rule 170 for 10.42.0.0/16 pod network - test internet connectivity. 64 days — test this week or defer to Q3.

2026-03-09

96

P0 - BLOCKING — TRIAGE: schedule or defer

Wazuh indexer recovery

Restart pod after NAT confirmed working - SIEM visibility blocked. Blocked by k3s NAT — cannot proceed until above resolved.

2026-03-09

96

P0 - Blocked by k3s

Strongline Gateway VLAN fix

8 devices in wrong identity group (David Rukiza assigned)

2026-03-16

89

P0 - TODO

TCP Clocks deployment

ISE identity group validation, query outputs, comms with team. Active d001 data Apr 22-23.

2026-04-22

52

P0 - ACTIVE

IoT Dr. Kim — recurring

Sleep study devices (Apr 15-16), watches recurrence (Apr 22). 5 incident versions in d001. Validate iPSK enrollment.

2026-04-15

59

P0 - RECURRING

Murus Portae (WAF) — Phase 0

FMC cert expired, ACP returns zero rules. d001: zone map, architecture D2, FMC API reference, ops script.

2026-04-16

58

P0 - INVESTIGATING

Vocera EAP-TLS Supplicant Fix

~10 phones failing 802.1X, missing supplicant config. 61 days — schedule with clinical engineering team.

2026-03-12

93

P1 - TODO — schedule

ISE MnT Messaging Service

Enable "Use ISE Messaging Service for UDP syslogs delivery". 61 days — low risk, schedule with ISE Patch 10 maintenance window.

2026-03-12

93

P2 - BUNDLE with Patch 10

Professional backlog remains critical. Check Days column for priorities.

BLOCKERS — Fix Immediately

Task Details Origin Days Impact

Z Fold 7 Termux

gopass and SSH not working

2026-03-10

58

BLOCKER — Cannot access passwords on mobile

gopass v3 organization

Inconsistent structure, poor key-value usage

2026-03-20

48

Inefficient password management, no aggregation

Git history scrub — sensitive personal terms

Plaintext references to personal legal matters in committed worklogs (WRKLOG-2026-03-14, WRKLOG-2026-04-18). Forward-fixed but old commits still contain strings. Requires git filter-repo + force-push. See runbook below.

2026-04-22

15

SECURITY — sensitive terms in public git history

Runbook: Git History Scrub (d000 Personal Terms)

Problem: Two committed worklogs contained plaintext references to personal legal matters. The files have been edited (forward-fix), but git history retains the original text in prior commits.

Affected commits: Any commit touching these files:

# Identify affected commits
git log --oneline -- \
  docs/modules/ROOT/pages/2026/03/WRKLOG-2026-03-14.adoc \
  docs/modules/ROOT/pages/2026/04/WRKLOG-2026-04-18.adoc

Scrub procedure:

# 1. BEFORE: Full backup of the repo
cp -a ~/atelier/_bibliotheca/domus-captures ~/atelier/_bibliotheca/domus-captures.bak

# 2. Install git-filter-repo (if not present)
# Arch: pacman -S git-filter-repo
# pip: pip install git-filter-repo

# 3. Create expressions file for replacement
cat > /tmp/scrub-expressions.txt << 'EXPR'
regex:(?i)divorce==[REDACTED]
regex:(?i)dissolutio(?!n\.adoc\.age)==[REDACTED-LEGAL]
regex:(?i)iliana==[REDACTED-NAME]
regex:(?i)angulo-arreola==[REDACTED-NAME]
regex:legal-divorce-notes\.age==legal-notes.age
regex:1099-NEC-iliana==1099-NEC
EXPR

# 4. Verify before (dry run — count matches in history)
git log -p --all -S 'divorce' -- '*.adoc' | grep -c 'divorce' || echo "0 matches"
git log -p --all -S 'iliana' -- '*.adoc' | grep -c 'iliana' || echo "0 matches"

# 5. Run filter-repo (DESTRUCTIVE — rewrites all commit hashes)
git filter-repo --replace-text /tmp/scrub-expressions.txt --force

# 6. Verify after
git log -p --all -S 'divorce' -- '*.adoc' | grep -c 'divorce' || echo "0 matches — CLEAN"
git log -p --all -S 'iliana' -- '*.adoc' | grep -c 'iliana' || echo "0 matches — CLEAN"

# 7. Re-add remotes (filter-repo removes them)
git remote add origin git@github.com:<user>/domus-captures.git
# Add any other remotes (Gitea, etc.)

# 8. Force-push to all remotes (DESTRUCTIVE — overwrites remote history)
git remote | xargs -I{} git push {} main --force

# 9. Clean up
rm /tmp/scrub-expressions.txt
rm -rf ~/atelier/_bibliotheca/domus-captures.bak  # only after verifying

Post-scrub checklist:

  • Backup created before running

  • git filter-repo installed

  • Expressions file reviewed — no false positives (e.g., Don Quijote "Angulo el Malo" is in segunda-parte/texto/texto-011.adoc — the regex targets angulo-arreola specifically to avoid this)

  • Dry-run counts match expectations

  • Filter-repo executed

  • Post-scrub verification shows 0 matches

  • Remotes re-added

  • Force-pushed to all remotes

  • Cloudflare Pages rebuild verified

  • Local clones on other machines re-cloned or git fetch --all && git reset --hard origin/main

  • Backup removed

URGENT - Requires Immediate Action

Item Details Deadline Status Impact

Housing Search

Granada Hills area - apartments/rooms

TBD

In Progress

Quality of life, commute

2025 Tax — IRS Transcript Review

MFJ filed 2026-04-22. Pull IRS Return Transcript to verify contents. Consult attorney re: Form 8857 (Innocent Spouse Relief). Details in encrypted case file.

Before attorney meeting

In Progress

Financial — liability exposure. See encrypted D000 case file.

Rack Relocation

Physical move of server rack. CR written: CR-2026-04-18 (pending in infra-ops). Borg backup completed. VM XML dumps, switch save, shutdown/startup procedure documented.

TBD

Pending

Infrastructure downtime — all services offline during move

D000 Legal Planning

Encrypted D000 case file. Open: dissolutio-open. Close: dissolutio-close. 19 partials + assembler. PDF build for attorney handoff. Critical deadline: Jan 2029.

Before Jan 2029

Active — escalating

Life transition — see case file for details

Credit Report Review

Pull reports from all 3 bureaus via annualcreditreport.com. Verify no unknown joint accounts or debts. Credentials in gopass: v3/personal/finance/credit/annual_credit_report

TBD

In Progress

Financial discovery — FL-142 preparation

Gopass Security Audit

Rotate passwords on shared/known accounts. Add 2FA backup codes to v3/personal/recovery/. Create missing government entries (IRS, SSA, VA, DMV). Add last_login field to active entries.

TBD

Pending

Digital security — pre-filing preparation

Subscription Audit

Download 3 months bank/CC statements (Chase, NFCU, USAA). Identify all recurring charges. Cancel unnecessary. Document active subscriptions for FL-150.

TBD

Pending

Financial — expense documentation

401(k) Enrollment

Enroll in CHLA 401(k) immediately. Post-separation contributions are 100% separate property. Reduces gross income for support calculations. Max 2026: $23,500/yr.

In progress (started 5/4)

In Progress

Financial — support calculation + retirement

URGENT — Performance Review Certifications

Certification Provider Deadline Status Impact

CISSP

ISC² — Certified Information Systems Security Professional

July 12, 2026

ACTIVE — Week 2 of 10 (Project)

Required for performance review. 10-week accelerated plan.

RHCSA 9

Red Hat Certified System Administrator

Q3 2026

ACTIVE — 21-phase curriculum (Project)

After CISSP. Required for performance review.

CISSP: 41 days remaining (exam July 12). Domain 1 study in progress. Schedule exam today (06-01).

Early Morning - 5:30am

Regex Training (CRITICAL CARRYOVER)

  • Session 3 - Character classes, word boundaries

  • Practice drills from regex-mastery curriculum

  • Status: 52 days carried over (since 2026-03-16) — CRITICAL

Regex training continues to slip. This is the foundation for all CLI mastery.

Daily Notes

Triage Status

Item Status Destination

URGENT: Ghost Sender — Mauricio validated POC exploit

🟡 meeting prep

daily-notes/2026-06-10/ghost-sender-investigation.adoc

⚡ CHARGE TIME IN PEOPLESOFT (carryover day 3)

❌ not started

PeopleSoft → submit hours before EOD

P1: Intune hybrid join cert auth failure (carryover day 3)

🟡 investigation file created, commands ready

decrypt-file data/d001/investigations/2026-06-08-intune-cert-auth/intune-cert-commands-2026-06-08.adoc.age

P1: Spam bypass — SCL 9 delivered to inbox (carryover day 3)

🟡 investigation file created, commands ready

spam-bypass-investigation.adoc — run Phase 1 on work machine

VNC removal — uninstall/disable across affected systems (carryover day 2)

🟡 runbook created, ready to execute

daily-notes/2026-06-09/vnc-removal.adoc

TCP Clocks batch 2 — switch NOT configured (carryover day 2)

⚠️ ISE done, switch blocking

New switch needs RADIUS/AAA config + ISE NAD entry

Install BYOD P12 cert on ZFold7 (carryover day 3)

🟡 cert issued, needs P12 bundle transfer

Phase 8: BYOD Certs

Fix dsec embedded quotes in vault unseal keys (carryover day 3)

❌ not started

dsec edit d000 dev/vault — remove wrapping double quotes

Rejoin AD from ISE GUI (carryover day 3)

❌ not started

Phase 6 — DOMUS_AD join point

Fix NAS SSH config (adminerosado → evanusmodestus) (carryover day 3)

❌ not started

~/.ssh/config line 245

Fix WLC SSH config (add PubkeyAuthentication no) (carryover day 3)

❌ not started

~/.ssh/config — add Host 9800-wlc-01 block

Add NTP rule to vyos-02 (carryover day 3)

❌ not started

set firewall ipv4 name MGMT_LOCAL rule 45

Add kvm-01 NAS mounts to /etc/fstab (carryover day 3)

❌ not started

10.50.1.70:/volume1/isos → /mnt/nas/isos

Generalize build-antora-page.sh for HTML output (carryover day 3)

❌ not started

scripts/build-antora-page.sh

Printer unreachable — iPSK auth + VLAN filtering (carryover day 3)

❌ blocked

Depends on ISE rotation completing

P0/P1/P2 tracker refresh — 48 days stale (carryover day 3)

❌ not started

trackers/work/priorities/

Meetings & Working Sessions

Meetings & Working Sessions — 2026-06-10

Pre-Meeting Blockers

Before the MS Log Forwarder working session, ISE must be sending logs to rsyslog and rsyslog must be forwarding to Monad. Complete these first:

1. Configure ISE → rsyslog (CHLXSYSLOG01)
ISE GUI: Administration → System → Logging → Remote Logging Targets → Add
  Name:       CHLXSYSLOG01
  IP Address: 10.248.13.254
  Port:       514
  Facility:   LOCAL6
  Max Length:  8192

Then: Logging Categories → assign CHLXSYSLOG01 to:
  - Passed Authentications
  - Failed Attempts
  - RADIUS Diagnostics
  - Authentication Flow Diagnostics

Full runbook: daily-notes/2026-06-09/rsyslog-new-sources.adoc

2. Verify rsyslog is receiving on CHLXSYSLOG01
# SSH to CHLXSYSLOG01

# Check rsyslog is running
systemctl status rsyslog

# Verify listening on 514
ss -tulnp | grep 514

# Tail ISE logs in real time (after config)
tail -f /var/log/remote/ise/*.log

# Check for ANY incoming traffic on syslog port
tcpdump -i any port 514 -c 20 -nn

# Count messages received in last 5 minutes
journalctl -u rsyslog --since "5 minutes ago" --no-pager | wc -l

# Check disk usage on syslog partition
df -h /var/log/remote/
3. Verify rsyslog → Monad forwarding
# Check forwarding config exists
cat /etc/rsyslog.d/90-monad-forward.conf

# Check rsyslog queue status (disk-assisted queue health)
rsyslogd -N1 2>&1 | head -20

# If queue files exist — messages are buffering (Monad unreachable)
ls -la /var/spool/rsyslog/monad-fwd*

# Test end-to-end: generate a test message
logger -p local6.info -t ISE-TEST "Test message from CHLXSYSLOG01 $(date +%Y%m%d-%H%M%S)"

Full runbook: daily-notes/2026-06-09/monad-rsyslog-pipeline.adoc

Meeting Schedule
Time Meeting Prep / Notes

AM

TCP Time Clock Connectivity Review

Batch 2: 9 MACs added to ISE (OUI 40:AC:BD), switch NOT configured (no RADIUS/AAA). Bring switch onboarding template from daily-notes/2026-06-09/tcp-clocks-batch2.adoc. Key issue: new switch deployed without AAA config — clocks in ISE but can’t authenticate. Action item: Validate all clocks are in IoT_Onboard group with descriptions — run ERS validation query (see below).

AM/PM

MS Log Forwarder Working Session

BLOCKER: ISE must be pointing to rsyslog BEFORE this meeting. Verify syslog landing with tail -f + tcpdump. Then demonstrate rsyslog → Monad forwarding. Bring: d001 open siem-qradarpartials/ise-lab-rsyslog-monad-2026-06-09.adoc, partials/rsyslog-central-collector.adoc. Victor + Mauricio involved — DCR not yet created (Rule ID + Stream Name needed).

PM

Abnormal Security Weekly Working Session

Ghost Sender is the lead topic. Mauricio validated POC. Bring: ghost-sender-investigation.adoc + D2 diagram. Also: spam bypass SCL 9 investigation (06-08), policy review validation commands (20 sections). d001: d001 open abnormal for playbook + queries.

PM

1-1 InfoSec Meeting with CISO (Quarterly Development Review)

Bring quarterly summary (Q2 2026 accomplishments). Key themes: Abnormal deployment, SIEM migration, MSCHAPv2 report, ISE API tooling, email security posture, incident response. See quarterly-development-q2-2026.adoc.

TCP Clocks — Pre-Meeting Validation

Goal: Confirm all clocks are in IoT_Onboard with descriptions before the review meeting.

d001 file: data/d001/projects/tcp-clocks/partials/queries-ers.adoc

Project status:

  • Batch 1 — OUI 40:AC:8D:00:93 — added to ISE, validated

  • Batch 2 — OUI 40:AC:BD:00:95 + 40:AC:BD:00:96 — 9 MACs added 2026-06-09

  • Blocker: New switch deployed without RADIUS/AAA config — clocks in ISE but can’t authenticate until switch is onboarded

Run this to validate all clocks (batch 1 + 2)
d001 open tcp-clocks
dsource d001 dev/network/ise

# Validate group + description for ALL clock OUI prefixes
for prefix in "40:AC:8D:00:93" "40:AC:BD:00:95" "40:AC:BD:00:96"; do
  ers "/endpoint?filter=mac.CONTAINS.${prefix}" | \
    jq -r '.SearchResult.resources[].id' | while read -r eid; do
      ep=$(ers "/endpoint/${eid}")
      gid=$(jq -r '.ERSEndPoint.groupId' <<< "$ep")
      group_name=$(ers "/endpointgroup/${gid}" | jq -r '.EndPointGroup.name')
      jq --arg group "$group_name" \
        '.ERSEndPoint | {mac, description, group: $group, staticGroupAssignment}' <<< "$ep"
    done
done

What to look for:

  • Every clock should show "group": "IoT_Onboard"

  • Every clock should have a description (not null or empty)

  • Every clock should show "staticGroupAssignment": true

If a clock is wrong — fix commands:

  • Missing description → PUT update (queries-ers.adoc line 137)

  • Wrong group → PUT reassign (queries-ers.adoc line 158)

Quick-Reference: d001 Decrypts Needed Today
# SIEM / rsyslog / Monad
d001 open siem-qradar

# Abnormal Security (Ghost Sender, playbook, queries)
d001 open abnormal

# TCP Clocks
d001 open tcp-clocks

# ASA VPN Migration (if it comes up)
d001 open asa-vpn-okta

Ghost Sender Investigation

Investigation: Ghost-Sender — Direct Send Exploitation via External MX

Executive Summary

An external attacker can forge the From: address of any internal or external user and deliver email directly to Exchange Online mailboxes. For internal senders, Outlook even resolves the sender’s profile picture. Mauricio Naranjo validated this exploit via proof-of-concept on 2026-06-10. InfoGuard (Swiss cybersecurity firm) published the research on 2026-06-09 and named it "Ghost-Sender."

No CVE assigned. Microsoft MSRC closed the report (2026-04-21) and general support called it a "known architectural limitation" (2026-05-29). Microsoft deployed and then rolled back a partial mitigation for internal spoofing (2026-04-22 to 2026-04-27). Evidence indicates active exploitation in the wild per Microsoft support.

Who is affected: Organizations using Exchange Online (or on-prem hybrid) with a third-party mail server or spam filter as their MX record — i.e., external MX. This is CHLA’s configuration (ESA / Abnormal as MX gateway). Over 20% of scanned bug bounty domains were vulnerable. Fewer than half of orgs with external MX have a mitigation applied.

What bypasses it: SPF, DKIM, DMARC policies of the spoofed sender domain are all irrelevant — the attack works regardless. Microsoft’s configuration analyzer, Enhanced Filtering, and Strict/Standard preset security policies do NOT prevent it.

Terminology
Term Meaning

Ghost-Sender

InfoGuard’s name for the attack research (published 2026-06-09). Use this to reference the specific exploit and the meeting topic.

Direct Send

The Exchange Online feature being exploited — unauthenticated SMTP relay on port 25. This is the root cause. Microsoft considers it a "known architectural limitation," not a vulnerability.

X-MS-Exchange-Organization-AuthAs

The detection signal. Header that EOP stamps on every message — Internal, Internet, or Anonymous. Cannot be forged by external senders. The transport rule keys on this.

External MX configuration

The vulnerable architecture — a third-party mail server (ESA, Proofpoint, Mimecast, Abnormal) sits as the MX record instead of Exchange Online Protection directly. CHLA’s current state.

What You Can Deliver Now (While Still on ESA)

ESA is still inline in CHLA’s mail path. That’s not a weakness here — it’s a control point you already have. Three categories of immediate value:

1. PowerShell Recon (run before the meeting)

These commands give you the current state to present. No changes, read-only.

Connect-ExchangeOnline

# Do we have ANY partner organization connectors?
Get-InboundConnector | Format-List Name, Enabled, ConnectorType, SenderIPAddresses, SenderDomains, RequireTls, RestrictDomainsToCertificate

# What transport rules exist? Any touching AuthAs already?
Get-TransportRule | Format-List Name, State, Priority, Description
Get-TransportRule | Where-Object { $_.HeaderContainsMessageHeader -like "*AuthAs*" -or $_.HeaderMatchesMessageHeader -like "*AuthAs*" } | Format-List Name

# Is Direct Send restricted?
Get-TransportConfig | Format-List ExternalDsnDefaultLanguage, HeaderPromotionModeSetting

# What IPs does the connection filter allow?
Get-HostedConnectionFilterPolicy | Format-List IPAllowList

# 30-day sender IP map — who sends as @chla.usc.edu from outside?
# NOTE: Get-MessageTrace -SenderAddress does NOT support * wildcard in all tenants.
# If "*@chla.usc.edu" errors with "Invalid SenderAddress value", use the pipeline filter:
Get-MessageTrace -StartDate (Get-Date).AddDays(-10) -EndDate (Get-Date) |
    Where-Object { $_.SenderAddress -like "*@chla.usc.edu" } |
    Group-Object FromIP |
    Sort-Object Count -Descending |
    Select-Object -First 20 Count, Name |
    Format-Table -AutoSize

This 30-day IP map is the critical deliverable — it’s the exception list that blocks transport rule enforcement. Nobody can deploy the rule without it. If you walk into the meeting with this data, you’re the one who unblocked the mitigation.

2. ESA-Side Mitigation (your domain expertise)

ESA is still inline. It can enforce a content filter that catches spoofed internal From: headers on inbound mail before they ever reach Exchange Online. This is a control the team has right now, independent of Exchange transport rules.

ESA content filter concept
Condition: Incoming mail
  AND envelope sender domain ≠ chla.usc.edu
  AND header From: contains @chla.usc.edu
Action: Quarantine with notification to security team

In ESA terms:
  Mail Policies → Incoming Content Filters → Add Filter
  Condition: Header "From" contains "chla.usc.edu"
  + Condition: Envelope Sender does NOT match "chla.usc.edu"
  Action: Quarantine (Policy quarantine)

This catches the same attack at the ESA layer. Defense in depth — even if the Exchange-side transport rule isn’t deployed yet, ESA blocks it. And when ESA is eventually decommissioned, the Exchange transport rule takes over.

Present this as: "We can mitigate at the ESA layer today, and at the Exchange layer when we’re ready. Both controls catch the same thing using different mechanisms."

3. Validate with InfoGuard Testing Tool
Run against CHLA domain (read-only fingerprint check)
https://ghost-sender.com/

Steps:
  1. Enter CHLA's domain
  2. Tool checks: MX record, partner connector presence, Direct Send status
  3. Reports whether domain is vulnerable
  4. Run BEFORE mitigation (baseline) and AFTER (validation)

Walk into the meeting with the scan result. If it says "vulnerable" — that’s the evidence. If it says "partially mitigated" — you know what’s missing.

How Ghost-Sender Works
Step Description

1. Attacker crafts email

External SMTP server sends a message with From: ceo@chla.usc.edu (or any internal/external address). All an attacker needs is a one-line PowerShell command. The envelope sender (MAIL FROM) can be anything.

2. External MX gateway

Message arrives at the third-party mail gateway (ESA / Abnormal) pointed to by the MX record. The gateway forwards it to Exchange Online.

3. Exchange Online receives

Because an external MX is used, Exchange Online accepts any incoming email by default with no further validation. EOP evaluates SPF/DKIM/DMARC against the envelope sender domain — irrelevant to the spoofed From: header.

4. AuthAs header stamped

Exchange Online stamps X-MS-Exchange-Organization-AuthAs with a value other than Internal (either Internet or Anonymous). This header cannot be spoofed — EOP strips and re-stamps it on all inbound messages.

5. Delivery + profile picture

Without a transport rule or partner connector checking AuthAs, the message delivers to the recipient’s inbox. It displays From: ceo@chla.usc.edu and Outlook resolves the sender’s profile picture — making it indistinguishable from a real internal email.

Why CHLA Is Specifically Vulnerable

CHLA uses an external MX record (ESA / Abnormal gateway). This is the exact configuration InfoGuard identifies as vulnerable. The mail path is:

Attacker → MX (ESA/Abnormal) → Exchange Online → Inbox
                                  ↑
                          No AuthAs validation
                          No partner connector
The Key Header: X-MS-Exchange-Organization-AuthAs

This header is the linchpin. Exchange Online sets it automatically — it cannot be spoofed by external senders because EOP strips and re-stamps it on inbound messages.

Value Meaning

Internal

Message originated from within the Exchange organization (authenticated via org transport)

Anonymous

Message arrived via unauthenticated SMTP (no connector match)

Internet

Message arrived from an authenticated external source (matched an inbound connector, but NOT internal)

When AuthAs is anything other than Internal AND the From: address is an internal domain — that is a Ghost Sender. The message claims to be internal but Exchange knows it came from outside.

InfoGuard recommends a layered approach. Any one of these helps; all three together is defense in depth.

Mitigation 1: Mail Flow Rule (Transport Rule)

Quarantine all emails where AuthAs is NOT set to Internal and the source IP is not in the expected relay list.

The condition is AuthAs ≠ Internalnot AuthAs = Internet. This is broader and catches both Internet and Anonymous authentication types.
Mitigation 2: Partner Organization Connector

Configure an inbound connector of type "Partner Organization" with:

  • Domain scope: wildcard (*) — applies to all incoming mail

  • Validation: IP-based OR certificate-based

  • Action: reject emails not meeting authentication criteria

Only ~25% of affected orgs had this properly deployed per InfoGuard’s research.

Mitigation 3: Disable Direct Send

Direct Send allows internal spoofing on its own. Disabling it protects against one attack variant independently of the other two mitigations.

Testing Tool

InfoGuard provides a testing tool at ghost-sender.com that performs fingerprinting checks and validates whether partner connector mitigation is deployed. Recommend running this against the CHLA domain before and after mitigation.

PowerShell: Create the Transport Rule
Step 0 — Verify current state
# List existing transport rules
Get-TransportRule | Format-List Name, State, Priority, Description

# Check if any rule already references AuthAs
Get-TransportRule | Where-Object {
    $_.HeaderContainsMessageHeader -like "*AuthAs*" -or
    $_.Description -like "*AuthAs*" -or
    $_.HeaderMatchesMessageHeader -like "*AuthAs*"
} | Format-List Name, State

# Check existing inbound connectors (partner org connectors)
Get-InboundConnector | Format-List Name, Enabled, ConnectorType, SenderIPAddresses, SenderDomains, RequireTls, RestrictDomainsToCertificate
Step 1 — Create the mail flow rule (AuthAs ≠ Internal)
# CRITICAL: Condition is "AuthAs NOT Internal" — catches both Internet AND Anonymous
# ExceptIfSenderIpRanges = your MX gateway IPs (ESA/Abnormal) + any legitimate relays
#
# NOTE: -HeaderMatchesMessageHeader with -Not prefix is not directly supported.
# Instead, use ExceptIfHeaderMatchesMessageHeader to allow Internal:

New-TransportRule -Name "Ghost Sender Quarantine — AuthAs Not Internal" `
    -HeaderMatchesMessageHeader "X-MS-Exchange-Organization-AuthAs" `
    -HeaderMatchesPatterns "Anonymous|Internet" `
    -ExceptIfSenderIpRanges "104.193.219.13", "104.193.222.32/29" `
    -SetSCL 9 `
    -Quarantine $true `
    -GenerateIncidentReport "erosado@chla.usc.edu" `
    -IncidentReportContent "Sender", "Recipients", "Subject", "MessageId", "Headers" `
    -Priority 0 `
    -Mode Audit `
    -Comments "Ghost Sender mitigation per InfoGuard research (2026-06-09). AuthAs ≠ Internal + IP not in relay list → quarantine. POC validated by Mauricio Naranjo 2026-06-10. Deploy in Audit first."
The IP exceptions (ExceptIfSenderIpRanges) MUST include all legitimate external sources that send email to your Exchange tenant. This means ESA/Abnormal gateway IPs, any partner relays, SaaS apps that send on your behalf, etc. An incomplete list will quarantine legitimate mail.
Identify legitimate external senders of your domain
# What IPs are in the connection filter allow list?
Get-HostedConnectionFilterPolicy | Format-List IPAllowList

# What inbound connectors exist?
Get-InboundConnector | Format-List Name, Enabled, SenderIPAddresses, SenderDomains, ConnectorType

# What IPs have sent as chla.usc.edu in the last 30 days?
# (Use message trace — identify legitimate sources)
# NOTE: -SenderAddress does NOT support * wildcard in all tenants.
# Use pipeline filter instead:
Get-MessageTrace -StartDate (Get-Date).AddDays(-10) -EndDate (Get-Date) |
    Where-Object { $_.SenderAddress -like "*@chla.usc.edu" } |
    Group-Object FromIP |
    Sort-Object Count -Descending |
    Select-Object -First 20 Count, Name |
    Format-Table -AutoSize
GUI Equivalent (Exchange Admin Center)

For team members who prefer the GUI:

  1. Exchange Admin CenterMail flowRules

  2. Click + Add a ruleCreate a new rule

  3. Name: Ghost Sender Quarantine — AuthAs Not Internal

  4. Apply this rule if:

    • A message header matches → Header: X-MS-Exchange-Organization-AuthAs → Patterns: Anonymous|Internet

  5. Except if:

    • The sender IP address is in the range → add ESA/Abnormal gateway IPs + all legitimate relay IPs

  6. Do the following:

    • Deliver the message to the hosted quarantine

    • Set the spam confidence level (SCL) to 9

    • Generate incident report and send it to → security team DL

  7. Priority: 0 (highest)

  8. Mode: Audit first (test for 7 days, review incident reports, then enforce)

  9. Comments: Ghost Sender mitigation per InfoGuard (2026-06-09). POC validated Mauricio 2026-06-10.

Start in Audit mode. Review incident reports for false positives — any SaaS app, partner relay, or vendor sending through your MX that isn’t in the IP exception list will be caught. Build the exception list from 30-day message trace data first.
Partner Organization Connector (GUI)

This is the second mitigation — can be deployed alongside the transport rule.

  1. Exchange Admin CenterMail flowConnectors

  2. + Add a connector → Connection from: Partner organization → Connection to: Office 365

  3. Domains: * (wildcard — applies to all incoming mail)

  4. Authentication:

    • Verify by IP address → add ESA/Abnormal gateway IPs

    • OR Verify by certificate → specify the TLS certificate of your mail gateway

  5. Reject emails that don’t meet authentication criteria

This ensures only mail from your known gateway reaches Exchange Online. Combined with the transport rule, you have two layers.

Testing After Rule Creation
Verify the rule exists and is active
Get-TransportRule -Identity "Block Ghost Sender*" | Format-List Name, State, Priority, Mode, WhenChanged
Check rule hit count (after some time)
Get-TransportRule -Identity "Block Ghost Sender*" | Format-List Name, RuleHits
Message trace — find messages caught by the rule
Get-MessageTrace -StartDate (Get-Date).AddDays(-1) -EndDate (Get-Date) -Status "Quarantined" |
    Where-Object { $_.SenderAddress -like "*@chla.usc.edu" } |
    Format-Table Received, SenderAddress, RecipientAddress, Subject, Status -AutoSize
Review quarantined messages
Get-QuarantineMessage -StartReceivedDate (Get-Date).AddDays(-1) -EndReceivedDate (Get-Date) -Type TransportRule |
    Format-Table ReceivedTime, SenderAddress, RecipientAddress, Subject -AutoSize
Relationship to Existing Investigations
Investigation Connection

Spam bypass (SCL 9 → inbox, 06-08)

SFV:SKA override. Different vector — user safe sender list bypassing spam verdict. Ghost Sender is about identity spoofing, not spam scoring.

Abnormal Security deployment

Abnormal in read/write mode since 05-14. Abnormal detects BEC/impersonation via behavioral ML. Ghost Sender is a header-level spoofing vector that Abnormal may or may not flag — depends on whether it inspects AuthAs. The transport rule is a deterministic control that doesn’t depend on ML.

Hoxhunt phishing simulation (SCL -1)

SCL -1 bypass is intentional for phishing training. Ghost Sender rule should NOT affect Hoxhunt — Hoxhunt sends from external domains, not as *@chla.usc.edu. Verify.

Risk Assessment
Risk Severity Mitigation

False positives — legitimate relays quarantined

High

Audit mode first. Map ALL external senders of your domain via message trace before enforcing.

Rule bypass — attacker uses MAIL FROM matching internal domain

Medium

The AuthAs header is stamped by Exchange, not the sender. Even if MAIL FROM is internal, external origin gets AuthAs: Internet.

IP exception list incomplete

High

Query inbound connectors + connection filter + 30-day message trace for FromIP to build exhaustive list.

Hoxhunt disruption

Low

Hoxhunt sends from external domains (not @chla.usc.edu). Confirm with a test trace.

Partner/vendor relay disruption

Medium

Any vendor that sends email "as" your org (e.g., ticketing systems, CRM) will be caught. Build exception list.

Team Discussion Outcomes (2026-06-10)
Victor Negri — Rule Deployed

Victor created the transport rule manually via Exchange Admin Center console. CCR to follow.

Sarah Clizer — Third-Party SaaS Risk Question

Question: "Are we 100% sure we are OK with this change? Did we consider the risk for third-party SaaS sending as CHLA?"

Answer (Victor): Third-party SaaS apps that send as @chla.usc.edu should route through ESA first per MX configuration. If they do, they arrive from the ESA gateway IP — which is in the exception list. The rule does not touch them. The rule only catches mail sent directly to Exchange Online, bypassing the ESA entirely. Any SaaS bypassing ESA is itself a misconfiguration that should be flagged.

Conclusion: SaaS risk is addressed by the existing mail architecture. ESA is the trust boundary. Legitimate mail flows through ESA; the rule quarantines everything that doesn’t.

Operational Concern — Dual IP Maintenance

Issue (Victor): This rule introduces a second location where authorized sender IPs must be maintained. If a vendor like Hoxhunt adds new sending IPs, those IPs must be added to:

  1. The inbound connector

  2. The Ghost-Sender transport rule (ExceptIfSenderIpRanges)

If either is missed, mail is either quarantined (rule not updated) or bypasses the check (connector not updated).

Mitigation options:

Option Description Status

Process control

Add cross-reference step to change process: any sender IP modification requires updating both connector and transport rule. Document in CCR.

Minimum — do this now

Partner Organization connector with cert validation

InfoGuard’s second mitigation. Validates sender by TLS certificate instead of IP. Eliminates IP-based maintenance entirely — cert stays the same when IPs change.

Long-term — evaluate

Runbook

Create runbook: "Adding/Modifying Authorized Email Sender IPs" with both locations listed, verification commands, and rollback steps.

Recommended

Verification commands — check both locations match
# Transport rule exception IPs
(Get-TransportRule -Identity "Ghost-Sender*").ExceptIfSenderIpRanges

# Inbound connector IPs
Get-InboundConnector | Format-List Name, SenderIPAddresses

# Compare — any IP in one but not the other?
$ruleIPs = (Get-TransportRule -Identity "Ghost-Sender*").ExceptIfSenderIpRanges
$connectorIPs = (Get-InboundConnector | Where-Object { $_.Enabled }).SenderIPAddresses
Write-Host "Rule IPs: $($ruleIPs -join ', ')"
Write-Host "Connector IPs: $($connectorIPs -join ', ')"
Hoxhunt Validation (TODO)

Hoxhunt sends directly to Exchange Online to simulate real phishing. If Hoxhunt’s sending IPs are not in the exception list, phishing simulations will be quarantined.

Find Hoxhunt sending IPs
Get-MessageTrace -SenderAddress "*@hoxhunt*" `
    -StartDate (Get-Date).AddDays(-30) -EndDate (Get-Date) |
    Group-Object FromIP |
    Format-Table Count, Name -AutoSize
  • Run Hoxhunt IP trace

  • Confirm IPs are in both the connector AND the transport rule exception list

  • If missing — add before next Hoxhunt campaign or simulations will be quarantined

Microsoft Disclosure Timeline (InfoGuard)
Date Event

2026-04-21

InfoGuard reported to MSRC. Closed as non-MSRC case — "not a security vulnerability."

2026-04-22

Active spoofing campaign detected. Microsoft temporarily fixed internal spoofing.

2026-04-27

Microsoft reverted the internal spoofing mitigation.

2026-05-29

Microsoft general support: "known architectural limitation." Suggests changing MX to M365 or adding headers (InfoGuard says this doesn’t fix it).

2026-06-09

InfoGuard publishes Ghost-Sender research. Dark Reading coverage. Testing tool at ghost-sender.com.

2026-06-10

Mauricio validates POC against CHLA tenant. Team meeting today.

Decision Matrix for Meeting
Option Description Recommendation

Transport rule in Audit mode + build IP exception list

Deploy rule (AuthAs ≠ Internal → quarantine) in audit mode for 7 days. Simultaneously build IP exception list from 30-day message trace + inbound connectors. Review incident reports for false positives. Then enforce.

Recommended — start here

Partner Organization connector

Configure inbound connector with wildcard domain, IP/cert validation. Rejects unauthorized mail at SMTP level. Deploy alongside the transport rule for defense in depth.

Recommended — deploy in parallel

Disable Direct Send

Blocks internal-to-internal spoofing variant independently. Low risk, high value.

Recommended — quick win

Enforce transport rule immediately

Deploy in enforce mode without audit period. Risk of disrupting legitimate relay traffic.

Only if evidence of active exploitation against CHLA specifically

Abnormal-only

Rely on Abnormal ML to detect Ghost Sender. No transport rule.

Not recommended — behavioral detection is probabilistic; transport rule is deterministic. Microsoft’s Enhanced Filtering and presets do NOT prevent this.

Change MX to point directly to M365

Microsoft’s own suggestion. Eliminates the vulnerable configuration entirely.

Not feasible — ESA/Abnormal are active in the mail path

Quarterly Development Summary (Q2 2026)

Q2 2026 — Quarterly Development Summary

Overview

Q2 spans approximately 70 working days (2026-04-01 through 2026-06-10). Worklogs document daily accomplishments. This quarter saw major project leadership across email security, SIEM migration, ISE operations, and automation development.

1. Email Security — Abnormal Security Deployment (Lead)
Milestone Details

CR-2026-05-07 submitted & approved

Change request for Abnormal read/write mode activation. CAB approved 2026-05-12.

Implementation 2026-05-13/14

Read/write mode enabled for pilot group. Post-deployment validation ongoing.

MDO/EOP Policy Audit

20-section PowerShell audit of Defender for Office 365 configuration. 40+ cmdlets documented. Mapped ESA policies to EOP equivalents.

Hoxhunt Investigation

SCL -1 bypass confirmed intentional for phishing simulation training.

Spam Bypass Investigation (P1)

SFV:SKA safe sender override delivering SCL 9 spam to inbox. Root cause identified, remediation commands prepared.

Ghost Sender Response (P0, today)

Mauricio validated POC. Transport rule mitigation designed. Meeting today.

Connect-ExchangeOnline proficiency

Both PowerShell and GUI workflows documented. Message trace, quarantine, audit log, ORCA analysis.

2. SIEM Migration — QRadar → Sentinel (Lead)
Milestone Details

Architecture decision

Sources → rsyslog (CHLXSYSLOG01) → Monad → Sentinel. Centralized collection tier provides disk-assisted queues, protocol normalization, edge filtering.

Monad console error resolved

Secrets configured in CHLA production tenant (2026-05-12).

ISE secure syslog integration

TLS cert imported, remote logging target configured on ISE. Streaming validation in progress.

rsyslog server operational

ASA logs confirmed landing (2026-06-08). ISE, switch, FMC source configs documented (2026-06-09). 5 runbooks created.

Monad pipeline design

rsyslog omfwd forwarding with disk-assisted queue (500MB buffer). 8-step Monad API pipeline creation documented.

Blocking items

DCR not yet created (Rule ID + Stream Name). Azure private network policy unresolved. Victor + Mauricio action.

3. MSCHAPv2 Migration Report
Milestone Details

6-sheet Standard Report

Executive summary, trend analysis, 5 migration waves, device detail, stale endpoint analysis, policy match by protocol.

Scale

~6,227 devices across 5 waves. Migration window 2026-05-04 to 2026-05-30.

Sheet 6 addition (2026-05-14)

Policy match by protocol for removal planning + anonymous identity validation.

SQL automation

ISE DataConnect queries optimized for report generation.

4. ISE Operations & API Development
Milestone Details

TCP Clocks deployment

Batch 1 + Batch 2: 9+ MACs onboarded via ERS batch API with exists-check. Switch AAA config gap discovered (new switch deployed without RADIUS).

IoT device management

Dr. Kim sleep study devices, watch recurrence tracking. iPSK enrollment validation. 5 incident iterations.

BMS Controller Segmentation

Full migration from Principia LaTeX. 77 device models, 7 buildings. 12 AsciiDoc partials, 5 Mermaid diagrams. Claroty xDome integration (risk 63.8 → 42.4).

ISE ERS/MnT/DataConnect tooling

Modular API testing, batch operations, self-contained heredoc patterns. dsource + ers() function workflow.

Zebra print investigation

217 devices analyzed, 0 failures found.

ISE lab rotation

ISE eval rotation on kvm-01. Phases 0-2 complete.

Vocera EAP-TLS

~10 phones failing 802.1X, missing supplicant config identified. Scheduled with clinical engineering.

5. ASA VPN Migration Plan — Okta RADIUS → Entra SAML
Milestone Details

Full migration plan built

5-phase timeline. ASA baseline captured: 2 tunnel groups, group policies, crypto/certs, DAP, active sessions.

ISE role analysis

Before/after comparison — ISE keeps authorization, accounting, posture. Only authentication moves to SAML.

Risk matrix

6 risks identified including VPN SSL cert expiring 2026-07-28 (49 days).

Multi-team coordination

Tony Sun (ASA), Justin Halbmann (Entra), Evan (ISE).

6. Incident Response & Case Studies
  • CVE-2026-20147 (ISE Patch 10, CVSS 9.9) — tracked, maintenance window pending (90 days)

  • CVE-2026-31431 (Copy Fail) — investigated and responded 2026-05-01

  • Dirty Frag CVE — investigated 2026-05-08

  • Dual KVM Hypervisor Outage — P1 incident, full recovery (2026-04-13)

  • Guest Redirect ACL — Mandiant remediation follow-up

  • Murus Portae (WAF) — FMC cert expired, ACP returns zero rules investigation

  • Strongline Gateway — 8 devices in wrong identity group

7. Automation & Tooling Development
  • Python dashboard suite — 4 scripts, 54 panels, pandas integration

  • Dashboard system — 37 visual assets (11 SVG + 14 PNG + 12 domain Graphviz)

  • YAML substrateproject-master.yaml (37 projects), association engine (107+ triples)

  • AsciiDoc build toolchain — custom roles, multi-format output, Rouge theme overhaul

  • Report generatordomus-report.py with 14 matplotlib charts

8. Professional Development (Education)
  • CISSP — exam scheduled 2026-07-12 (32 days)

  • C programming — Phase 0 complete, 11-phase curriculum (stdio → syscalls → kernel → security)

  • Don Quijote — Chapters 1–35+ in Spanish (analytical reading with Pedro, tutor)

  • Classical rhetoric — De Oratore Latin study, dispositio framework applied to technical writing

  • CLI mastery — awk, sed, grep, find, xargs, lsof pipelines, strace/ftrace/AppArmor bridge

Talking Points for CISO
  1. Email security posture is stronger than 90 days ago. Abnormal in read/write, MDO/EOP fully audited, Ghost Sender mitigation designed today.

  2. SIEM migration is architecturally sound. rsyslog collection tier operational, Monad pipeline designed, blocking on DCR creation (not our dependency).

  3. ISE operations are data-driven. ERS batch API, DataConnect SQL, 6-sheet MSCHAPv2 report. Decisions backed by evidence.

  4. Incident response is documented and repeatable. Every investigation has a structured runbook with PowerShell commands.

  5. Automation trajectory. PowerShell + Python + shell — building tools that produce repeatable results, not one-off fixes.

Session Summary

Session Summary — 2026-06-10

Morning (Pre-Work)
  • Reviewed CLAUDE.md system architecture

  • Populated daily notes triage with 16 carryover items from 06-09

  • Created Ghost-Sender investigation document (20k) from InfoGuard research

  • Corrected header condition from AuthAs = Internet to AuthAs ≠ Internal

  • Created D2 diagram: attack flow → ESA gateway → EOP → transport rule

  • Created quarterly development summary (Q2 2026) — 8 domains, 5 CISO talking points

  • Created meeting schedule with pre-meeting blockers for 5 meetings

  • Created TCP clocks validation section with ERS API query

Ghost-Sender Response (All Day)
  • 3 mitigations documented: transport rule, partner org connector, disable Direct Send

  • Victor deployed rule via Exchange console — team discussion outcomes captured

  • Sarah’s SaaS risk answered: ESA is the trust boundary

  • Dual IP maintenance concern documented with verification PowerShell

  • Hoxhunt protection confirmed (3 IPs + /25 range in exception list)

  • Audit output: 9 IPs in sync between rule and connectors

  • Validation commands partial: 6-section quick audit script

  • Email hygiene section: SCL/BCL/SFV terminology, quarantine analysis, DKIM

SIEM — rsyslog → Monad Pipeline (Live)
  • Configured TLS forwarding on CHLXSYSLOG01 to *.l4.monad.com

  • Resolved 2 errors: StreamDriver.permittedPeers typo, StreamDriverAuthMode="anon"

  • 337 queued messages recovered from disk-assisted queue

  • Installed gawk, jq, neovim on production Debian 13

  • 500+ line rsyslog operational command reference (ISE/ASA/FMC/switch)

  • Color-coded live dashboards: ISE auth, all-source unified feed, cross-source summary

  • Per-source log file analysis for /var/log/%hostname%/

  • 9-step troubleshooting runbook for new source not arriving

  • Session summary distilled from 45k-line history dump

  • Facility explanation: LOCAL6 is routing label, not content modifier

Meetings (5)
  • TCP Time Clock connectivity review — batch 2 status, switch AAA gap

  • MS Log Forwarder working session — rsyslog → Monad demo, ISE connected live

  • Abnormal Security weekly — Ghost-Sender lead topic, rule deployed

  • 1-1 InfoSec with CISO — quarterly development review

  • Team impressed by WSL/Arch/neovim on production server

d001 Project Architecture (21 Projects)
  • Every d001 project reorganized: assembler at root, partials/ for content, archive/ for old iterations

  • 21/21 projects clean — zero loose files

  • 17 new assemblers created, all encrypted, all buildable via d001 build

  • Inbox partials created for SIEM + Abnormal

M365 Message Trace Investigation
  • 10-step Exchange Online investigation using Get-MessageTraceV2

  • V1 → V2 iteration: added null checks, deterministic sort, throw on failure

  • Graduated from SIEM inbox to abnormal-security/partials/message-trace-investigation.adoc

  • Transport rule gotcha: Event is "Transport rule Prepend subject" not "TransportRule"

  • FromIP 216.71.144.209 = Cisco ESA — matches Ghost-Sender exception list

Graphviz Study
  • Created data/d000/education/graphviz-study/ with partials + assembler

  • 4 lessons: fundamentals, port anchoring, clusters/tiers, edge styles

  • 2 diagrams: firewall zones (VyOS), infrastructure topology (port-anchored 3560CX)

  • HTML TABLE port pattern — color-coded cells, individual port anchoring

Portable nvim Config
  • dots-quantum/nvim-portable/init.lua — 160 lines, zero plugins

  • All domus-nvim muscle memory: space-leader, jk, centering, visual move, paste-without-yank

  • Usage: nvim -u <(cat init.lua) — nothing on disk

Commands Learned
  • sed -i '1760s/old/new/' — line-targeted replacement

  • grep -oP 'KEY=\K[^,;]+' — value extraction with \K reset

  • rsyslogd -N1 — config validation without restart

  • Command substitution $(…​) vs process substitution <(…​)

  • tee heredoc for Graphviz diagrams

  • write memory on L2 switch generates syslog (no loopback)

Stats
  • 34+ commits across domus-captures and dots-quantum

  • 21 d001 projects reorganized (62 files moved)

  • 500+ lines rsyslog commands

  • 216 lines PowerShell commands added

  • 160 lines portable nvim config

  • 4 Graphviz lessons + 2 production diagrams

Keycloak Rebuild — Evening Session

Keycloak Rebuild — P0 Triage & Project Scaffold

What We Did
  • P0 task inventory — triaged all P0 items across work (11 items) and personal (3 items) trackers

    • Work: MSCHAPv2 report (54d DUE), SIEM migration (61d), Monad pipeline (91d), ISE Patch 10 CVSS 9.9 (90d OVERDUE), k3s NAT (93d BLOCKING), + 6 more

    • Personal: netapi commercialization, Ollama API, ThinkPad T16g setup

  • Keycloak rebuild decision — keycloak-01 corrupted (SPOF), SSO broken for ISE admin + Grafana

    • Evaluated alternatives: SimpleSAMLphp, Authentik, Kanidm — chose Keycloak (SAML+OIDC, REST API proven in INC-2026-02-14-001)

    • KVM VM over container — avoids k3s NAT blocker, provides snapshot capability

    • PostgreSQL over H2 — durability, prior corruption may be H2-related

  • Project scaffolded per STD-001 — 8 files created:

    • partials/projects/keycloak-rebuild/ — metadata, summary (8-phase plan), assessment (risk + decision log), appendix-issues, appendix-todos

    • pages/projects/personal/keycloak-rebuild/ — index, appendix-issues, appendix-todos

    • Nav entry added after ThinkPad P16g, before RHEL 9 Workstation

  • Session command capture — extracted 36 bash commands + 22 reads from JSONL, organized by phase with learning-worthy patterns

    • Saved to data/d000/sessions/SESSION-2026-06-10-keycloak-rebuild.adoc — encrypt before commit

  • Antora attributes verifiedkeycloak-01.inside.domusdigitalis.dev, 10.50.1.80 (10.50.1.80), domus (domus), KVM hosts all exist

What’s Next (Resume Session)
  • Answer open questions — KVM host (kvm-01 or kvm-02), base OS, realm name (domusdigitalis vs domus), full SP inventory

  • Write phase partials — phase-00 through phase-07 as implementation runbook

  • Update trackers — promote Keycloak from P3/deferred to active in SPOF, HA status, and deferred trackers

  • Build verify — user runs make to validate scaffold

Before Commit
# Encrypt session capture
encrypt-file data/d000/sessions/SESSION-2026-06-10-keycloak-rebuild.adoc

# Stage
git add data/d000/sessions/SESSION-2026-06-10-keycloak-rebuild.adoc.age \
  docs/modules/ROOT/partials/projects/keycloak-rebuild/ \
  docs/modules/ROOT/pages/projects/personal/keycloak-rebuild/ \
  docs/modules/ROOT/nav.adoc \
  docs/modules/ROOT/partials/worklog/daily-notes/2026-06-10/keycloak-rebuild-session.adoc \
  docs/modules/ROOT/partials/worklog/daily-notes/2026-06-10.adoc

Ad-Hoc Requests

Ad-Hoc Requests

URGENT: Ghost Sender — Exchange Online Spoofing Exploit
Field Value

Reported

2026-06-10 (morning)

Reporter

Mauricio Naranjo — validated POC exploit

Severity

P0 — active spoofing vector

Meeting

Scheduled today — team discussion on mitigation

Investigation

daily-notes/2026-06-10/ghost-sender-investigation.adoc

Diagram

examples/diagrams/ghost-sender-mail-flow.d2

What happened: InfoGuard (Swiss cybersecurity firm) published Ghost-Sender research on 2026-06-09. Mauricio validated the POC against CHLA’s tenant the same day. An external attacker can spoof any From: address (internal or external) — Outlook resolves profile pictures for spoofed internal senders. SPF/DKIM/DMARC are bypassed entirely. No CVE — MSRC rejected as "known architectural limitation." Evidence of active exploitation in the wild.

CHLA is specifically vulnerable because we use external MX (ESA/Abnormal gateway). This is the exact configuration InfoGuard identifies.

Three mitigations (InfoGuard recommended):

  1. Transport rule: quarantine where AuthAs ≠ Internal (matches both Internet and Anonymous) except from ESA/Abnormal gateway IPs

  2. Partner Organization connector: inbound connector with wildcard domain, IP/cert validation — rejects unauthorized mail at SMTP level

  3. Disable Direct Send: blocks internal spoofing variant independently

Preparation for meeting:

  • Review Ghost Sender investigation document — understand the exploit mechanism

  • Review D2 diagram — visualize the mail flow with and without the rule

  • Confirm current transport rules: Get-TransportRule | Format-List Name, State, Priority

  • Check existing X-MS-Exchange-Organization-AuthAs rules: Get-TransportRule | Where-Object { $.HeaderContainsMessageHeader -like "AuthAs" -or $.Description -like "AuthAs" }

  • Have Connect-ExchangeOnline ready — both PowerShell and GUI approaches needed since team uses both

Key concepts to know:

  • X-MS-Exchange-Organization-AuthAs: Header set by Exchange Online — cannot be spoofed by external senders. Values: Internal (org-authenticated), Anonymous (unauthenticated SMTP), Internet (external via connector). The rule catches anything that is NOT Internal.

  • Mail flow rules (transport rules): Evaluated in priority order, can quarantine, redirect, reject. Deploy in Audit mode first.

  • Partner Organization connector: Inbound connector that validates sender IP or TLS cert. Rejects at SMTP level.

  • Direct Send: Feature that allows SMTP relay from internal apps. Disabling prevents internal spoofing variant.

  • Testing tool: ghost-sender.com — run against CHLA domain to validate before/after mitigation.


Work (CHLA)

CHARGE TIME IN PEOPLESOFT - CRITICAL. Do this NOW before anything else.

Critical (P0)

Project Description Owner Status Due Blocker

Linux Research (Xianming Ding)

EAP-TLS for Linux workstations, dACL, UFW

Evan

BEHIND (72 days overdue)

02-24

Certificate "password required" - nmcli fix documented

iPSK Manager

Pre-shared key automation

Ben Castillo

BEHIND

 — 

DB replication issues

MSCHAPv2 Migration

Legacy auth deprecation — 6,227 devices, 5 waves. 6 batch SQL queries + 3-API endpoint profile script added (05-11). Report due.

Evan

25% — Report due, batch queries ready

05-30

Report to turn in

Research Segmentation

All endpoints to Untrusted VLAN

Evan

BLOCKED

 — 

CISO decision pending

Disaster Recovery

ISE DR scoping — dot1x closed mode = total blackout

Evan

Scoping

 — 

 — 

Mandiant Remediation

Copy 4/16 findings, Guest ACL lab, Q2 assessment

Evan

Active

 — 

 — 

SIEM QRadar → Sentinel

Full SIEM platform transition. Monad console error resolved 05-12. Secrets configured. Blocked on DCR creation (Rule ID + Stream Name). Azure private network policy unresolved.

Evan

Active — blocked on DCR

Q2 2026

Victor/Mauricio: create DCR, resolve Azure network policy

Abnormal Security

AI email platform — ESA cutover. CR assigned, CAB May 12 15:00. Implementation May 14 10:00.

Evan

Active — CAB today 15:00

05-14

Pre-CAB checklist: confirm Tyler, Jason, Sarah

High Priority (P1)

Project Description Owner Status Target

ISE 3.4 Migration

Upgrade from 3.2p9

Evan

Blocked — maintenance window needed

Q2 2026

Switch Upgrades

IOS-XE fleet update (C9300, 3560CX)

Evan

Pending

Q2 2026

Spikewell BYOD VPN

dACL SQL, AD group integration

Evan

Active

 — 

Strongline Gateway

MAC capture, Identity Group setup — 37 days aging

Evan

Active — David Rukiza assigned

 — 

Abnormal Security

AI email security platform research, ESA cutover timeline

Evan

Newly assigned

 — 

DMZ Migration

External services audit behind NetScaler

Evan

Audit phase

 — 

Firewall Audit (murus-portae)

EtherChannel query, prefilter, policy assignments

Evan

Scoping — ASA API creds needed

 — 

iPSK Manager HA

Server 2 config, TLS, SQL security audit

Evan

In progress

 — 

Sentinel KQL

Build proficiency, distinguish from team

Evan

Onboarding

 — 

VNC Blocking

Block and eliminate VNC protocol enterprise-wide

Evan

Active — Phase 0 (Discovery)

Mid-June 2026

Strategic (P2)

Project Description Owner Status

HHS Regulatory Compliance

New HHS security policies implementation

TBD

NOT STARTED

InfoSec Reporting Dashboard

PowerBI metrics for executives

TBD

NOT STARTED

EDR Migration (AMP → Defender)

Endpoint protection consolidation

TBD

NOT STARTED

Azure Legacy Migration

Modern landing zone

Team

In Progress

ChromeOS EAP-TLS

SCEP + Victor, Paul testing

Victor

In Progress

P0 — Critical / Blocking

Security & Compliance

  • ISE 3.2 Patch 10 upgrade — CVE-2026-20147 CVSS 9.9 / CVE-2026-20148. Propose maintenance window once patch confirmed on software.cisco.com.

  • ISE Advisory sa-ise-rce-traversal-8bYndVrZ — check Patch 10 availability

  • Mandiant Remediation — findings status tracked. Working session prep + defensive posture documented (comms-2026-04-24). Copy 4/16 updates into Excel at work. Guest ACL lockdown (WIR-M-01) pending lab validation. appendix-todos updated with MSCHAPv2 milestones.

  • Guest ACL update — guest redirect ACL work needed. Lab validate GUEST_CWA_REDIRECT_MAX_SECURITY in d000, then joint CR with NE. On today’s task list.

  • Disaster Recovery & Downtime Procedures — ISE top priority (dot1x closed mode = SPOF for network access)

    • ISE DR: Document failover sequence — PAN, MnT, PSN priority order

    • ISE DR: RADIUS dead-server detection on WLCs/switches — critical-auth VLAN fallback

    • ISE DR: Backup/restore procedures — scheduled config backups, tested restores

    • FTD/FMC DR: FMC loss = no policy management

    • Network DR: Core/distribution switch failure, STP reconvergence, HSRP failover

    • Document RTO/RPO per system

SIEM Migration (QRadar → Sentinel)

  • SIEM QRadar → Sentinel Migration — LEAD ROLE. 4 collection iterations (Apr 16, 17, 17-streamlined, 20-streamlined). Python chart pipeline built (qradar-charts.py). Migration XLSX generated. Verification pending. Comms sent Apr 23.

    • d001 artifacts: 8 JSON exports, 2 CSV inventories, migration XLSX, top5 source SVG/PNG, verification doc

    • Dependency: Monad pipeline for log source transition

    • Dependency: Sentinel KQL proficiency for query migration

  • Monad Pipeline Evaluation (origin: 2026-03-11) — lead role. Console error RESOLVED 05-12. 06-09: Architecture decision — rsyslog (CHLXSYSLOG01) as collection tier → Monad → Sentinel. ISE lab → rsyslog → Monad 6-step execution guide created with 10 API calls. ASA lab logs already flowing through rsyslog. DCR still needed — Victor + Mauricio.

  • Sentinel KQL — build proficiency, distinguish from team. Azure portal access acquired.

  • QRadar log source report — run AQL queries, fetch JSON, generate Python Excel

Active Deployments & Migrations

  • MSCHAPv2 Migration — 6-sheet Standard Report ready. Migration window 05-04 to 05-30 CLOSED. Confirm final report status and next steps with team. 6,227 MSCHAPv2 devices, 14,249 EAP-TLS/TEAP (70% migrated).

  • MSCHAPv2 weekly cadence — recurring Wednesday call established (first 04-22). Completed 2026-04-22.

  • MSCHAPv2 ownership matrix — sent in scoping email 4/24 with manager callouts (@Albert, @John). Completed 2026-04-24.

  • TCP Clocks deployment — Batch 1: 7 clocks validated (OUI 40:AC:8D). Batch 2 (06-09): 9 new MACs (OUI 40:AC:BD) added via ERS. 1 one-off reassigned. New switch deployed without RADIUS/AAA — clocks can’t authenticate. Switch onboarding template + 3 validation queries documented. ERS queries self-contained with ers function.

  • SRT Research VLAN — confirm roles with Tony Sun: Tony implementor, Evan tester. CAB approved 04-21.

  • Downtime Computers enforcement — draft ISE AuthZ rule: medigate_724 + Wireless = DenyAccess. Separate CR. d001: DC queries, audit CSVs (v1-v3), wireless violations report delivered 04-21.

  • Enterprise Linux 802.1X — standardize Shahab/Ding deployment (CISO priority). Overdue since 02-24. Blocked by nmcli cert fix.

  • Abnormal Security — CR-2026-05-07. Implemented 05-13. 06-09 update: Full policy review — 20-section EOP validation commands rebuilt, Hoxhunt SCL-1 investigation (intentional bypass confirmed), sclizer junk folder triage (~800 emails), Outlook reactions audit added, Connect-ExchangeOnline msalruntime fix documented. ESA migration expansion in progress — priority to move off ESA to full environment.

    • Team: Cox/William, Landeros/Jason, Rosado/Evan, Naranjo/Mauricio, Sandoval/Carlos

  • ASA VPN: Okta RADIUS → Entra SAML — (NEW 06-09) 5-phase migration plan built. ASA baseline captured (2 tunnel groups: CHLA_CORPORATE_USERS, CHLA_BYOD_USERS). 6 ISE policy screenshots. Tony Sun (ASA), Justin Halbmann (Entra/Okta), Evan (ISE). VPN cert expires 07-28. PDF deliverable ready. Share with team this week.

Tube System Upgrade (NEW — 06-01)

  • Tube System Upgrade — iTrack 3528165. 15x 10" TS stations need MAC addresses added to ISE identity group IoT_Onboard. MACs received from vendor (C8:1A:FE:20:xx:xx series). Station list spans ICU (CTICU, PICU, BMT, NICU, NICCU), ED, Surgery, Trauma, Pharmacy. Vendor contact: John Genest. Rationale: manufacturer no longer supports current system; failure risks delayed/missed patient care.

BMS Controller Segmentation (MIGRATED — 06-09)

  • BMS Controller Segmentation — Full migration from Principia LaTeX to d001. 12 partials, 5 Mermaid diagrams, 4 legacy PDFs, ISE screenshots. d001 open bms-controller. Completed 2026-06-09.

BMS Device Inventory (NEW — 04-24)

  • BMS Device Inventory — 72 devices discovered across 37 switches (04-24). Profile-driven architecture (Claroty/Medigate). 16 queries built. Phase 0 complete. Next: cross-reference with Visio diagrams, classify by function, begin D2 diagrams. Cleanup: delete 4 orphaned test groups, migrate 4 retire-dACL devices, investigate 3 null-profile devices.

VNC Blocking (NEW — 05-11)

  • VNC Blocking — block and eliminate VNC enterprise-wide. Due mid-June 2026. Phase 0: discovery. January AQL query baseline to incorporate. Cross-reference BMS inventory for VNC-capable devices.

Investigations & Audits

  • Murus Portae (WAF) — Phase 0 discovery in progress. FMC cert expired. d001: DMZ NetScaler WAF investigation, zone map, architecture D2 diagrams (v1+v2 SVGs), FMC REST API reference guide, ops script. FMC API returning zero ACP rules — under investigation.

  • Firewall audit — FMC discovery inventory done (d001: fmc-discovery-2026-04-16). EtherChannel query, prefilter, policy assignments pending.

  • IoT Dr. Kim devices — RECURRING. All 4 MACs validated in IoT_iPSK_VLAN1620_Misc (04-24). v2 validation queries built with 7 deep analysis queries (group flapping, credential leakage, profile drift, NAS tracking, remediation timeline, deny audit, OUI scan). Revalidate — confirm no flapping since 04-24.

  • IoT device validation queries — v2 created with partials architecture, 16 queries across ERS/MnT/DataConnect/FMC. Completed 2026-04-24.

Stale Blockers (carried via carryover tracker)

  • k3s NAT verification — rule 170, 10.42.0.0/16 pod network (origin: 2026-03-09). 92 days. Blocks Wazuh indexer recovery → blocks SIEM visibility. Decide: test or defer to Q3.

  • Strongline Gateway VLAN fix — 8 devices wrong identity group (origin: 2026-03-16). 85 days. David Rukiza assigned — follow up on status.

Administrative

  • PeopleSoft — track time for current week

  • iTrack tickets — close open tickets

  • KQL library — build initial queries in codex + d001

  • Linux Research project — finalize and review

  • Tax filing 2025 (MFJ) — see encrypted case file in data/d000/personal/ for details and action items

P1 — Important

  • MSCHAPv2 action-item tracker — owner/status/next-steps per workstream

  • ISE admin MFA enforcement — recommendation tied to advisory (interim control pending Patch 10)

  • DMZ Migration — external services audit behind NetScaler. Linked to Murus Portae investigation.

  • Vocera/Wyse iTrack RCA — complete root cause report

  • GCC ISE Support — 3/4 nodes restored, PSN-04 deferred

  • Wazuh indexer recovery — blocked by k3s NAT (origin: 2026-03-09)

  • Vocera EAP-TLS Supplicant Fix (origin: 2026-03-12)

  • iPSK Manager HA — blocked by DB replication (Ben Castillo)

  • ISE 3.4 Migration — depends on Patch 10 completion first

  • Git history scrub — murus-portae-output.md + ise-analytics CSVs

  • Encrypt prep-cmds-2026-04-15.adoc — plaintext committed to git

  • ISE MnT Messaging Service — enable UDP syslog delivery (maintenance window needed)

Infrastructure (Personal)

  • Borg backups — test and validate on ALL systems (Razer, P16g, vault-01, bind-01, kvm-01, kvm-02)

  • Borg — verify backup script paths updated from dotfiles-optimus to dots-quantum

  • Borg — create initial archive for ThinkPad P16g if none exists

  • Libvirt VLAN hook debug on both KVMs

  • Te1/0/2 cable replacement and re-test

  • Vault Raft cluster — verify vault-01 rejoined

  • Fix EAP-TLS keyring/secrets issue on Razer workstation

Completed (confirmed — do not delete, archive only)

  • CR-2026-04-15 SRT Research VLAN — submitted to iTrack. Completed 2026-04-15.

  • CAB presentation 4/21 — SRT Research VLAN 233 → CHLA-Research. APPROVED. Completed 2026-04-21.

  • Downtime Computers wireless audit — 45 computers, 16 violating, v3 report delivered. Completed 2026-04-21.

  • Git identity fix — dots-quantum/git/.gitconfig email corrected. Completed 2026-04-21.

  • MSCHAPv2 10:30 meeting — next steps + ACL coordination. Completed 2026-04-17.

Service Requests (SR)

SR# Request Requestor Opened Status

3508542

Zoll cards connection issue

STALE — verify in iTrack

3508524

Disable dot1x on (2) network ports - 5th floor 3250 Wilshire (PXE-boot imaging issues)

STALE — verify in iTrack (issues persisted after disable)

3528165

Tube System Upgrade — 15 stations, MAC addresses for ISE IoT_Onboard identity group

Genest, John (vendor contact)

2026-06-01

NEW — MACs received, need ISE onboarding

Incidents (INC)

INC# Priority Description Opened SLA Status

1911859

Strongline Gateways in Miscellaneous Subnet

STALE — verify in iTrack (related to carryover P0)

Change Requests - Emergency (ECAB)

CR# Description Opened Scheduled Status

No emergency changes

Change Requests - Normal

CR# Description Opened Scheduled Status

No normal changes

Change Requests - Scheduled/Standard

CR# Description Opened Window Status

No scheduled changes

Change Requests - Root Cause / Post-Incident

CR# Description Related INC Opened Status

100451

Vocera Phones and Wyse devices went off network

STALE — verify in iTrack


Session Accomplishments (Claude Code)

Ghost-Sender — Direct Send Exploitation Response

  • InfoGuard research (published 2026-06-09) analyzed — corrected header condition from AuthAs = Internet to AuthAs ≠ Internal

  • 3 mitigations documented: transport rule, partner org connector, disable Direct Send

  • Investigation doc: 20k, full PowerShell (CLI + GUI), risk assessment, decision matrix, Microsoft disclosure timeline

  • D2 diagram: attack flow → ESA gateway → EOP → AuthAs stamp → rule decision → quarantine/allow

  • Team discussion outcomes captured: Victor’s rule deployment, Sarah’s SaaS risk answer, dual IP maintenance concern

  • Ghost-Sender validation commands: 6-section audit script with PASS/FAIL output

  • Email hygiene section added: SCL/BCL/SFV terminology, quarantine analysis, junk config, DKIM, quick posture audit

  • d001 audit output: IP sync verified (9 IPs match between rule + connectors), Hoxhunt protected, ESA protected

  • Dark Reading article analyzed, InfoGuard testing tool documented (ghost-sender.com)

SIEM — CHLXSYSLOG01 rsyslog → Monad Pipeline (Live)

  • Configured /etc/rsyslog.d/10-forward.conf — TLS forwarding to *.l4.monad.com

  • Resolved 2 errors: StreamDriver.permittedPeers typo → camelCase, CA cert path → StreamDriverAuthMode="anon"

  • 337 queued messages recovered from disk-assisted queue on restart

  • Installed gawk (mawk lacks capture groups), jq, neovim on production Debian 13

  • Installed rsyslog-gnutls for TLS support

  • 500+ line rsyslog operational command reference (scripts/rsyslog-commands.adoc):

    • ISE: auth dashboard (green/red PASS/FAIL with running counters), per-file analysis (accounting, diagnostics, admin audit), failure reasons, NAS breakdown

    • ASA: severity breakdown, deny events, VPN sessions

    • FMC/FTD: message count, connection events, IPS/intrusion

    • Switch: 802.1X/MAB events, interface flap detection, port security

    • Cross-source: unified color-coded feed, hourly timeline, summary dashboard

    • Per-source log file analysis for /var/log/%hostname%/ (correct path, not /var/log/remote/)

    • Troubleshooting: 9-step diagnostic for new source not arriving, root cause matrix

  • Session summary distilled from 45k-line history dump using grep -oP, awk NR ranges

  • Log source inventory: ISE, ASA, 2 switches (by IP), 2 Windows endpoints, Tenable scanner

  • Garbage directory issue documented (malformed Windows EventLog %hostname% parsing)

  • Tony’s switch (10.193.144.250) — not arriving, L2 SVI Vlan144, troubleshooting deferred

  • ISE health warning: RabbitMQ container PID limit at 310%

d001 Root Assemblers Created

  • data/d001/projects/abnormal-security/abnormal-security.adoc — 19 partials, 6 logical sections

  • data/d001/projects/siem-qradar-to-sentinel/siem-qradar-to-sentinel.adoc — 14 partials

  • Both buildable: build-adoc.sh <assembler>.adoc pdf --theme light-cyan

TCP Clocks — Connectivity Review

  • Validation query added to meetings partial — ERS API check for group + description

  • Batch 1 + Batch 2 status documented

  • Switch AAA config gap — new switch deployed without RADIUS config

Quarterly Development Summary (Q2 2026)

  • 8-domain summary: email security, SIEM migration, MSCHAPv2, ISE operations, ASA VPN, incidents, automation, education

  • 5 CISO talking points prepared for 1-1

Worklog Infrastructure

  • Daily notes triage: 16 carryover items from 06-09

  • 4 meeting partials with pre-meeting blockers and d001 decrypt commands

  • Facility explanation documented (LOCAL6 is routing label, not content modifier)

Commands & Patterns Learned

grep -oP with \K (Perl regex value extraction)

  • grep -oP 'FailureReason=\K[^,;]+' file — extract just the value after a key

  • \K resets match start — everything before is required but not in output

  • Pipe to sort -u for unique, sort | uniq -c | sort -rn for counted

rsyslogd -N1 (config validation without restart)

  • Validates config syntax without touching running service

  • Safe pattern: sudo rsyslogd -N1 2>&1 && sudo systemctl restart rsyslog || echo "FIX CONFIG FIRST"

StreamDriverAuthMode="anon" (TLS without cert verification)

  • Encrypts syslog forwarding without verifying Monad’s certificate chain

  • Appropriate for SaaS vendor endpoints where DNS resolution is trusted

gawk vs mawk

  • Debian default mawk lacks match($0, /regex/, array) capture groups

  • gawk required for all ISE/ASA analysis commands — sudo apt install -y gawk

d001 Project Architecture — Full Reorganization (21 projects)

  • Every d001 project now has: README.adoc + assembler at root, partials/ for content, archive/ for old iterations

  • 21/21 projects clean — zero loose files at root

  • 17 new assemblers created, all encrypted, all buildable via d001 build <slug> pdf

  • SIEM project cleanup: 14 loose files moved to partials/ or archive/

  • Inbox partials created for SIEM + Abnormal — dump commands first, organize later

M365 Message Trace Investigation (PowerShell)

  • 10-step Exchange Online message trace investigation using Get-MessageTraceV2

  • V1: raw commands with real Red Canary trace output (MessageTraceId, transport rules, FromIP)

  • V2: hardened with null checks, deterministic sort, throw on failure, fallback logic

  • Graduated from SIEM inbox to abnormal-security/partials/message-trace-investigation.adoc

  • Transport rule gotcha documented: Event is "Transport rule Prepend subject" not "TransportRule"

  • Authentication validation checklist: SPF/DKIM/DMARC + AuthAs cross-reference to Ghost-Sender

  • FromIP 216.71.144.209 identified as Cisco ESA — matches Ghost-Sender exception list

Graphviz Study — Network Diagram Programming

  • Created data/d000/education/graphviz-study/ with partials architecture + assembler

  • 4 lessons: fundamentals, port anchoring (record vs HTML TABLE), clusters/tiers/overlays, edge styles

  • 2 production diagrams: firewall-zones (VyOS zone policy), domus-topology (full infrastructure with port-anchored 3560CX)

  • HTML TABLE port pattern: <TD PORT="g5"> with color-coded cells — the Visio replacement

  • Edge style vocabulary: solid=trunk, dashed=wireless, dotted=control plane, bold=port-channel, red=denied

  • Catppuccin-free white background version for professional sharing

BMS PDF Inventory & Diagram Tool Evaluation

  • Cataloged all 9 BMS Controller Segmentation PDFs — 4 primary docs, 5 LaTeX diagrams

  • Identified 4x duplication across Principia, Doctrina, Sapientia, Aethelred-Codex

  • Built Gateway Building topology slice in 5 tools for comparison: D2, Graphviz, Mermaid, PlantUML, matplotlib

  • All outputs in /tmp/bms-diagram-compare/ — D2 recommended for Antora/Kroki integration

  • find CLI corrections documented: explicit path, predicate ordering (cheap before expensive), no unnecessary globs

  • Linguistic analysis of find grammar written in English and Spanish (syntax, morphemes, grammar)

  • Formal correspondences section: Curry-Howard, Chomsky Hierarchy, Godel Numbering, Church-Turing, Category Theory

  • Cantor’s diagonal argument, cardinal numbers, density of rationals, Robinson’s infinitesimals (hyperreals)

  • Neovim learning goal captured: beyond :%s/old/new/g into text objects, dot repeat, macros, registers, :g, :argdo

  • Full report: Principia/02_Assets/PRJ-ISE-CHLA/profiling/BMS-Controller-Segmentation/BMS-PDF-Inventory.adoc

BMS Diagram Toolchain — Full Build (d001)

  • Upgraded from Gateway-only to full campus topology across all 5 tools

  • Graphviz v2 campus: all 8 buildings, HTML TABLE switches with port cells, record nodes with hostname/IP/MAC, port-labeled edges, full legend — selected as primary tool (premio)

  • 3 new Graphviz diagrams replacing legacy TikZ/Mermaid originals:

    • protocol-communication.dot — BACnet/IP, Metasys N2, Fox, MS/TP, RADIUS, pxGrid, DNS/DHCP/NTP

    • dacl-enforcement.dot — 28-ACE dACL flow: permitted (BACnet, DHCP, DNS, NTP, HTTP/S, ICMP) vs denied (internet, clinical, SSH, telnet)

    • device-onboarding.dot — full workflow: vendor MAC submission → 802.1X timeout → MAB → ISE profiling → match/no-match paths → quarantine/approve/block

  • Replaced 4 broken Antora include::example$ Mermaid references with rendered Graphviz PNGs

  • PlantUML ISE auth sequence: 7-phase MAB flow (onboarding, 802.1X, RADIUS, profiling, Access-Accept, DHCP, reauth)

  • D2, Mermaid, matplotlib campus versions built alongside — no tool menospreciado

  • cli-grammar partial: parsing build-adoc, find, dot as sentences (verb, direct object, complement, preposition)

  • Assembler: 20 includes, clean build at 6.8M with 8 embedded diagrams

  • build-adoc command parsed linguistically: pdf=complement (not noun), --theme=preposition (not adjective)

  • Cervantes discussion: find grammar → linguistic analysis → formal correspondences (Curry-Howard, Chomsky, Godel, Cantor) → Quixote as distributed self → the Captive’s brother who went to las Indias

Portable nvim Config (dots-quantum)

  • nvim-portable/init.lua — 160 lines, zero plugins, zero network, zero git

  • All domus-nvim muscle memory: space-leader, jk escape, half-page centering, visual line move, paste-without-yank, search-replace, window nav, buffer cycling

  • Autocmds: yank highlight, auto-resize, whitespace strip, return to last position

  • Built-in statusline, habamax colorscheme

  • Usage on any server: nvim -u <(cat ~/path/to/init.lua) — nothing written to disk

Process substitution for server config

  • nvim -u <(cat file) feeds config via file descriptor — nothing on the server’s filesystem

  • Alternative: scp once to /tmp/init.lua, use with nvim -u /tmp/init.lua

sed line-targeted replacement

  • grep -n to find line number → awk 'NR==1760' to verify → sed -i '1760s/old/new/' to change → grep -n to verify

  • Without the line number prefix, sed is a shotgun — hits every match. The adoc-pdfbuild-adoc rename incident.

build-adoc alias

  • alias build-adoc='$HOME/atelier/_bibliotheca/domus-asciidoc-build/bin/build-adoc.sh'

  • Added to dots-quantum .zshrc, stow already symlinked — source ~/.zshrc to activate


Personal

In Progress

Project Description Status Notes

k3s Platform

Production k3s cluster on kvm-01

Active

Prometheus, Grafana, Wazuh deployed

Wazuh Archives

Enable archives indexing in Filebeat

Active

PVC fix pending

kvm-02 Hardware

Supermicro B deployment

Active

Hardware ready, RAM upgrade done

Planned

Project Description Target Blocked By

Vault HA (3-node)

vault-02, vault-03 on kvm-02

Q2 2026 (slipped from Q1)

kvm-02 deployment

k3s HA (3-node)

Control plane HA

Q2 2026 (slipped from Q1)

kvm-02 deployment

ArgoCD GitOps

k3s GitOps deployment

After k3s stable

 — 

MinIO S3

Object storage for k3s

After ArgoCD

 — 

Domus Inventory

Personal asset management (YAML + CLI + AsciiDoc)

Q2 2026

Schema approved

Active — Infrastructure

Task Details Priority Status Due

Wazuh agent deployment

Deploy agents to all infrastructure hosts

P2

Pending

After archives fix

k3s Platform

Production k3s cluster on kvm-01

P1

In Progress

 — 

Wazuh Archives

Enable archives indexing in Filebeat, PVC fix

P1

In Progress

 — 

kvm-02 Hardware

Supermicro B deployment, RAM upgrade done

P1

In Progress

 — 


Active — Security & Encryption

Task Details Priority Status Due

Configure 4th YubiKey

SSH FIDO2 keys

P1

TODO

 — 

Cold storage M-DISC backup

age-encrypted archives

P1

TODO

After YubiKey setup


Active — Development & Tools

Task Details Priority Status Due

netapi Commercialization

Go CLI rewrite with Cobra-style argument discovery, package for distribution

P0

Active

 — 

Ollama API Service

FastAPI (17 endpoints), productize — config audit, doc tools, runbook gen

P0

Active

 — 

Shell functions (fe, fec, fef)

File hunting helpers

P3

TODO

 — 


Active — Documentation

Task Details Priority Status Due

D2 Catppuccin Mocha styling

domus-* spoke repos (177 files total)

P3

In Progress

 — 


Active — Financial

Task Details Priority Status Due

Amazon order history import

Download CSV from Privacy Central → parse with awk → populate subscriptions tracker

P1

Waiting

Pending Amazon data export (requested 2026-04-04)


Active — Education

Task Details Priority Status Due

No active education tasks — see education trackers


Active — Personal & Life Admin

Task Details Priority Status Due

ThinkPad T16g Setup

Arch install, stow dotfiles, Ollama stack, netapi dev env

P0

Pending

 — 

P50 Arch to Ubuntu migration

CR-2026-03-12

P2

In Progress

 — 

X1 Carbon Ubuntu installs

2 laptops, LUKS encryption

P2

In Progress

 — 

P50 Steam Test

Test Flatpak Steam + apt cleanup of broken i386 packages

P3

Pending

 — 

Documentation Sites

Notes

Day-specific personal notes here.


Education

Claude Code Mastery

Resource Details Progress Status

Claude Code Full Course (4 hrs)

Nick Saraev - YouTube comprehensive course

26:49 / 4:00:00

IN PROGRESS

Claude Code Certification

Anthropic official certification (newly released)

Not started

GOAL

Skills Mastery (Critical)

Certification Deadlines

  • CISSP - July 12, 2026 (10-week plan active — Week 1)

  • RHCSA 9 - Q3 2026 (after CISSP)

  • LPIC-1 - Renewal required (blocks LPIC-2)

Spanish C1 Certification Goals

Certification Provider Target Status Strategy

SIELE C1

Instituto Cervantes / UNAM / Salamanca

Q2 2026

ACTIVE

Computer-based, faster results - take FIRST

DELE C1

Instituto Cervantes

Q3/Q4 2026

PLANNED

After SIELE success, harder exam

DELE C2

Instituto Cervantes

2027

FUTURE

Mastery level - requires extensive immersion

SIELE is computer-adaptive, results in 3 weeks. DELE is paper-based, results in 3-4 months. Do SIELE first to validate readiness.

Don Quijote Writing Practice - DELE C1/C2 Initiative

Method:

  1. Read chapter in original Spanish

  2. Write personal analysis/understanding en espanol

  3. AI review for grammar, vocabulary, register

  4. Build comprehensive understanding of literary elements

Today’s Study

  • Focus: CISSP (41 days to July 12 exam — schedule exam today 06-01), MSCHAPv2 migration wrap-up

  • Secondary: RHCSA curriculum, Spanish SIELE C1

  • CISSP — Security & Risk Management (continuing). Schedule exam this afternoon.

  • RHCSA — continue curriculum phase

  • Spanish — Don Quijote reading + analysis (DTLA study day)

  • MSCHAPv2 — migration window closed 05-30, review final report

Regex Training (CRITICAL)

  • Status: 52 days carried over (since 2026-03-16)

  • Priority: After PeopleSoft, before Quijote

  • Session: Character classes, word boundaries


Infrastructure

Documentation Sites

Site URL Status Actions Needed

Domus Digitalis

docs.domusdigitalis.dev

Active

Validate, harden, improve

Architectus

docs.architectus.dev

Active

Public portfolio site - maintain

HA Deployment Status

System Description Status Notes

VyOS HA

vyos-01 (kvm-01) + vyos-02 (kvm-02) with VRRP VIP

✅ COMPLETE

2026-03-07 - pfSense decommissioned

BIND DNS HA

bind-01 (kvm-01) + bind-02 (kvm-02) with AXFR

✅ COMPLETE

Zone transfer operational

Vault HA

Raft cluster (vault-01/02/03)

✅ COMPLETE

Integrated with PKI

Keycloak Rebuild

keycloak-01 corrupted, rebuild from scratch

🔄 NEXT

Priority P3 - SSO broken

FreeIPA HA

ipa-02 replica planned

📋 PLANNED

Linux auth redundancy

AD DC HA

home-dc02 replication

📋 PLANNED

Windows auth redundancy

iPSK Manager HA

ipsk-mgr-02 with MySQL replication

📋 PLANNED

PSK portal redundancy

ISE HA

PAN HA (ise-01 reconfigure)

⏳ DEFERRED

Wait until ise-02 stable

ISE 3.5 Migration

Upgrade path: 3.2p9 → 3.4 (P1) → 3.5 (target)

📋 PLANNED

After 3.4 Migration completes (Q2 2026)

Single Points of Failure (CRITICAL)

These systems have NO redundancy - outage impacts production.
System Impact if Down Mitigation

ISE (ise-02)

All 802.1X stops - wired and wireless auth fails

ise-01 reconfiguration deferred until ise-02 stable

Keycloak (keycloak-01)

SAML/OIDC SSO broken (ISE admin, Grafana, etc.)

NEXT PRIORITY - Rebuild runbook

FreeIPA (ipa-01)

Linux auth, sudo rules, HBAC fails

ipa-02 replica planned

AD DC (home-dc01)

Windows auth, Kerberos, GPO fails

home-dc02 replica planned

iPSK Manager

Self-service PSK portal unavailable

ipsk-mgr-02 with MySQL replication planned

Validation Tasks

Task Details Status

docs.domusdigitalis.dev validation

Test all cross-references, search, rendering

TODO

docs.domusdigitalis.dev hardening

HTTPS, CSP headers, security review

TODO

docs.architectus.dev validation

Public site content review

TODO

Hub-spoke sync verification

All components building correctly

Ongoing


Quick Commands

Git & GitHub CLI

create GitHub repo from existing local repo
gh repo create <name> --private --source . --remote origin --push
clone a forked repo into a specific directory
gh repo clone EvanusModestus/PowerShell ~/atelier/_projects/work/PowerShell
gh repo clone defaults to SSH. If key is passphrase-protected, load agent first: eval "$(ssh-agent -s)" && ssh-add ~/.ssh/id_ed25519_github
cross-repo commit search — all domus repos on a specific date
for repo in ~/atelier/_bibliotheca/domus-*/ ~/atelier/_projects/personal/domus-*/; do
  [ -d "$repo/.git" ] || continue
  name=$(basename "$repo")
  git -C "$repo" log --since="2026-04-06" --until="2026-04-07" --format="%h %aI %s" 2>/dev/null |
    awk -v r="$name" '{print r, $0}'
done
commit history touching only today’s modified files
git log --oneline -- $(find . -name "*.adoc" -type f -newermt "$(date +%F)")
unstage a file without losing changes
git restore --staged data/d001/api/ise-dataconnect/output/output-2026-04-24

Safe — removes from staging area only. Working tree is untouched. Use when you accidentally git add a plaintext or output file.

gh CLI — repo discovery and filtering

list repos by name pattern (domus/antora ecosystem)
gh repo list --limit 100 --json name,description \
  | jq -r '.[] | select(.name | test("domus|antora|asciidoc"; "i")) | "\(.name)\t\(.description)"'
top 20 most recently updated repos
gh repo list --limit 100 --json name,description,updatedAt \
  | jq -r 'sort_by(.updatedAt) | reverse | .[:20] | .[] | "\(.updatedAt[:10])\t\(.name)\t\(.description)"'
top 10 repos by disk usage
gh repo list --limit 100 --json name,diskUsage \
  | jq -r '.[] | "\(.diskUsage)\t\(.name)"' | sort -rn | head -10
clone a repo that’s not local yet
gh repo clone EvanusModestus/<repo-name> ~/atelier/_bibliotheca/<repo-name>

find & grep

files modified since midnight today (precise — not "last 24 hours")
find . -name "*.adoc" -type f -newermt "$(date +%F)" | sort
-mtime 0 means "last 24 hours", not "today". -newermt "$(date +%F)" compares against midnight — exact.
case-insensitive file search
find . -iname "*mschap*" -type f | sort
multiple name patterns with -o
find . -type f \( -iname "*ise*" -o -iname "*mschap*" \) | sort
same thing, single regex — fewer parens, extensible
find . -type f -iregex '.*\(ise\|mschap\).*'
exclude directories
find . -type f -iname "*meeting*" \
  -not -path "*/node_modules/*" \
  -not -path "*/.git/*" \
  -not -path "*/build/*"
recent drafts by modification time (newest first)
find .drafts -type f -printf '%T@ %Tc %p\n' | sort -rn | awk '{$1="";print}' | head -3
grep — know what you’re counting
grep -rl "pattern" . --include="*.adoc"         # file count (which files)
grep -rn "pattern" . --include="*.adoc"         # line matches (every occurrence)
grep -rc "pattern" . --include="*.adoc" | grep -v ':0$'  # match count per file
search with context — avoid opening the file
grep -rn -E 'git init|gh repo create' docs/ --include='*.adoc' -B2 -A2

Search codex by content — which files contain a command?

find all PowerShell files that use a specific cmdlet
find docs/modules/ROOT/examples/codex/powershell -type f -name "*.adoc" \
  -exec grep -l 'Get-Process\|Start-Process\|pipeline\|Where-Object' {} \;

Pattern: find -exec grep -l returns only filenames with matches — like grep -rl but with find’s `-type f -name filtering. Use \| for OR in grep basic regex. Swap the pattern for any cmdlet or keyword to locate coverage across the codex.

inventory a codex tool directory — count files per tier
find docs/modules/ROOT -name "powershell" -type d \
  -exec sh -c 'echo "$1: $(find "$1" -type f | wc -l) files"' _ {} \;
find orphaned examples (not included by any page)
for f in $(find docs/modules/ROOT/examples/codex/powershell -name "*.adoc" -type f); do
  base=$(basename "$f")
  dir_parent=$(basename $(dirname "$f"))
  grep -rq "$dir_parent/$base" docs/modules/ROOT/pages/codex/powershell/ \
    docs/modules/ROOT/examples/codex/powershell/*.adoc 2>/dev/null \
    || echo "ORPHAN: $f"
done

find → grep → open in nvim

find by path + content, open result in nvim
nvim $(find -path '*oauth*' -name '*.adoc' -type f \
  -exec grep -l 'timeout\|expire\|reconfig\|token' {} \;)

Command substitution $(…​) feeds all matches as arguments to nvim — opens every hit as a buffer. :bn/:bp to cycle, :ls to list. One file? Opens directly. Five files? All loaded, ready to navigate.

find by content across entire tree, open in nvim
nvim $(find docs/modules/ROOT -name '*.adoc' -type f \
  -exec grep -l 'token.*expire\|oauth.*refresh' {} \;)
open one at a time (sequential — -exec nvim per match)
find -path '*oauth*' -name '*.adoc' -type f \
  -exec grep -l 'timeout\|expire' {} \; \
  -exec nvim {} \;
Trailing \| in grep patterns matches empty string — every file matches. Always end with a term, not a pipe: 'timeout\|expire\|token' not 'timeout\|expire\|token\|'.

Trace Antora partial inclusion chains

who includes this partial? (one level up)
grep -rl 'commands/shell' docs/modules/ROOT/partials/
count all pages that include a partial
grep -rl 'quick-commands' docs/modules/ROOT | wc -l
full chain: partial → assembler → every page that uses it
file="commands/shell"
grep -rl "$file" docs/modules/ROOT/partials/ | while read f; do
  parent=$(basename "$f" .adoc)
  echo "$file -> $parent"
  grep -rl "$parent" docs/modules/ROOT/pages/ | while read p; do
    echo "  -> $(basename "$p")"
  done
done

Pattern: grep -rl finds which files contain the string. Chain two passes — first finds the assembler partial, second finds every page that includes it. Works for any partial in the Antora include hierarchy.

Multi-pattern file search — worklog partial discovery

brute force — one find per partial name
find docs/modules/ROOT -name "*urgent.adoc*" -type f
find docs/modules/ROOT -name "*morning.adoc*" -type f
consolidated — single find with regex (production approach)
find docs/modules/ROOT -type f -regextype posix-extended \
  -regex '.*(urgent|morning|work-chla|personal|education|infrastructure|quick-commands|related)\.adoc' \
  | sort

Pattern: -regextype posix-extended enables | alternation without escaping. One process, one sort — versus 8 separate finds. The sort deduplicates visually and groups by path.

pipeline alternative — find piped to grep
find docs/modules/ROOT -type f -name "*.adoc" \
  | grep -E 'urgent|morning|work-chla|personal|education|infrastructure|quick-commands|related'

Trade-off: the pipeline version is more readable but spawns two processes. The regex version is a single find — faster on large trees, same result.

Cross-repo literary term search — bibliotheca-wide discovery

When searching for a term across the entire _bibliotheca (multiple repos, mixed file types), these patterns escalate from narrow to broad.

1. Single repo — count matches per file
grep -rn --include='*.adoc' -c 'sanchuelo' . | grep -v ':0$'
2. Cross-repo — filenames only (all bibliotheca)
grep -rl --include='*.adoc' -i 'sanchuelo' ~/atelier/_bibliotheca/ | sort
3. Cross-repo with context — see the line in situ
grep -rn --include='*.adoc' -i -B1 -A1 'sanchuelo' ~/atelier/_bibliotheca/domus-captures/
4. Multi-filetype — .adoc + .txt (catches source texts)
grep -rl -i 'sanchuelo' ~/atelier/_bibliotheca/ --include='*.txt' --include='*.adoc' | sort
5. Null-safe find + xargs — handles spaces in paths
find ~/atelier/_bibliotheca/ -type f \( -name '*.adoc' -o -name '*.txt' \) -print0 \
  | xargs -0 grep -li 'sanchuelo' | sort
6. Open all hits directly in nvim
grep -rl -i 'sanchuelo' ~/atelier/_bibliotheca/ --include='*.adoc' --include='*.txt' | xargs nvim

Pattern escalation: #1 confirms the term exists and where. #2 expands to all repos. #3 shows context without opening files. #4 adds plain text sources (Quijote .txt originals). #5 is the safe version for automation. #6 opens everything for editing.

Trade-off: grep -r --include is faster for known file types. find | xargs grep is safer for paths with spaces and more extensible (add -name '*.md' etc.). For literary searches across the bibliotheca, #4 or #5 is usually the right starting point — the source texts are .txt, not .adoc.

Email thread analysis — extract people, dates, commitments, silence

who’s in the thread (@ mentions + From headers)
grep -P '(@\w+|^From:.*<)' comms.adoc
timeline — every date with context
grep -nP '\d{1,2}/\d{1,2}/\d{2,4}|20\d{2}-\d{2}-\d{2}' comms.adoc
commitments — who promised what
grep -niP '(I can |I will |I.ll |we will |we.ll )' comms.adoc
open questions and unknowns
grep -niP '(\?|need to confirm|need to validate|TBD|pending)' comms.adoc

comm — set difference (who hasn’t replied)

# All recipients
grep -oP '<\K[^>]+' comms.adoc | sort -u > /tmp/all-recipients

# All senders
grep -P '^From:' comms.adoc | grep -oP '<\K[^>]+' | sort -u > /tmp/replied

# Who's silent — follow-up targets
comm -23 /tmp/all-recipients /tmp/replied

comm -23 outputs lines only in file 1 (recipients not in senders). Requires sorted input. grep -oP '<\K[^>]+' uses PCRE lookbehind — match < but don’t include it, capture until >.

Sort find results by modification time (newest first)

find discovers files but has no sort. Chain -printf with sort to order by mtime.

awk '{print $2}' truncates filenames with spaces — Familia Romana_ Lingva…​ becomes Familia. Always use the null-safe or sub() variants below for real data.
epoch sort — space-safe (production version)
# Sort by mtime, strip epoch prefix — handles spaces in filenames
find ~/Downloads -maxdepth 1 -name '*.pdf' -printf '%T@ %p\n' | sort -rn | awk '{sub(/^[^ ]+ /,""); print}'

sub(/[ ]+ /,"") removes everything up to and including the first space (the epoch). {print $2} would split on every space — fatal for Familia Romana_ Lingva Latina.

human-readable timestamps alongside
# ISO 8601 timestamps — readable and lexicographically sortable
find ~/Downloads -maxdepth 1 -name '*.pdf' -printf '%T+ %p\n' | sort -r | head -20

%T+ renders YYYY-MM-DD+HH:MM:SS — no epoch math needed, still sorts correctly as text.

null-safe — the bulletproof version
# Null-delimited: survives any filename (newlines, quotes, unicode)
find ~/Downloads -maxdepth 1 -name '*.pdf' -printf '%T@\t%p\0' | sort -zrn | awk -v RS='\0' -F'\t' '{print $2}'

-printf '%T@\t%p\0' — tab separates epoch from path, null terminates. sort -z sorts null-delimited records. awk -v RS='\0' -F'\t' reads null-terminated, splits on tab — $2 is now the full path regardless of spaces.

stat fallback — portable (BSD/macOS)
# GNU stat equivalent — works where -printf is unavailable
find ~/Downloads -maxdepth 1 -name '*latin*' -exec stat --format='%Y %n' {} + | sort -rn | awk '{sub(/^[^ ]+ /,""); print}'

-exec …​ {} + batches all files into one stat call (faster than \;). On macOS, use stat -f '%m %N' instead of --format='%Y %n'.

File intelligence — size, type, duplicates, age

Beyond finding files — interrogating them.

top 10 largest files in a directory tree
# Size in bytes (-printf %s), human-readable via numfmt
find ~/Downloads -type f -printf '%s\t%p\n' | sort -rn | head -10 | numfmt --to=iec --field=1

numfmt --to=iec --field=1 converts the first field from bytes to K/M/G. sort -rn on raw bytes is exact — ls -lhS rounds and sometimes mis-sorts.

find duplicates by size (fast pre-filter before checksumming)
# Files sharing a byte count — likely duplicates (confirm with md5sum)
find ~/Downloads -type f -printf '%s %p\n' | awk '{seen[$1]++; files[$1]=files[$1] "\n  " $0} END {for (s in seen) if (seen[s]>1) print files[s]}'
find duplicates by content — definitive
# md5sum only files with duplicate sizes (two-pass: fast then precise)
find ~/Downloads -type f -printf '%s\n' | sort | uniq -d | while read -r size; do
  find ~/Downloads -type f -size "${size}c" -exec md5sum {} +
done | sort | uniq -w32 -D

Two-pass: first find duplicate sizes (cheap), then md5sum only those (expensive). uniq -w32 -D compares first 32 chars (the hash) and prints all duplicates.

file type census — what’s actually in this directory?
# Count files by MIME type (not extension — extensions lie)
find ~/Downloads -type f -exec file --mime-type -b {} + | sort | uniq -c | sort -rn

file --mime-type -b reports actual content type. -b suppresses filename. A .pdf that’s really text/html is a failed download.

stale files — untouched for 30+ days
# Files not accessed in 30 days — candidates for cleanup
find ~/Downloads -maxdepth 1 -type f -atime +30 -printf '%A+ %s\t%p\n' | sort | numfmt --to=iec --field=2

-atime 30` = access time older than 30 days. `-printf '%A' shows last access. Useful for Downloads cleanup without deleting something you just renamed.

disk usage by subdirectory — sorted
# Which subdirectories consume the most space?
find . -maxdepth 1 -type d -exec du -sh {} + 2>/dev/null | sort -rh | head -20

Batch operations — rename, move, transform

rename all files — strip spaces, lowercase, normalize unicode
# Dry run — show what would change (remove echo to execute)
find ~/Downloads -maxdepth 1 -type f -name '* *' -print0 | while IFS= read -r -d '' f; do
  dir=$(dirname "$f")
  base=$(basename "$f" | tr ' ' '-' | tr '[:upper:]' '[:lower:]')
  echo mv "$f" "$dir/$base"
done

IFS= read -r -d '' — the holy trinity for null-safe filename reading. IFS= prevents whitespace trimming. -r prevents backslash interpretation. -d '' reads until null.

move files by extension into categorized subdirectories
# Sort Downloads chaos into folders by type
find ~/Downloads -maxdepth 1 -type f -print0 | while IFS= read -r -d '' f; do
  ext="${f##*.}"
  case "$ext" in
    pdf|epub)    dest="books" ;;
    jpg|png|svg) dest="images" ;;
    sh|py|rb)    dest="scripts" ;;
    *)           dest="other" ;;
  esac
  mkdir -p ~/Downloads/"$dest"
  echo mv "$f" ~/Downloads/"$dest"/
done

${f##.} — parameter expansion: strip longest match of . from front, leaving only the extension. No basename or awk needed.

batch convert epub → asciidoc (like your Cicero fetch script)
# Convert all epubs in a directory to asciidoc via pandoc
find . -name '*.epub' -type f -exec sh -c '
  for epub; do
    adoc="${epub%.epub}.adoc"
    pandoc -f epub -t asciidoc "$epub" -o "$adoc" \
      && printf "  → %s (%s lines)\n" "$adoc" "$(wc -l < "$adoc")" \
      || printf "  ✗ failed: %s\n" "$epub"
  done
' _ {} +

-exec sh -c '…​' _ {} + — batch mode. _ fills $0 (script name, discarded). All matched files become $1, $2, …​ iterated by for epub. One sh invocation, not one per file.

xargs power patterns

parallel processing — 4 cores
# Checksum all PDFs in parallel (4 processes)
find ~/Downloads -name '*.pdf' -print0 | xargs -0 -P4 md5sum

-P4 runs 4 md5sum processes simultaneously. -print0 | xargs -0 is the null-safe pipeline — no filename can break it.

batched execution — two arguments at a time
# Compare files pairwise with diff
find . -name '*.adoc' -print0 | xargs -0 -n2 diff --brief

-n2 feeds two arguments per invocation. Useful for pairwise comparisons, copy operations (-n2 with cp), or any command taking exactly two args.

placeholder — insert filename at specific position
# Backup every config file: cp <file> <file>.bak
find /etc -maxdepth 1 -name '*.conf' -print0 | xargs -0 -I{} cp {} {}.bak

-I{} replaces {} with each filename. Slower than + batching (one cp per file) but necessary when the filename must appear in a specific position.

Process substitution — diff without temp files

compare two directory listings
# What files exist in study-A but not study-B?
diff <(find data/d000/education/ciceron-study -type f -name '*.adoc' | sort) \
     <(find data/d000/education/latin-study -type f -name '*.adoc' | sort)

<(cmd) creates a file descriptor from command output. diff sees two "files" — no temp files created, no cleanup needed.

compare file counts across directories
# Side-by-side: file type census of two directories
paste <(find dir1 -type f -exec file --mime-type -b {} + | sort | uniq -c | sort -rn) \
      <(find dir2 -type f -exec file --mime-type -b {} + | sort | uniq -c | sort -rn)

awk, sed, jq

awk — field extraction

print second field (whitespace-delimited)
awk '{print $2}' file.txt
custom delimiter — colon-separated (like /etc/passwd)
awk -F: '{print $1, $3}' /etc/passwd
extract JSON code blocks from AsciiDoc
awk '/\[source,json\]/{getline; if ($0 ~ /^----/) {p=1; next}} p && /^----/{p=0; next} p' file.adoc
field extraction with printf formatting
awk '{printf "%-30s %s\n", $1, $2}' file.txt

sed — stream editing

in-place replacement with verify-before/after
# Before
awk 'NR==73' /etc/ssh/sshd_config
# Change
sed -i '73s/#GSSAPIAuthentication no/GSSAPIAuthentication yes/' /etc/ssh/sshd_config
# After
awk 'NR==73' /etc/ssh/sshd_config
extract line range
sed -n '10,20p' file.txt

sed — line-targeted replacement (verify-before / change / verify-after)

the full pattern: locate → validate → change → verify
# 1. LOCATE: find the line number
grep -n 'adoc-pdf' zsh/.zshrc

# 2. VALIDATE: read the exact line before changing
awk 'NR==1760' zsh/.zshrc

# 3. CHANGE: target by line number — only hits that line
sed -i '1760s/alias adoc-pdf=/alias build-adoc=/' zsh/.zshrc

# 4. VERIFY: confirm change AND check for collateral
grep -n 'build-adoc\|adoc-pdf' zsh/.zshrc

Without the line number prefix (1760s/), sed replaces every match in the file — a shotgun. With it, surgical. The line number comes from grep -n.

multi-line verify — check two specific lines at once
awk 'NR==1218 || NR==1760' zsh/.zshrc
range extraction — NR for surgical reads from large files
# grep found the error at line 44164 — read 50 lines of context
awk 'NR>=44160 && NR<=44210' session-dump.adoc

No head | tail chains. No sed -n '44160,44210p'. One awk, two numbers.

grep -oP with \K — value extraction from key-value logs

extract just the value after a key (Perl regex)
# ISE syslog — extract failure reasons
grep -oP 'FailureReason=\K[^,;]+' /var/log/syslog | sort | uniq -c | sort -rn

# ISE — extract MAC addresses
grep -oP 'Calling-Station-ID=\K[0-9A-Fa-f:.-]+' /var/log/syslog | sort -u

# ISE — extract NAS IPs
grep -oP 'NAS-IP-Address=\K[0-9.]+' /var/log/syslog | sort -u

# ISE — extract device names
grep -oP 'NetworkDeviceName=\K[^,;]+' /var/log/syslog | sort -u

\K resets the match start — everything before \K is required context but excluded from output. [^,;]+ captures until the next delimiter. Pipe to sort -u for unique, sort | uniq -c | sort -rn for counted frequency.

pattern: grep -oP 'KEY=\KVALUE_REGEX' | sort pipeline
# Generic form — works for any key=value log format
grep -oP 'FIELD_NAME=\K[^,;]+' logfile | sort | uniq -c | sort -rn | head -20

jq — JSON processing

extract nested fields
curl -s localhost:8080/stats | jq '.stats.total_files'
filter array by property
jq '.results[] | select(.category == "standards")' response.json
transform to TSV for spreadsheets
jq -r '.[] | [.title, .path] | @tsv' response.json | column -t -s $'\t'
GitHub API + jq — commit history by path
gh api "repos/EvanusModestus/domus-captures/commits?path=docs/&per_page=10" |
  jq -r '.[] | "\(.commit.author.date[:10]) \(.sha[:7]) \(.commit.message | split("\n")[0])"'

Shell Patterns

xargs — when the next command reads arguments, not stdin

Next command reads…​ Use

stdin (awk, grep, wc, sort)

pipe directly

arguments (stat, rm, cp, nvim, git add)

xargs

copy today’s files to backup — -I{} placeholder
mkdir -p /tmp/adoc-backup-$(date +%F) && \
  find . -name "*.adoc" -type f -newermt "$(date +%F)" | \
  xargs -I{} cp {} /tmp/adoc-backup-$(date +%F)/
parallel validation — -P4 runs 4 at a time
find .drafts -name "*.adoc" -type f | xargs -P4 -I{} asciidoctor -o /dev/null {}
null-delimited pipeline — safe for filenames with spaces
find . -name "*.adoc" -type f -print0 | xargs -0 wc -l

Process substitution — <(cmd) treats output as a file

compare tracker state: yesterday vs today
diff <(grep '|' partials/trackers/work/adhoc/carryover.adoc | head -20) \
     <(git show HEAD~1:partials/trackers/work/adhoc/carryover.adoc | grep '|' | head -20)
files on disk vs files in nav — drift detection
diff <(find docs/modules/ROOT/pages/projects/chla/mschapv2-migration -name "*.adoc" -type f | sort) \
     <(grep -oP 'mschapv2-migration/[^[]+\.adoc' docs/modules/ROOT/nav.adoc | sort)

Command substitution — embed output as arguments

open most recently modified file in nvim
nvim "$(find data/ -name '*.adoc' -type f -printf '%T@ %p\n' | sort -rn | awk 'NR==1{print $2}')"
line count across a project
wc -l $(find docs/modules/ROOT -path '*mschapv2*' -name '*.adoc' -type f)

Conditional execution — capture, test, act

open matching files only if they exist
files=$(find .drafts -name 'in*' -type f) && [ -n "$files" ] && nvim $files
open files that contain unchecked items
files=$(grep -rl '\[ \]' .drafts/*.adoc) && [ -n "$files" ] && nvim $files
guard with grep -q — only act if pattern matches
grep -q 'TODO\|FIXME\|\[ \]' "$file" && nvim "$file"

Pattern: $(capture)[ -n ] tests non-empty → && only proceeds if true. grep -q is the idempotent guard — run repeatedly, only opens when there’s work.

Decrypt and open — find .age, decrypt, nvim in one shot

files=$(find . -name "*tcp-clock*.age" -type f) && \
  [ -n "$files" ] && echo "$files" | xargs -I{} decrypt-file {} && \
  nvim $(echo "$files" | sed 's/\.age$//')

Pattern: find .age only (never tries plaintext), sed derives the decrypted path, guard prevents empty nvim. Change the glob to match any project.

tee_clean — color on screen, clean text in file

tee_clean() {
  tee >(sed 's/\x1b\[[0-9;]*m//g' > "$1")
}

# Color output on terminal, stripped in file
jq -C '.' data.json | tee_clean output.json
xq -C '.' data.xml | tee_clean output.json

# Wrap a whole block
{
  echo "=== Summary ==="
  jq -C '.[] | .name' data.json
} | tee_clean summary.txt

The >(cmd) is process substitution — tee writes to stdout AND to the subshell pipe. sed strips ANSI escape sequences (\x1b\[[0-9;]*m) before they hit the file.

Dependency check — verify toolchain in one shot

for cmd in asciidoctor asciidoctor-pdf pandoc rouge d2 mmdc age; do
  printf "%-20s %s\n" "$cmd" "$(command -v $cmd >/dev/null 2>&1 && echo 'OK' || echo 'MISSING')"
done

Pattern: command -v checks if binary exists on PATH. >/dev/null 2>&1 suppresses output — we only care about exit code. Swap the tool list for any project’s dependencies.

printf safety — dashes as data, not options

wrong — printf treats --- as invalid option
printf '---\n\n'
right — %s format string treats --- as data
printf '%s\n\n' '---'

Kill stuck SSH sessions

Find established SSH connections
lsof -i TCP -n -P | awk '/ssh.*ESTABLISHED/ {print $2, $9}'
Kill all stuck SSH sessions to a specific host
lsof -i TCP -n -P | awk '/ssh.*kvm-01.*ESTABLISHED/ {print $2}' | sort -u | xargs kill
Kill ALL stuck SSH sessions
lsof -i TCP -n -P | awk '/ssh.*ESTABLISHED/ {print $2}' | sort -u | xargs kill

lsof -i TCP -n -P lists all TCP connections. awk filters for SSH + ESTABLISHED, prints only the PID ($2). sort -u deduplicates (multiple file descriptors per process). xargs kill sends SIGTERM to each.

File Descriptors & Redirection

The three file descriptors

FD Name Purpose

0

stdin

input to the command

1

stdout

normal output (valid results)

2

stderr

error messages

Split stdout and stderr into separate files

find / -name "*.conf" 1>results.txt 2>errors.txt

Suppress errors — 2>/dev/null

find / -name "*.conf" 2>/dev/null

Merge stderr into stdout — 2>&1

command 2>&1 | grep "pattern"

This sends both stdout and stderr through the pipe. Without 2>&1, only stdout reaches grep — errors print to the terminal and bypass the pipeline.

Heredoc patterns

multi-line input to a command
cat <<'EOF'
Line 1
Line 2
EOF
heredoc commit messages (quotes prevent variable expansion)
git commit -m "$(cat <<'EOF'
feat: add new feature

Multi-line description here.
EOF
)"

API & curl/jq

domus-api — Documentation System REST API

start the API server
cd ~/atelier/_projects/personal/domus-api && uv run uvicorn domus_api.main:app --host 0.0.0.0 --port 8080
health check
curl -s localhost:8080/ | jq
full-text search
curl -s 'localhost:8080/search?q=mandiant' | jq
search — extract path, title, match count
curl -s 'localhost:8080/search?q=mandiant' | jq '.results[] | {path, title, match_count}'
list pages by category
curl -s 'localhost:8080/pages?category=standards' | jq
all antora.yml attributes
curl -s localhost:8080/attributes | jq

GitHub API

cross-repo search via GitHub API
gh search code "vault seal" --owner EvanusModestus --json repository,path,textMatches |
  jq '.[] | {repo: .repository.full_name, file: .path, match: .textMatches[].fragment}'
count .adoc files in a repo via API
gh api 'repos/EvanusModestus/domus-captures/git/trees/main?recursive=1' |
  jq '[.tree[] | select(.path | endswith(".adoc"))] | length'

Domus Workflows

Read content from terminal (meeting-ready)

today’s worklog
bat docs/modules/ROOT/pages/2026/04/WRKLOG-$(date +%Y-%m-%d).adoc
current priorities
bat docs/modules/ROOT/partials/trackers/work/priorities/current.adoc
carryover backlog
bat docs/modules/ROOT/partials/trackers/work/adhoc/carryover.adoc
any project summary
bat docs/modules/ROOT/partials/projects/mandiant-remediation/summary.adoc

Search and discovery

find all files related to a topic
grep -rl "MSCHAPv2" docs/modules/ROOT/ --include="*.adoc" | sort
search codex entries
grep -rn "pattern" docs/modules/ROOT/partials/codex/ --include="*.adoc" -B1 -A3
list all worklogs for a month
ls -1 docs/modules/ROOT/pages/2026/04/WRKLOG-*.adoc

Tracker aging — calculate days from origin

how many days since a carryover item started
echo $(( ($(date +%s) - $(date -d "2026-03-09" +%s)) / 86400 ))

Encrypted data access (d001)

view encrypted file without disk write
age --decrypt -i ~/.secrets/.metadata/keys/master.age.key \
  data/d001/projects/mandiant-remediation/findings-status-2026-04-16.adoc.age \
  | bat --language asciidoc
project encryption dashboard
for d in data/d001/projects/*/; do
  total=$(find "$d" -type f | wc -l)
  plain=$(find "$d" -type f ! -name '*.age' ! -name 'README.adoc' ! -name '.gitkeep' ! -name '*.py' | wc -l)
  printf "%-25s %s files  %s plaintext\n" "$(basename "$d")" "$total" "$plain"
done

d000 study builds

batch build all docs for a Quijote chapter range
for d in p1-cap-03{7,8,9}; do
  for f in data/d000/education/quijote-study/notas/$d/*.adoc; do
    d000 build "$d/$(basename "$f" .adoc)" html --variant light-cyan
  done
done
build a single study doc (use unique path fragment)
d000 build p1-cap-038/texto-anotado html --variant light-cyan
d000 build p1-cap-038/texto-anotado pdf --theme light-cyan
batch build PDFs for a chapter range
for d in p1-cap-03{7,8,9}; do
  for f in data/d000/education/quijote-study/notas/$d/*.adoc; do
    d000 build "$d/$(basename "$f" .adoc)" pdf --theme light-cyan
  done
done
open all rendered chapters at once
firefox data/d000/education/quijote-study/notas/p1-cap-03{7,8,9}/output/*.html &
firefox data/d000/education/quijote-study/notas/p1-cap-03{7,8,9}/output/*.pdf &
print chapter PDFs (requires CUPS configured)
lp data/d000/education/quijote-study/notas/p1-cap-03{7,8,9}/output/*.pdf
build LPL logic study (English / Spanish)
d000 build annotated-text pdf --theme light-cyan
d000 build lpl-study/notas/texto-anotado pdf --theme light-cyan
build Cicero study
d000 build de-oratore/libro-i/texto-anotado html --variant light-cyan

Available themes

PDF themes (--theme)
ls ~/atelier/_bibliotheca/domus-asciidoc-build/themes/pdf/ | sed 's/-theme\.yml//'
# base blue burgundy catppuccin creative dark don-quijote green
# learning light-cyan navy operations orange purple reference royal
HTML variants (--variant)
~/atelier/_bibliotheca/domus-asciidoc-build/docinfo/compose.sh --list
# light dark catppuccin royal light-cyan

ISE & Network Ops

ISE ERS API — endpoint CRUD

set credentials (session)
export ISE_HOST="{ise-ip}" ISE_USER="admin" ISE_PASS="$(gopass show -o ise/admin)"
list identity groups
curl -sk "https://$ISE_HOST:{ise-ers-port}/ers/config/identitygroup" \
  -H "Accept: application/json" -u "$ISE_USER:$ISE_PASS" | jq '.SearchResult.resources[].name'
check if endpoint exists by MAC
curl -sk "https://$ISE_HOST:{ise-ers-port}/ers/config/endpoint?filter=mac.EQ.AA:BB:CC:DD:EE:FF" \
  -H "Accept: application/json" -u "$ISE_USER:$ISE_PASS" | jq '.SearchResult.total'

Certificate inspection

view EAP-TLS client cert from local store
openssl x509 -in {cert-dir}/client.pem -text -noout | head -30
check cert expiry
openssl x509 -in {cert-dir}/client.pem -enddate -noout

Network diagnostics

check listening ports
ss -tlnp | grep -E ':{port-https}|:{port-ssh}|:{port-ldaps}'
test ISE connectivity
nc -zv {ise-ip} {ise-ers-port}
DNS resolution
dig {ise-hostname} +short

ISE eval rotation — backup & restore

backup from ISE CLI (when admin UI is license-locked)
# SSH to ISE
ssh admin@ise-02.inside.domusdigitalis.dev

# Verify NAS repo
show repository nas-01

# Get encryption key (on workstation)
dsource d000 dev/storage
echo $ISE_BACKUP_KEY

# Run backup
backup pre-rotation-2026-06 repository nas-01 ise-config encryption-key plain <KEY>
list backups on NAS
ssh admin@ise-02.inside.domusdigitalis.dev
show repository nas-01
restore to fresh ISE node
configure terminal
repository nas-01
  url nfs://10.50.1.70:/volume1/ise_backups
exit

restore <backup-filename> repository nas-01 encryption-key plain <KEY>

VyOS — VRRP & VLAN inspection

VRRP status and VIPs
show vrrp
show configuration commands | grep vrrp | grep 'address'
firewall zone membership
show configuration commands | grep 'firewall zone' | grep 'member'
DHCP leases and ARP
show dhcp server leases
show arp
full interface/VLAN map
show interfaces

CUPS printing — validation & setup

software validation
command -v lpstat && echo "CUPS present" || echo "CUPS not installed"
lpstat -r                                # scheduler running?
lpstat -p -d                             # printers + default
daemon lifecycle
sudo systemctl enable --now cups         # start + persist
printer discovery
lpinfo -v                                # available backends/URIs
lpinfo -m | grep -i <brand>             # available drivers
add printer and set default
sudo lpadmin -p <name> -v <uri> -m everywhere -E
lpoptions -d <name>
print
lp file.pdf                              # default printer
lp -d <name> -o sides=two-sided-long-edge file.pdf

PowerShell (from zsh)

All PowerShell commands run inside pwsh -NoLogo -Command '…​' from zsh. Running them bare fails — zsh interprets $, |, () as shell syntax.

Process management

top 5 processes by memory
pwsh -NoLogo -Command 'Get-Process | Sort-Object WorkingSet64 -Descending |
  Select-Object -First 5 ProcessName, Id,
    @{N="MB";E={[math]::Round($_.WorkingSet64/1MB)}} | Format-Table'
stop/start Teams
pwsh -NoLogo -Command 'Get-Process | Where-Object {$_.ProcessName -like "*teams*"} | Stop-Process'
pwsh -NoLogo -Command 'Start-Process "ms-teams"'

Export to JSON (pipe to jq)

always use -NoLogo when piping pwsh output to zsh tools
pwsh -NoLogo -Command 'Get-Process | Sort-Object WorkingSet64 -Descending |
  Select-Object -First 5 ProcessName, Id,
    @{N="MB";E={[math]::Round($_.WorkingSet64/1MB)}} | ConvertTo-Json' | jq '.'
Never pipe Format-Table into ConvertTo-Json — it produces layout metadata, not data. Select-Object first, then ConvertTo-Json.

Wi-Fi management (netsh)

force fresh network scan
netsh wlan disconnect interface="Wi-Fi"
netsh wlan show networks mode=bssid
netsh wlan connect name="CHLA-Remote" interface="Wi-Fi"

SSH from PowerShell

connect to homelab from Windows terminal
ssh evan@modestus-razer.inside.domusdigitalis.dev

WSL ↔ Windows — Cross-Environment Commands

From zsh (WSL) — control Windows
run any PowerShell command from zsh
pwsh -NoLogo -Command 'Get-Date'
run multi-line PowerShell from zsh (heredoc)
pwsh -NoLogo -Command "$(cat <<'PS'
$procs = Get-Process | Where-Object { $_.WorkingSet64 -gt 100MB }
$procs | Sort-Object WorkingSet64 -Descending |
  Select-Object ProcessName, Id, @{N="MB";E={[math]::Round($_.WorkingSet64/1MB)}} |
  Format-Table -AutoSize
PS
)"
open a file in Windows from WSL
# Open in default Windows app
wslview /mnt/c/Users/erosado/Documents/report.pdf

# Open Explorer to current WSL directory
explorer.exe .

# Open specific Windows path
explorer.exe 'C:\Users\erosado\Downloads'
copy WSL output to Windows clipboard
# Pipe anything to Windows clipboard
cat file.txt | clip.exe

# Copy a command's output
pwsh -NoLogo -Command 'Get-TransportRule | Format-List Name, State' | clip.exe
access Windows files from WSL
# Windows C: drive is at /mnt/c
ls /mnt/c/Users/erosado/Downloads/

# Copy from Windows to WSL
cp /mnt/c/Users/erosado/Downloads/report.pdf ~/atelier/

# Watch a Windows directory for new files
find /mnt/c/Users/erosado/Downloads -maxdepth 1 -mmin -5 -type f -printf '%T+ %p\n' | sort -r
From PowerShell — control WSL
run a bash command from PowerShell
wsl -e bash -c 'grep -rn "Ghost-Sender" ~/atelier/_bibliotheca/domus-captures/docs/'
run a specific WSL command and capture output
$result = wsl -e bash -c 'git -C ~/atelier/_bibliotheca/domus-captures log --oneline -5'
$result
Process Management — Windows Side
top processes by memory — formatted table
pwsh -NoLogo -Command '
Get-Process | Sort-Object WorkingSet64 -Descending |
  Select-Object -First 20 ProcessName, Id,
    @{N="MB";E={[math]::Round($_.WorkingSet64/1MB)}},
    @{N="CPU(s)";E={[math]::Round($_.CPU,1)}},
    @{N="Handles";E={$_.HandleCount}} |
  Format-Table -AutoSize'
find a specific process
pwsh -NoLogo -Command 'Get-Process | Where-Object { $_.ProcessName -like "*teams*" } |
  Select-Object ProcessName, Id, @{N="MB";E={[math]::Round($_.WorkingSet64/1MB)}} |
  Format-Table -AutoSize'
kill by name
pwsh -NoLogo -Command 'Stop-Process -Name "Teams" -Force -ErrorAction SilentlyContinue'
kill by PID
pwsh -NoLogo -Command 'Stop-Process -Id 12345 -Force'
what’s listening on a port (Windows equivalent of ss -tulnp)
pwsh -NoLogo -Command 'Get-NetTCPConnection -State Listen |
  Select-Object LocalAddress, LocalPort, OwningProcess,
    @{N="Process";E={(Get-Process -Id $_.OwningProcess -ErrorAction SilentlyContinue).ProcessName}} |
  Sort-Object LocalPort | Format-Table -AutoSize'
specific port check
pwsh -NoLogo -Command 'Get-NetTCPConnection -LocalPort 8080 -ErrorAction SilentlyContinue |
  Select-Object LocalAddress, LocalPort, RemoteAddress, State,
    @{N="Process";E={(Get-Process -Id $_.OwningProcess).ProcessName}}'
Services — Windows Side
list running services
pwsh -NoLogo -Command 'Get-Service | Where-Object { $_.Status -eq "Running" } |
  Sort-Object DisplayName | Format-Table Name, DisplayName, Status -AutoSize'
check a specific service
pwsh -NoLogo -Command 'Get-Service -Name "WinRM" | Format-List Name, DisplayName, Status, StartType'
restart a service
Restart-Service -Name "WinRM" -Force
System Info — Quick Health from zsh
one-shot Windows system summary
pwsh -NoLogo -Command '
Write-Host "=== Windows System ===" -ForegroundColor Cyan
Write-Host "Hostname: $env:COMPUTERNAME"
Write-Host "User:     $env:USERNAME"
Write-Host "OS:       $((Get-CimInstance Win32_OperatingSystem).Caption)"
Write-Host "Uptime:   $((Get-Date) - (Get-CimInstance Win32_OperatingSystem).LastBootUpTime)"
Write-Host "RAM:      $([math]::Round((Get-CimInstance Win32_OperatingSystem).TotalVisibleMemorySize/1MB))GB total, $([math]::Round((Get-CimInstance Win32_OperatingSystem).FreePhysicalMemory/1MB))GB free"
Write-Host "CPU:      $((Get-CimInstance Win32_Processor).Name)"
Write-Host "Disk C:   $([math]::Round((Get-PSDrive C).Free/1GB))GB free of $([math]::Round(((Get-PSDrive C).Used + (Get-PSDrive C).Free)/1GB))GB"'
disk usage — all drives
pwsh -NoLogo -Command 'Get-PSDrive -PSProvider FileSystem |
  Select-Object Name, @{N="Used(GB)";E={[math]::Round($_.Used/1GB,1)}},
    @{N="Free(GB)";E={[math]::Round($_.Free/1GB,1)}},
    @{N="Total(GB)";E={[math]::Round(($_.Used+$_.Free)/1GB,1)}} |
  Format-Table -AutoSize'
Exchange Online — Connect from zsh
connect to Exchange Online (launches MFA prompt in Windows)
pwsh -NoLogo -Command 'Connect-ExchangeOnline -UserPrincipalName erosado@chla.usc.edu'
MFA prompt opens in the Windows browser. After auth, the session persists in the pwsh process. For multi-command sessions, start pwsh interactively instead of one-shot commands.
interactive PowerShell session from zsh (for Exchange, etc.)
pwsh -NoLogo
# Then inside pwsh:
# Connect-ExchangeOnline
# Get-TransportRule | Format-List Name, State
# exit
File Transfer Patterns
move files between WSL and Windows
# WSL → Windows Downloads
cp ~/atelier/_bibliotheca/domus-captures/output/report.pdf /mnt/c/Users/erosado/Downloads/

# Windows → WSL (glob)
cp /mnt/c/Users/erosado/Downloads/*.{png,pdf,jpg} ~/atelier/_staging/

# Bulk move with null safety
find /mnt/c/Users/erosado/Downloads -maxdepth 1 -name '*.pdf' -mmin -60 -print0 |
  xargs -0 -I{} cp {} ~/atelier/_staging/
watch Windows Downloads for new files (live)
inotifywait -m /mnt/c/Users/erosado/Downloads -e create -e moved_to |
  awk '{printf "%s  %s\n", strftime("%H:%M:%S"), $3}'
inotifywait requires inotify-tools. Install with sudo pacman -S inotify-tools if not present.

Security & Encryption

View encrypted files without writing to disk

pipe age decrypt to bat — nothing touches the filesystem
age --decrypt -i ~/.secrets/.metadata/keys/master.age.key \
  data/d001/projects/mandiant-remediation/findings-status-2026-04-16.adoc.age \
  | bat --language asciidoc --file-name "findings-status-2026-04-16.adoc"

Batch re-encrypt — brace expansion + loop

re-encrypt multiple project files
for f in data/d001/projects/mandiant-remediation/{findings-status,guest-acl-update,siem-report}-2026-04-16.adoc; do
  rm -f "${f}.age" && echo y | encrypt-file "$f"
done
Always rm -f the .age first. If you skip it, encrypt-file prompts about overwrite and may only delete the plaintext without re-encrypting.

Detect stale plaintext — files needing re-encryption

find plaintext newer than its .age counterpart
for f in data/d001/projects/*/*.adoc; do
  age="${f}.age"
  if [ -f "$f" ] && [ -f "$age" ]; then
    pt_mod=$(/usr/bin/stat -c'%Y' "$f")
    age_mod=$(/usr/bin/stat -c'%Y' "$age")
    [ "$pt_mod" -gt "$age_mod" ] && echo "STALE: $f"
  fi
done

Secure delete — shred for sensitive plaintext

shred -u data/d001/projects/mandiant-remediation/man-report.txt
On SSD/NVMe, shred is less effective (wear leveling), but better than rm which only removes the directory entry.

Pre-push audit — find all unencrypted project files

find data/d001/projects -type f ! -name '*.age' ! -name 'README.adoc' ! -name '.gitkeep' ! -name '*.py' | sort

System & Infrastructure

PipeWire audio validation

wpctl status                                    # PipeWire status
pactl list sinks short                          # list audio sinks
pw-play /usr/share/sounds/freedesktop/stereo/bell.oga  # test default sink
journalctl -b --grep='sof|cs35l56' --no-pager | tail -20  # kernel audio firmware
cat /proc/asound/cards                          # ALSA sound cards

gopass — personal document management

gopass-personal-docs    # interactive entry creation
gopass-query bills      # list recurring bills with totals
gopass-query storage    # list storage units with gate codes
gopass-query export bills  # export category to JSON

Makefile — daily workflow

make new-day      # create today's worklog + update attributes
make serve        # build + local server (port 8000)
make              # build only
make sync-nav     # sync worklog nav entries
make update-index # rebuild monthly index

KVM — VM & ISO management

list VMs on a KVM host
ssh kvm-01 "sudo virsh list --all"
ssh kvm-02 "sudo virsh list --all"
find ISE ISOs across KVM hosts (case-insensitive glob)
ssh kvm-01 "ls -lh /mnt/nas/isos/*[Ii][Ss][Ee]* /var/lib/libvirt/images/*[Ii][Ss][Ee]* /mnt/onboard-ssd/isos/*[Ii][Ss][Ee]* 2>/dev/null"
ssh kvm-02 "ls -lh /mnt/nas/isos/*[Ii][Ss][Ee]* /mnt/ssd/libvirt/images/*[Ii][Ss][Ee]* 2>/dev/null"
console into a VM
sudo virsh console <vm-name>             # Escape: Ctrl+]
check NAS mount on KVM host
ssh kvm-01 "mount | grep nas; ls /mnt/"

Per-project file dashboard

per-project summary — total files vs unencrypted plaintext
for d in data/d001/projects/*/; do
  total=$(find "$d" -type f | wc -l)
  plain=$(find "$d" -type f ! -name '*.age' ! -name 'README.adoc' ! -name '.gitkeep' ! -name '*.py' | wc -l)
  echo "$(basename "$d") | ${total} files | ${plain} plaintext"
done

USB-C / Thunderbolt Charging Diagnostics

Full evidence capture to file (one block, timestamped)
{
  echo "=== Power Supply ==="
  cat /sys/class/power_supply/*/status
  echo ""
  cat /sys/class/power_supply/*/type
  echo ""
  echo "=== UPower ==="
  upower -d | grep -E 'state|percentage|energy-rate|voltage'
  echo ""
  echo "=== dmesg (typec/thunderbolt/PD) ==="
  sudo dmesg | grep -iE 'typec|thunderbolt|ucsi|PD|power.delivery|charging' | tail -20
  echo ""
  echo "=== Pacman log (kernel/typec) ==="
  grep -iE 'thunderbolt|typec|ucsi|^.*upgraded linux ' /var/log/pacman.log | tail -20
} | tee /tmp/INC-$(date +%F)-usbc-charging.txt

Pattern: { } groups commands into a single stdout stream. tee writes to file AND displays on screen. Reusable for any multi-command evidence capture — change the commands inside, keep the structure.