365 lines
15 KiB
Markdown
365 lines
15 KiB
Markdown
# Git Audit Report — IT Pro Partner Gitea Organization
|
||
|
||
**Date:** 2026-08-08
|
||
**Auditor:** Hermes Agent (automated)
|
||
**Scope:** git.itpropartner.com / ippadmin (all 42 remote repos + 59 local clones in `/root/projects/`)
|
||
|
||
---
|
||
|
||
## Summary Verdict
|
||
|
||
**The Gitea organization suffers from repo sprawl, weak hygiene, and live secrets in history.** Of 42 remote repos, 32 are single-commit documentation stubs. Only 3–4 repos show active development. The `itpp-infrastructure/docs/` folder is a flat grab-bag and needs structured nesting. 23 local-only repos lack any off-server backup. Credentials are embedded in plaintext across at least 2 repos (`scripts`, `hermes-recovery`). Consolidation, cleanup, and a hygiene push are overdue.
|
||
|
||
---
|
||
|
||
## Quick Answers to User's Two Questions
|
||
|
||
### 1. Should `itpp-infrastructure/docs/` have more nested folders?
|
||
|
||
**Yes, absolutely.** The current structure is:
|
||
|
||
```
|
||
docs/
|
||
backup-restore/ ← already nested (good)
|
||
legal/ ← already nested (good)
|
||
ops-portal/ ← already nested (good)
|
||
app2-caddyfile-audit-2026-07-21.md ← flat
|
||
client-katie-watts-design.md ← flat
|
||
cost-control-rollout-2026-07-24.md ← flat
|
||
git-audit-2026-08-07.md ← flat
|
||
infrastructure-gap-assessment-2026-08-04.md ← flat
|
||
key-inventory.md ← flat
|
||
mattermost-replacement-analysis.md ← flat
|
||
model-chain.md ← flat
|
||
project-log.md ← flat
|
||
projects-master-readme.md ← flat
|
||
super-search-cf-bypass.md ← flat
|
||
super-search-enhancement-plan.md ← flat
|
||
uptime-kuma-monitoring-plan.md ← flat
|
||
```
|
||
|
||
**14 flat files is too many.** Recommended restructuring:
|
||
|
||
```
|
||
docs/
|
||
audit/ ← git-audit reports, gap assessments
|
||
backup-restore/ ← (existing, keep)
|
||
clients/ ← client-katie-watts-design.md
|
||
infrastructure/ ← model-chain.md, cost-control-rollout, caddyfile-audit, key-inventory
|
||
legal/ ← (existing, keep)
|
||
monitoring/ ← uptime-kuma-monitoring-plan.md
|
||
ops-portal/ ← (existing, keep)
|
||
projects/ ← project-log.md, projects-master-readme.md
|
||
super-search/ ← cf-bypass, enhancement-plan
|
||
mattermost-replacement-analysis.md ← leave at top level (one-off)
|
||
```
|
||
|
||
This gives every file a clear home without over-nesting.
|
||
|
||
### 2. Are there too many top-level repos? Should they be consolidated?
|
||
|
||
**Yes — 42 repos is far too many for the actual workload.** Here's the breakdown:
|
||
|
||
| Category | Count | Action |
|
||
|----------|-------|--------|
|
||
| Active development repos | 3–4 | Keep (`itpp-infrastructure`, `hermes-skills`, `scripts`, `homelab`) |
|
||
| Single-commit documentation stubs | 32 | Consolidate into fewer repos |
|
||
| Empty repos | 1 | Delete (`auth` — no commits, no content) |
|
||
| MCP stub repos | 5 | Merge into one `mcp-catalog` repo |
|
||
| Duplicate/stale repos | 2 | Resolve (`itpp-infra` vs `itpp-infrastructure`, `cartmylist-repo` vs `cartmylist`) |
|
||
| Local-only (unbacked) | 23 | Push to Gitea or archive |
|
||
|
||
**Recommended consolidation:**
|
||
|
||
1. **Merge 4 MCP stubs** (`mcp-browser`, `mcp-email`, `mcp-filesystem`, `mcp-git`) into `mcp-servers/` as subdirectories
|
||
2. **Merge related business ideas** into a `project-ideas` monorepo:
|
||
- `apex-track`, `boxpilot`, `digital-signage`, `fleettracker360`, `gift-a-roast`, `hudu`, `launchcheck`, `mooresunnydaze`, `nvr-shield`, `osint-tool`, `shark-game`, `startup-studio`, `track-a-flock`, `personal-assistant`
|
||
3. **Merge related operational repos**: `disaster-recovery` + `backup-restore` → `disaster-recovery/` with `backup-restore/` subdir
|
||
4. **Merge website/docs stubs**: `itpropartner-website`, `content-creation-pipeline`, `ops-portal` → subdirectories in `itpp-infrastructure`
|
||
5. **Resolve** `itpp-infra` (stale, SSH-only, 1 commit) → archive; use `itpp-infrastructure` as primary
|
||
6. **Delete** `auth` (empty repo with `auth.db` — never committed)
|
||
7. **Keep as-is**: `itpp-infrastructure`, `hermes-skills`, `hermes-recovery`, `scripts`, `homelab`, `dre`, `verdicttank`, `voipsimplicity`, `voipsimplicity-manual`, `forefront-wireless-portal`, `super-search-business`, `model-fallback`, `unifi`, `unms`, `pipeline`, `super-search`
|
||
|
||
**Target:** ~15–20 repos instead of 42.
|
||
|
||
---
|
||
|
||
## Detailed Findings
|
||
|
||
### Step 1 — Full Inventory
|
||
|
||
| Metric | Count |
|
||
|--------|-------|
|
||
| Remote repos on Gitea | 42 |
|
||
| Local clones in `/root/projects/` | 59 |
|
||
| On Gitea AND cloned locally | 36 |
|
||
| On Gitea but NOT cloned locally | 6 |
|
||
| Cloned locally but NOT on Gitea | 23 |
|
||
| Public repos | 30 |
|
||
| Private repos | 12 |
|
||
| Repos using `master` as default branch | 30 |
|
||
| Repos using `main` as default branch | 9 |
|
||
| Other (HEAD/detached) | 20 |
|
||
|
||
**Repos on Gitea but not cloned locally:** `cartmylist`, `mcp-browser`, `mcp-email`, `mcp-filesystem`, `mcp-git`, `super-search`
|
||
|
||
**Local-only repos (no remote — no off-server backup):** `assistant`, `auth`, `capabilities`, `hear-read`, `intelsight`, `intelsight-landing`, `internal`, `mockup`, `my-itpropartner-portal`, `ops`, `ops-v2-portal`, `osint`, `proposals`, `pry`, `research-search-mcp`, `schedule`, `shonuff`, `shonuff-caller`, `static`, `status`, `voice-previews`, `itpp-infra`, `cartmylist-repo`
|
||
|
||
### Step 2 — Per-Repo Deep Scan
|
||
|
||
#### Hygiene Check: `.gitignore` and `README.md`
|
||
|
||
| Status | Count |
|
||
|--------|-------|
|
||
| Has `.gitignore` | 8 of 59 (13.6%) |
|
||
| Has `README.md` | 56 of 59 (94.9%) |
|
||
|
||
**Repos missing `.gitignore`:** 51 repos. This is the single biggest hygiene gap.
|
||
|
||
**Repos missing `README.md`:** `cartmylist-repo`, `voipsimplicity-manual`, `auth`
|
||
|
||
#### Repos with Dirty Working Trees
|
||
|
||
| Repo | Dirty Files | Severity |
|
||
|------|-------------|----------|
|
||
| `hermes-skills` | 33 | **HIGH** — cache artifacts not committed |
|
||
| `hermes-recovery` | 26 | **HIGH** — uncommitted recovery scripts |
|
||
| `voipsimplicity-manual` | 11 | **MEDIUM** |
|
||
| `research-search-mcp` | 10 | **MEDIUM** |
|
||
| `auth` | 9 | **MEDIUM** |
|
||
| `digital-signage` | 6 | **LOW** |
|
||
| `itpp-infra` | 5 | **LOW** |
|
||
| `pipeline` | 5 | **LOW** |
|
||
| `disaster-recovery` | 4 | **LOW** |
|
||
| `mooresunnydaze` | 4 | **LOW** |
|
||
| `verdicttank` | 4 | **LOW** |
|
||
| `itpp-infrastructure` | 3 | **LOW** |
|
||
|
||
**13 repos** have uncommitted changes. `hermes-skills` (33 files) and `hermes-recovery` (26 files) are the worst offenders.
|
||
|
||
### Step 3 — Secrets Scan
|
||
|
||
**CRITICAL findings in 2 repos:**
|
||
|
||
#### `scripts` — 10 potential secrets
|
||
Real, hardcoded credentials found in Windows provisioning scripts:
|
||
```
|
||
+Password="[REDACTED]"
|
||
+Password="[REDACTED]"
|
||
+Password="[REDACTED]"
|
||
+Username="ippadmin"
|
||
+Username="liberty-admin"
|
||
```
|
||
|
||
These are active Windows admin credentials embedded in PowerShell unattend scripts. **This is a data breach risk.** If these repos ever go public or are cloned outside ITPP infrastructure, client credentials are exposed.
|
||
|
||
#### `hermes-recovery` — 8 potential secrets
|
||
Includes the Gitea API token used for this audit:
|
||
```
|
||
+TOKEN="[REDACTED]"
|
||
+TELEGRAM_BOT_TOKEN="[REDACTED]"
|
||
+password="***"
|
||
+token = "[REDACTED]"
|
||
```
|
||
|
||
The Gitea token itself is committed. This means `hermes-recovery` as a public repo exposes admin credentials.
|
||
|
||
#### `hermes-skills` — 15 potential hits
|
||
Most are false positives (example values, `process.env.` references, placeholder text). One real hit: a Comfy CLI API key in a SKILL.md.
|
||
|
||
### Step 4 — Structural Checks
|
||
|
||
#### Remote URL Audit
|
||
|
||
| Remote Type | Count | Action |
|
||
|-------------|-------|--------|
|
||
| HTTPS to Gitea | 36 | OK |
|
||
| GitHub (upstream) | 1 | OK (`msp-claude-skills`) |
|
||
| SSH to Gitea | 2 | **FIX** — `itpp-infra`, `cartmylist-repo` |
|
||
| No remote | 23 | **FIX** — local-only, no backup |
|
||
|
||
`itpp-infra` uses `git@git.itpropartner.com:ippadmin/itpp-infra.git` (SSH) — this repo has no corresponding HTTPS clone and appears to be a stale/abandoned repo (1 commit, 5 dirty files).
|
||
|
||
`cartmylist-repo` (local) vs `cartmylist` (Gitea) is a naming mismatch. The local clone has an SSH remote to what is likely a different repo.
|
||
|
||
#### Branch Naming
|
||
|
||
- **30 repos use `master`** — industry standard is now `main`
|
||
- **9 repos use `main`**
|
||
- **20 repos have detached HEAD or no commits**
|
||
|
||
**Branch mismatch:** `itpp-infrastructure` has `main` locally but `master` on Gitea. This means the remote may have both branches.
|
||
|
||
#### Large Files
|
||
|
||
| Repo | File | Size |
|
||
|------|------|------|
|
||
| `hermes-skills` | `.hub/index-cache/hermes-index.json` | 38.9 MB |
|
||
| `hermes-skills` | `.curator_backups/2026-07-12T15-48-44Z/skills.tar.gz` | 2.7 MB |
|
||
|
||
Both are cache artifacts that should be in `.gitignore`, not tracked.
|
||
|
||
#### Public vs Private
|
||
|
||
**30 of 42 repos (71%) are public.** This is a concern because:
|
||
- `scripts` contains client admin passwords — **public**
|
||
- `hermes-recovery` contains Gitea admin token — **public**
|
||
- Many repos with sensitive infrastructure details are public
|
||
|
||
### Step 5 — Commit Quality
|
||
|
||
#### Commit Message Quality
|
||
|
||
| Pattern | Count | Assessment |
|
||
|---------|-------|------------|
|
||
| `Initial commit — YYYY-MM-DD` | 16 | Poor — conveys nothing |
|
||
| `Update YYYY-MM-DD — root` | 5 | Meaningless |
|
||
| `Initial: <project name>` | 8 | Barely adequate |
|
||
| Descriptive conventional commits | 4 | Good (`homelab`, `verdicttank`, `forefront-wireless-portal`) |
|
||
|
||
**32 repos have only 1 commit** — these are documentation stubs, not developed projects.
|
||
|
||
#### Active vs Abandoned
|
||
|
||
| Status | Criteria | Repos |
|
||
|--------|----------|-------|
|
||
| **Active** | 3+ commits, recent activity | `itpp-infrastructure` (52), `scripts` (11), `homelab` (6), `forefront-wireless-portal` (5), `dre` (4), `verdicttank` (4), `personal-assistant` (3), `super-search-business` (3), `voipsimplicity` (3) |
|
||
| **Stub** | 1–2 commits, last push July 2025 | 32 repos |
|
||
| **Abandoned** | No commits or stale >3 months | `itpp-infra`, `auth`, `cartmylist-repo` |
|
||
|
||
### Step 6 — Local-Only Repos
|
||
|
||
23 repos in `/root/projects/` have no remote. Breakdown:
|
||
|
||
| Category | Repos | Action |
|
||
|----------|-------|--------|
|
||
| Uncommitted stubs (0 commits) | 17 | Push to Gitea or archive |
|
||
| Has commits, no remote | 1 (`shonuff-caller`) | Push to Gitea |
|
||
| Stale noise | 5 | Archive and delete (`auth`, `itpp-infra`, `cartmylist-repo`, etc.) |
|
||
|
||
The 17 repos with 0 commits and only detached HEAD are effectively just directories with a `.git` folder — not real repos. They should be either pushed as proper repos or archived.
|
||
|
||
---
|
||
|
||
## Prioritized Action Plan
|
||
|
||
### 🔴 Immediate (This Week)
|
||
|
||
| # | Action | Severity |
|
||
|---|--------|----------|
|
||
| 1 | **Rotate all credentials exposed in `scripts` repo** — Windows passwords, Gitea token, Telegram bot token. Then purge from Git history with `git filter-branch` or `bfg-repo-cleaner` | **CRITICAL** |
|
||
| 2 | **Rotate Gitea API token** in `hermes-recovery` — it's publicly visible. Generate new token, update all consumers, purge old from history | **CRITICAL** |
|
||
| 3 | **Make `scripts` and `hermes-recovery` PRIVATE** — they contain live credentials visible to anyone | **CRITICAL** |
|
||
| 4 | **Add `.gitignore` to all 51 repos missing one** — start with the active repos first | **HIGH** |
|
||
| 5 | **Commit or stash all dirty working trees** — 13 repos have uncommitted work at risk of loss | **HIGH** |
|
||
|
||
### 🟡 Short-Term (This Month)
|
||
|
||
| # | Action | Severity |
|
||
|---|--------|----------|
|
||
| 6 | **Reorganize `itpp-infrastructure/docs/`** into nested folders (audit/, clients/, infrastructure/, monitoring/, projects/, super-search/) | **MEDIUM** |
|
||
| 7 | **Consolidate 4 MCP repos into `mcp-servers/`** as subdirectories — delete empty stubs after merge | **MEDIUM** |
|
||
| 8 | **Merge 14 single-commit business idea repos** into a `project-ideas` monorepo | **MEDIUM** |
|
||
| 9 | **Delete `auth`** (empty repo, 0 commits) | **MEDIUM** |
|
||
| 10 | **Resolve `itpp-infra` vs `itpp-infrastructure`** — archive `itpp-infra`, standardize on `itpp-infrastructure` | **MEDIUM** |
|
||
| 11 | **Add `.gitignore` entries to `hermes-skills`** for `.hub/`, `.curator_backups/` | **MEDIUM** |
|
||
| 12 | **Rename `master` → `main` on repos where it matters** (at minimum `itpp-infrastructure` to fix branch mismatch) | **LOW** |
|
||
|
||
### 🔵 Pre-Public / Pre-Open-Source
|
||
|
||
| # | Action | Severity |
|
||
|---|--------|----------|
|
||
| 13 | **Audit all 30 public repos** — ensure no private infrastructure details, client names, IPs, or credentials are exposed | **HIGH** |
|
||
| 14 | **Decide public/private policy** — which repos genuinely need to be public? Currently 71% are public. | **MEDIUM** |
|
||
| 15 | **Push 23 local-only repos to Gitea** or archive them. No code living only on a single server. | **HIGH** |
|
||
| 16 | **Clean commit history** — rebase repos with "Update YYYY-MM-DD — root" messages into meaningful commits | **LOW** |
|
||
|
||
---
|
||
|
||
## Template: Standard `.gitignore`
|
||
|
||
For any new or cleaned repo, use:
|
||
|
||
```gitignore
|
||
# OS
|
||
.DS_Store
|
||
Thumbs.db
|
||
|
||
# IDE
|
||
.vscode/
|
||
.idea/
|
||
*.swp
|
||
*.swo
|
||
|
||
# Python
|
||
__pycache__/
|
||
*.py[cod]
|
||
*.egg-info/
|
||
.venv/
|
||
venv/
|
||
|
||
# Node
|
||
node_modules/
|
||
|
||
# Secrets — NEVER commit these
|
||
.env
|
||
.env.*
|
||
*.pem
|
||
*.key
|
||
credentials.json
|
||
*.token
|
||
|
||
# Cache / generated
|
||
.hub/
|
||
.curator_backups/
|
||
*.tar.gz
|
||
*.zip
|
||
|
||
# Data
|
||
*.db
|
||
*.sqlite
|
||
*.sqlite3
|
||
```
|
||
|
||
---
|
||
|
||
## Appendix: Full Repo Inventory
|
||
|
||
### Active Repos (keep as standalone)
|
||
|
||
| Repo | Commits | Last Commit | Branch | `.gitignore` | Assessment |
|
||
|------|---------|-------------|--------|-------------|------------|
|
||
| `itpp-infrastructure` | 52 | 2026-08-07 | main/master mismatch | YES | **Primary hub** — healthy |
|
||
| `hermes-skills` | 1 | 2026-07-15 | master | NO | Active mirror, 13.6MB |
|
||
| `hermes-recovery` | 1 | 2026-07-15 | master | YES | Critical backup kit |
|
||
| `scripts` | 11 | 2026-07-25 | master | NO | Active, **has secrets** |
|
||
| `homelab` | 6 | 2026-07-24 | main | NO | Active, good commits |
|
||
| `dre` | 4 | 2026-07-25 | master | NO | Active development |
|
||
| `verdicttank` | 4 | 2026-08-07 | main | NO | Active, good commits |
|
||
| `forefront-wireless-portal` | 5 | 2026-07-25 | master | NO | Active, good commits |
|
||
| `super-search-business` | 3 | 2026-07-25 | main | NO | Active |
|
||
| `voipsimplicity` | 3 | 2026-07-24 | master | NO | Active client work |
|
||
| `voipsimplicity-manual` | 1 | 2026-08-05 | master | NO | Active client work |
|
||
|
||
### Stub Repos (consolidate)
|
||
|
||
All 32 repos below are single-commit documentation stubs with no ongoing development. Consolidate into `project-ideas/` monorepo or relevant parent repo:
|
||
|
||
`apex-track`, `backup-restore`, `boxpilot`, `content-creation-pipeline`, `digital-signage`, `disaster-recovery`, `fleettracker360`, `gift-a-roast`, `hudu`, `itpropartner-website`, `launchcheck`, `mcp-browser`, `mcp-email`, `mcp-filesystem`, `mcp-git`, `mcp-servers`, `model-fallback`, `mooresunnydaze`, `nvr-shield`, `ops-portal`, `osint-tool`, `personal-assistant`, `pipeline`, `shark-game`, `startup-studio`, `super-search`, `track-a-flock`, `unifi`, `unms`, `cartmylist`
|
||
|
||
### To Delete or Archive
|
||
|
||
| Repo | Reason |
|
||
|------|--------|
|
||
| `auth` | Empty (0 commits, 0 content) |
|
||
| `itpp-infra` | Stale duplicate of `itpp-infrastructure`, SSH-only remote, 1 commit |
|
||
| `cartmylist-repo` | Local clone with SSH remote, mismatched name (real one is `cartmylist` on Gitea) |
|
||
|
||
### Local-Only (push or archive)
|
||
|
||
`assistant`, `auth`, `capabilities`, `hear-read`, `intelsight`, `intelsight-landing`, `internal`, `mockup`, `my-itpropartner-portal`, `ops`, `ops-v2-portal`, `osint`, `proposals`, `pry`, `research-search-mcp`, `schedule`, `shonuff`, `shonuff-caller`, `static`, `status`, `voice-previews`
|
||
|
||
---
|
||
|
||
*Report generated by Hermes Agent git-audit workflow. Next audit recommended: 2026-11-08.*
|