From 7cb2ebed2de9db3780a4ed29ad1aca0863543f8a Mon Sep 17 00:00:00 2001 From: jgitta Date: Wed, 8 Jul 2026 14:12:05 -0500 Subject: [PATCH] Sync workspace with Gitea: merge June/July session docs - README + siklos-docker-services: add authentik/searxng/beszel rows, photos VM section, 2026-06-24 cleanup notes, 2026-07-03 Portainer conversion note (jgpc naming preserved) - Add: authentik setup docs, frigate docs/config, OCIS migration docs, AdventureLog bug report, install-native-apps.sh, workstation-disk-analysis.md - Remove obsolete: cgitta-nextcloud-zip-analysis.md, kopia-restore-guide.md - .gitignore: ocis-credentials.md (secrets stay local), *.bak --- .gitignore | 3 + ...reLog-BugReport-location-name-overwrite.md | 71 ++++ README.md | 19 +- authentik/SETUP-GUIDE.md | 352 ++++++++++++++++++ authentik/caddy-auth.caddy | 49 +++ authentik/docker-compose.yml | 92 +++++ cgitta-nextcloud-zip-analysis.md | 64 ---- docs/jarvis-build-documentation.docx | Bin 19977 -> 0 bytes .../frigate-cameras.md => frigate-cameras.md | 0 jgpc/frigate/config.yml => frigate-config.yml | 0 frigate-docker-compose.yml | 40 ++ homelab-overview.md | 27 +- install-native-apps.sh | 213 +++++++++++ jellyfin/node-exporter/docker-compose.yml | 15 + jgpc/frigate/frigate-cctv-setup.md | 176 --------- kopia-restore-guide.md | 153 -------- nextcloud-to-ocis-migration-plan.md | 260 +++++++++++++ ocis-migration-file-comparison-2026-06-22.md | 185 +++++++++ owncloud/README.md | 76 ---- owncloud/organize_photos.sh | 72 ---- siklos-docker-services.md | 15 +- siklos/beszel/docker-compose.yml | 27 -- siklos/monitoring/docker-compose.yml | 65 ++++ .../frigate/docker-compose.yml | 0 .../open-webui/docker-compose.yml | 0 .../stirling-pdf/docker-compose.yml | 0 workstation-disk-analysis.md | 232 ++++++++++++ 27 files changed, 1611 insertions(+), 595 deletions(-) create mode 100644 AdventureLog-BugReport-location-name-overwrite.md create mode 100644 authentik/SETUP-GUIDE.md create mode 100644 authentik/caddy-auth.caddy create mode 100644 authentik/docker-compose.yml delete mode 100644 cgitta-nextcloud-zip-analysis.md delete mode 100644 docs/jarvis-build-documentation.docx rename jgpc/frigate/frigate-cameras.md => frigate-cameras.md (100%) rename jgpc/frigate/config.yml => frigate-config.yml (100%) create mode 100644 frigate-docker-compose.yml create mode 100755 install-native-apps.sh create mode 100644 jellyfin/node-exporter/docker-compose.yml delete mode 100644 jgpc/frigate/frigate-cctv-setup.md delete mode 100644 kopia-restore-guide.md create mode 100644 nextcloud-to-ocis-migration-plan.md create mode 100644 ocis-migration-file-comparison-2026-06-22.md delete mode 100644 owncloud/README.md delete mode 100755 owncloud/organize_photos.sh delete mode 100644 siklos/beszel/docker-compose.yml create mode 100644 siklos/monitoring/docker-compose.yml rename {jgpc => thinkstation}/frigate/docker-compose.yml (100%) rename {jgpc => thinkstation}/open-webui/docker-compose.yml (100%) rename {jgpc => thinkstation}/stirling-pdf/docker-compose.yml (100%) create mode 100644 workstation-disk-analysis.md diff --git a/.gitignore b/.gitignore index 2066fb9..7390421 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,6 @@ credentials.md kopia-docker-compose-fixed.yml *.secret *.env +ocis-credentials.md +*.bak +*.bak2 diff --git a/AdventureLog-BugReport-location-name-overwrite.md b/AdventureLog-BugReport-location-name-overwrite.md new file mode 100644 index 0000000..f25b741 --- /dev/null +++ b/AdventureLog-BugReport-location-name-overwrite.md @@ -0,0 +1,71 @@ +# [BUG] Selecting a location search result overwrites the correct name with a reverse-geocoded POI name (e.g. "39") + +🛑 **Note**: I searched existing issues before filing and did not find a match. + +## 🐞 Bug Description + +When adding a location, the search box returns the correct place from OpenStreetMap (e.g. **"Elk Rock State Park"**) with the correct address. But as soon as I **click that search result**, the app discards the result's name and replaces the location **Name** field with an unrelated value — in my case the bare number **"39"**. + +The root cause is that selecting a search result does not keep the name the search already provided. Instead the app takes only the result's coordinates and performs a **reverse geocode** on them, then uses that reverse-geocode's `location_name` as the location Name. At the park's center point, the nearest named OSM object is a campsite tagged `tourism=39`, so the Name becomes "39". The coordinates and region (Marion County, Iowa) are correct — only the Name is wrong. + +This means the app is throwing away good data it already has. + +## 🔄 Steps to Reproduce + +1. Add a new Location (e.g. inside a Collection). +2. In "Search for a location", type `Elk Rock State Park`. +3. The dropdown correctly shows **"Elk Rock State Park — Elk Rock State Park, Knoxville Township, Marion County, Iowa, United States"**. +4. Click that result. +5. The Name field is populated with **"39"** instead of "Elk Rock State Park". + +## ✅ Expected Behavior + +When a user selects a search result, the location **Name** should be set from that result's `name` (e.g. "Elk Rock State Park"). Reverse geocoding is fine for filling region/city/country, but it should **not overwrite** the name the user just selected from search. + +## 📸 Evidence (server-side reproduction) + +Running the app's own geocoding functions on the backend (`v0.12.1`, OSM/Nominatim, no Google key): + +**Step A — what the search dropdown returns (`search_osm('elk rock state park')[0]`):** +``` +name = 'Elk Rock State Park' +lat, lon = 41.4008688, -93.0776561 +display_name = 'Elk Rock State Park, Knoxville Township, Marion County, Iowa, United States' +``` + +**Step B — what the app does on selection (`reverse_geocode(41.4008688, -93.0776561, user)`):** +``` +location_name = '39' +display_name = 'Marion County, Iowa, US' +city / region = Marion County / Iowa +``` + +Raw Nominatim reverse lookup for those coordinates confirms the source of "39": +``` +name: '39' +display_name: '39, Hayes Drive, Knoxville Township, Marion County, Iowa, 50138, United States' +address: { "tourism": "39", "road": "Hayes Drive", "county": "Marion County", ... } +``` + +So the correct name ("Elk Rock State Park") from Step A is replaced by the reverse-geocode `location_name` ("39") from Step B. + +## 🔧 Suggested Fix + +In the frontend "add location" flow, when a search result is chosen, keep that result's `name` for the location Name field. Continue to call `reverse_geocode` for region/city/country enrichment, but do not let its `location_name` overwrite a name that came from an explicit search-result selection. (Only fall back to the reverse-geocoded name when the location was placed by clicking the map, where no search name exists.) + +Relevant backend code for reference: +- `adventures/views/reverse_geocode_view.py` → `search` (returns the correct `name`) +- `adventures/geocoding.py` → `search_osm` (passes OSM `name` through) and `reverse_geocode` / `reverse_geocode_osm` (returns `location_name` from the nearest OSM object, which can be a bare number) + +## 🐳 Environment Details + +- **Host:** Self-hosted (Proxmox VM, Docker via Portainer) +- **Install Method:** Docker Compose +- **AdventureLog Version:** v0.12.1 +- **Reverse Proxy:** Caddy +- **Geocoding provider:** OpenStreetMap / Nominatim (no `GOOGLE_MAPS_API_KEY` set) +- **Browser:** + +## 📎 Additional Context + +The reverse-geocoded region data (Marion County, Iowa, United States) is correct — this bug is specifically about the **Name** field being overwritten after a search-result selection. Users can manually retype the Name as a workaround, but it defeats the purpose of searching. diff --git a/README.md b/README.md index 925e4bc..d991104 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ All stacks are managed via **Portainer** at http://192.168.88.27:9000 (or https: | siklos | 192.168.88.27 | Main Docker server (VM 112 on Proxmox) | 3 (local) | 26.1.5 | | jellyfin | 192.168.88.10 | Media server | 4 (agent) | 26.1.5 | | jgpc | 192.168.88.41 | Workstation / Ollama / AI | 5 (agent) | 29.4.2 | +| photos | 192.168.88.32 | Photos VM (VM 113) — Immich | 8 (agent) | — | ## Stack Inventory @@ -34,7 +35,7 @@ All stacks are managed via **Portainer** at http://192.168.88.27:9000 (or https: | Stack | Port(s) | Data Path | URL | Notes | |---|---|---|---|---| | actual-budget | 5006 | /srv/docker/actual-budget | budget.jgitta.com | Budget tracking | -| beszel | 8085 | /srv/docker/beszel | beszel.jgitta.com | Beszel hub — all-in-one monitoring (replaced Prometheus/Grafana/cAdvisor) | +| authentik | 9011 | /srv/docker/authentik | auth.jgitta.com | SSO / identity provider (stack 58; secrets stored as Portainer env vars) | | dashboard | 8096 | /srv/docker/dashboard | dashboard.jgitta.com | Custom homepage | | dashy | 8081 | /srv/docker/dashy | dashy.jgitta.com | Dashboard | | gitea | 3002, 2222 | /srv/docker/gitea/data | gitea.jgitta.com | Git server — this repo | @@ -47,13 +48,21 @@ All stacks are managed via **Portainer** at http://192.168.88.27:9000 (or https: | linkwarden | 3015 | /srv/docker/linkwarden/pgdata | links.jgitta.com | Link manager | | litellm | 4000 | /srv/docker/litellm | — | LLM API gateway | | meshcentral | 444 | /srv/docker/meshcentral | mesh.jgitta.com | Remote management | +| beszel | 8085 | /srv/docker/beszel | beszel.jgitta.com | Beszel hub — all-in-one monitoring (replaced Prometheus/Grafana/cAdvisor) | | onlyoffice | 8880 | /srv/docker/onlyoffice | office.jgitta.com | Office document server (replaced Collabora) | -| pihole | host network | /srv/docker/pihole | pihole.jgitta.com | DNS ad blocker | +| pihole | host network | /srv/docker/pihole | pihole.jgitta.com | DNS ad blocker + faster-whisper + open-webui | | portainer | 9000, 9443 | /srv/docker/portainer | portainer.jgitta.com | Container management UI | | uptime-kuma | 3001 | /srv/docker/uptime-kuma/data | status.jgitta.com | Uptime monitoring | +| searxng | 8092 | /srv/docker/searxng | search.jgitta.com | Private search engine | | watchtower | — | — | — | Auto-updates containers | | wordpress | 8095 | /srv/docker/wordpress | jgitta.com | WordPress site | +### photos (192.168.88.32) — 1 stack + +| Stack | Port(s) | Data Path | URL | Notes | +|---|---|---|---|---| +| immich | 2283 | /mnt/immich-photos (uploads), /srv/docker/immich/postgres (DB) | pictures.jgitta.com | Stack 45; postgres needs `shared_preload_libraries=vectors.so,vchord.so` (unquoted, list-form command) | + ### jellyfin (192.168.88.10) — 3 stacks | Stack | Port(s) | Data Path | Notes | @@ -62,14 +71,13 @@ All stacks are managed via **Portainer** at http://192.168.88.27:9000 (or https: | beszel-agent | 45876 | — | Beszel agent (systemd binary, not Docker) | | portainer-agent | 9001 | — | Portainer agent (fully managed) | -### jgpc (192.168.88.41) — 4 stacks +### jgpc (192.168.88.41) — 3 stacks | Stack | Port(s) | Data Path | Notes | |---|---|---|---| | frigate | 5000, 8554, 8555 | /srv/docker/frigate | NVR / camera AI | | open-webui | 3000 | /srv/docker/open-webui | Ollama web UI | | portainer-agent | 9001 | — | Portainer agent (shows "limited" — expected, see notes) | -| stirling-pdf | 8081 | /srv/docker/stirling-pdf | PDF tools | | beszel-agent | 45876 | — | Beszel agent (systemd binary, not Docker) | ## Caddy Reverse Proxy @@ -111,6 +119,8 @@ All stack data is stored under `/srv/docker//` on each host. Old compose files were renamed to `.bak` after migration to Portainer. +**2026-07-03 — external stacks converted to Portainer:** authentik (stack 58), beszel (57), searxng (56) were created in Portainer; watchtower (18), karakeep (21), frigate (31), immich (45) were redeployed so Portainer owns the running containers. Frigate and immich stack definitions were updated to match the newer host compose files (frigate: cpuset pinning + 8G memory limit; immich: /mnt/immich-photos path + vectorchord postgres image). Host compose files renamed to `.bak`. Do not run `docker compose up` from /srv/docker — deploy via Portainer only. + ## Kopia Backup Notes (updated 2026-05-07) Kopia backs up Nextcloud data (VM 103) and other sources to TrueNAS NFS. @@ -142,4 +152,3 @@ siklos to manage all three Docker hosts from one UI. | OCIS VM | VM 114 (192.168.88.63) — cloud.jgitta.com | | Docker server VM | VM 112 = siklos (192.168.88.27) | | Caddy proxy VM | 192.168.88.110 | - diff --git a/authentik/SETUP-GUIDE.md b/authentik/SETUP-GUIDE.md new file mode 100644 index 0000000..4469cf8 --- /dev/null +++ b/authentik/SETUP-GUIDE.md @@ -0,0 +1,352 @@ +# Authentik SSO — Setup Guide + +Authentik is your central identity provider. Once set up, you log in once at `auth.jgitta.com` and all your services recognize you automatically. + +--- + +## Phase 1: Deploy the Stack + +### Step 1 — Copy files to siklos + +SSH into siklos and create the directory: + +```bash +ssh jgitta@192.168.88.27 +sudo mkdir -p /srv/docker/authentik +``` + +Copy the files from this folder to siklos: + +```bash +# Run these from your local machine (not from siklos) +scp docker-compose.yml jgitta@192.168.88.27:/srv/docker/authentik/ +scp .env jgitta@192.168.88.27:/srv/docker/authentik/ +``` + +### Step 2 — Deploy via Portainer + +1. Open Portainer at `https://portainer.jgitta.com` +2. Click **Stacks** → **Add stack** +3. Name it `authentik` +4. Choose **Upload** and upload the `docker-compose.yml` from this folder +5. Scroll down to **Environment variables** → click **Load variables from .env file** → upload `.env` +6. Click **Deploy the stack** + +Authentik takes about 60–90 seconds to start on first run (it runs database migrations). + +### Step 3 — Add auth.jgitta.com to Caddy + +SSH into the Caddy VM and add the auth block: + +```bash +ssh caddy # (root@192.168.88.110) +nano /etc/caddy/sites/infrastructure.caddy +``` + +Add this at the bottom of the file: + +``` +auth.jgitta.com { + import web_secure + reverse_proxy 192.168.88.27:9011 +} +``` + +Then reload Caddy: + +```bash +systemctl reload caddy +``` + +### Step 4 — Add DNS record + +Add `auth.jgitta.com` to: +- **MikroTik** DNS: `auth.jgitta.com` → `192.168.88.110` (same as all other subdomains) +- **Cloudflare**: A record `auth` → your WAN IP, **grey cloud** (DNS only) + +--- + +## Phase 2: First-Time Setup + +1. Open `https://auth.jgitta.com/if/flow/initial-setup/` +2. Create your admin account (email + password) +3. You'll land on the Authentik admin dashboard + +> **Tip:** The admin interface is at `https://auth.jgitta.com/if/admin/` — bookmark it. + +--- + +## Phase 3: Service Integrations + +### What each type means + +- **Native OIDC**: The service has a built-in "Login with SSO" button. Best experience. +- **Forward Auth**: Caddy intercepts the request and checks Authentik before letting you in. Works on services with no login support at all. + +--- + +### 3A — Native OIDC Services + +For each service below, you create an **OAuth2/OIDC Provider** in Authentik, then configure the service to use it. + +#### How to create an OIDC Provider in Authentik (do this for each service) + +1. Go to `https://auth.jgitta.com/if/admin/` → **Applications** → **Providers** → **Create** +2. Choose **OAuth2/OpenID Provider** +3. Fill in: + - **Name**: e.g. `Gitea` + - **Authorization flow**: `default-provider-authorization-explicit-consent` + - **Client type**: `Confidential` + - **Redirect URIs**: (see per-service table below) +4. Click **Finish** — copy the **Client ID** and **Client Secret** +5. Go to **Applications** → **Create**: + - **Name**: same as provider + - **Slug**: lowercase, e.g. `gitea` + - **Provider**: select the one you just created + +--- + +#### Gitea (`gitea.jgitta.com`) + +**Redirect URI**: `https://gitea.jgitta.com/user/oauth2/authentik/callback` + +In Gitea → Site Administration → Authentication Sources → Add: +| Field | Value | +|---|---| +| Authentication type | OAuth2 | +| Name | `authentik` | +| OAuth2 provider | OpenID Connect | +| Client ID | (from Authentik) | +| Client Secret | (from Authentik) | +| OpenID Connect Auto Discovery URL | `https://auth.jgitta.com/application/o/gitea/.well-known/openid-configuration` | + +--- + +#### Portainer (`portainer.jgitta.com`) + +**Redirect URI**: `https://portainer.jgitta.com/` + +In Portainer → Settings → Authentication → OAuth: +| Field | Value | +|---|---| +| Provider | Custom | +| Client ID | (from Authentik) | +| Client Secret | (from Authentik) | +| Authorization URL | `https://auth.jgitta.com/application/o/authorize/` | +| Access token URL | `https://auth.jgitta.com/application/o/token/` | +| Resource URL | `https://auth.jgitta.com/application/o/userinfo/` | +| Redirect URL | `https://portainer.jgitta.com/` | +| Logout URL | `https://auth.jgitta.com/application/o/portainer/end-session/` | +| User identifier | `preferred_username` | +| Scopes | `openid email profile` | + +--- + +#### Linkwarden (`links.jgitta.com`) + +**Redirect URI**: `https://links.jgitta.com/api/v1/auth/callback/authentik` + +Add to the Linkwarden stack's environment variables in Portainer: +``` +NEXTAUTH_URL=https://links.jgitta.com +AUTHENTIK_CUSTOM_NAME=Authentik +AUTHENTIK_ISSUER=https://auth.jgitta.com/application/o/linkwarden/ +AUTHENTIK_CLIENT_ID= +AUTHENTIK_CLIENT_SECRET= +``` + +Redeploy the stack after adding these. + +--- + +#### Immich (VM113 — `192.168.88.32`) + +**Redirect URI**: `https://immich.jgitta.com/auth/login` *(if you have this subdomain)* — or `app.immich.cloud://` for mobile app + +In Immich → Administration → Authentication Settings: +| Field | Value | +|---|---| +| Enable OAuth | ✓ | +| Issuer URL | `https://auth.jgitta.com/application/o/immich/` | +| Client ID | (from Authentik) | +| Client Secret | (from Authentik) | +| Scope | `openid email profile` | +| Button text | `Login with Authentik` | +| Auto register | ✓ (optional — creates Immich user on first SSO login) | + +--- + +#### OCIS / ownCloud (`cloud.jgitta.com`, VM114) + +OCIS already uses OIDC. Update its config to point to Authentik instead of its built-in IDP. + +**Redirect URI**: `https://cloud.jgitta.com/` + +SSH into VM114 and edit the OCIS systemd environment or config file to set: +``` +OCIS_OIDC_ISSUER=https://auth.jgitta.com/application/o/ocis/ +PROXY_OIDC_ISSUER=https://auth.jgitta.com/application/o/ocis/ +WEB_OIDC_CLIENT_ID= +``` + +> Note: OCIS with external OIDC is more involved — reach out if you want a dedicated guide for this one. + +--- + +#### Home Assistant (`ha.jgitta.com`, VM106) + +**Redirect URI**: `https://ha.jgitta.com/auth/oidc/callback` + +In Home Assistant → `configuration.yaml`, add: +```yaml +homeassistant_cloud: # remove this if present + +# In configuration.yaml: +http: + use_x_forwarded_for: true + trusted_proxies: + - 192.168.88.110 # Caddy VM + +# Via HACS or built-in: install "OpenID Connect" (HACS → Integrations → search OIDC) +# Or use the built-in auth provider: +homeassistant: + auth_providers: + - type: homeassistant + - type: trusted_networks + trusted_networks: + - 192.168.88.0/24 +``` + +Then add the `authentik` integration via Settings → Integrations → Add → search "OpenID Connect": +| Field | Value | +|---|---| +| Client ID | (from Authentik) | +| Client Secret | (from Authentik) | +| Metadata URL | `https://auth.jgitta.com/application/o/homeassistant/.well-known/openid-configuration` | + +--- + +#### Open WebUI (`ai.jgitta.com`) + +**Redirect URI**: `https://ai.jgitta.com/oauth/oidc/callback` + +Add to the Open WebUI stack environment variables: +``` +ENABLE_OAUTH_SIGNUP=true +OAUTH_MERGE_ACCOUNTS_BY_EMAIL=true +OAUTH_PROVIDER_NAME=Authentik +OPENID_PROVIDER_URL=https://auth.jgitta.com/application/o/openwebui/.well-known/openid-configuration +OAUTH_CLIENT_ID= +OAUTH_CLIENT_SECRET= +OAUTH_SCOPES=openid email profile +``` + +--- + +#### Homarr (`homarr.jgitta.com`) + +**Redirect URI**: `https://homarr.jgitta.com/api/auth/callback/oidc` + +Add to Homarr stack environment: +``` +AUTH_PROVIDER=oidc +AUTH_OIDC_CLIENT_ID= +AUTH_OIDC_CLIENT_SECRET= +AUTH_OIDC_URI=https://auth.jgitta.com/application/o/homarr/ +AUTH_OIDC_CLIENT_NAME=Authentik +``` + +--- + +#### Nextcloud (`next.jgitta.com`, VM103) + +Install the `user_oidc` app in Nextcloud (Apps → Search "OpenID Connect user backend"). + +In Nextcloud → Administration → OpenID Connect: +| Field | Value | +|---|---| +| Identifier | `authentik` | +| Client ID | (from Authentik) | +| Client Secret | (from Authentik) | +| Discovery endpoint | `https://auth.jgitta.com/application/o/nextcloud/.well-known/openid-configuration` | + +**Redirect URI** to enter in Authentik: `https://next.jgitta.com/apps/user_oidc/code` + +--- + +### 3B — Forward Auth (Caddy Middleware) + +These services have no native SSO. Caddy checks Authentik before granting access. + +#### Step 1 — Create a Proxy Provider in Authentik + +1. Authentik Admin → **Applications** → **Providers** → **Create** +2. Choose **Proxy Provider** +3. Set: + - **Name**: `Forward Auth` + - **Authorization flow**: `default-provider-authorization-implicit-consent` + - **Forward auth (single application)** → OR **Forward auth (domain level)** + - For domain-level (covers all subdomains): external host = `https://auth.jgitta.com` +4. Create an Application called `Forward Auth` linked to this provider + +#### Step 2 — Deploy the Outpost + +1. Authentik Admin → **Applications** → **Outposts** → **Create** +2. Type: **Proxy** +3. Applications: select `Forward Auth` +4. Integration: **Docker** (Authentik will auto-deploy the outpost container on siklos) + +#### Step 3 — Add forward auth to Caddy + +Edit `/etc/caddy/snippets.caddy` on the Caddy VM and add: + +``` +(authentik_forward_auth) { + forward_auth http://192.168.88.27:9000 { + uri /outpost.goauthentik.io/auth/caddy + copy_headers X-authentik-username X-authentik-groups X-authentik-email X-authentik-name X-authentik-uid + trusted_proxies private_ranges + } +} +``` + +Then add `import authentik_forward_auth` to any site block you want protected: + +``` +dashy.jgitta.com { + import web_secure + import authentik_forward_auth + reverse_proxy http://192.168.88.27:8081 +} +``` + +**Services to protect with forward auth:** +| Service | Subdomain | Current Port | +|---|---|---| +| Dashy | dashy.jgitta.com | :8081 | +| SearXNG | search.jgitta.com | :8092 | +| Beszel | beszel.jgitta.com | :8085 | +| Uptime Kuma | status.jgitta.com | :3001 | +| Glances | glances.jgitta.com | :61208 | +| Actual Budget | budget.jgitta.com | :5006 | +| Guacamole | apache.jgitta.com | :8080 | + +--- + +## Recommended Order + +1. Deploy Authentik stack (Phase 1) +2. Complete initial setup (Phase 2) +3. Start with **Gitea** (simplest native OIDC, easy to test) +4. Add **Portainer** and **Homarr** +5. Set up **Forward Auth outpost** to protect Dashy, SearXNG, Beszel, etc. +6. Tackle Nextcloud, OCIS, Immich, Home Assistant individually + +--- + +## Troubleshooting Tips + +- **Can't reach auth.jgitta.com**: Check that Caddy reloaded (`systemctl reload caddy`) and MikroTik DNS has the record +- **Redirect URI mismatch error**: The redirect URI in Authentik must exactly match what the service sends — check for trailing slashes +- **"Invalid client"**: Client ID or Secret was copy-pasted with extra whitespace — re-enter manually +- **Forward auth loops**: Make sure the Authentik app URL itself (`auth.jgitta.com`) does NOT have `import authentik_forward_auth` — it would loop forever diff --git a/authentik/caddy-auth.caddy b/authentik/caddy-auth.caddy new file mode 100644 index 0000000..15a6c57 --- /dev/null +++ b/authentik/caddy-auth.caddy @@ -0,0 +1,49 @@ +# ============================================================================= +# Authentik — add this block to /etc/caddy/sites/infrastructure.caddy +# on the Caddy VM (192.168.88.110) +# ============================================================================= + +# Main Authentik UI — login portal at https://auth.jgitta.com +auth.jgitta.com { + import web_secure + reverse_proxy 192.168.88.27:9011 +} + + +# ============================================================================= +# Forward Auth snippet — add to snippets.caddy on the Caddy VM +# ============================================================================= +# This snippet protects any service that doesn't have its own login page. +# Add "import authentik_forward_auth" to any site block to require Authentik login. +# +# (authentik_forward_auth) { +# forward_auth http://192.168.88.27:9011 { +# uri /outpost.goauthentik.io/auth/caddy +# copy_headers X-authentik-username X-authentik-groups X-authentik-email X-authentik-name X-authentik-uid +# trusted_proxies private_ranges +# } +# } + + +# ============================================================================= +# Example: protect Dashy with forward auth +# Replace your existing dashy block with this: +# ============================================================================= +# dashy.jgitta.com { +# import web_secure +# import authentik_forward_auth +# reverse_proxy http://192.168.88.27:8081 +# } + + +# ============================================================================= +# Services to protect with forward auth (uncomment after Authentik is running): +# ============================================================================= +# Add "import authentik_forward_auth" to these site blocks: +# - dashy.jgitta.com (:8081) +# - search.jgitta.com (:8092) +# - beszel.jgitta.com (:8085) +# - status.jgitta.com (:3001) +# - glances.jgitta.com (:61208) +# - budget.jgitta.com (:5006) +# - apache.jgitta.com (:8080) — Guacamole (also has native OIDC extension) diff --git a/authentik/docker-compose.yml b/authentik/docker-compose.yml new file mode 100644 index 0000000..fa5d18a --- /dev/null +++ b/authentik/docker-compose.yml @@ -0,0 +1,92 @@ +version: "3.4" + +# Authentik — Identity Provider / SSO +# Host: siklos (192.168.88.27) +# Web UI: https://auth.jgitta.com (Caddy → port 9011) +# Data: /srv/docker/authentik/ + +services: + + postgresql: + image: docker.io/library/postgres:16-alpine + container_name: authentik-postgres + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "pg_isready -d $${POSTGRES_DB} -U $${POSTGRES_USER}"] + start_period: 20s + interval: 30s + retries: 5 + timeout: 5s + volumes: + - /srv/docker/authentik/postgres:/var/lib/postgresql/data + environment: + POSTGRES_PASSWORD: ${PG_PASS} + POSTGRES_USER: ${PG_USER:-authentik} + POSTGRES_DB: ${PG_DB:-authentik} + + redis: + image: docker.io/library/redis:alpine + container_name: authentik-redis + command: --save 60 1 --loglevel warning + restart: unless-stopped + healthcheck: + test: ["CMD-SHELL", "redis-cli ping | grep PONG"] + start_period: 20s + interval: 30s + retries: 5 + timeout: 3s + volumes: + - /srv/docker/authentik/redis:/data + + server: + image: ghcr.io/goauthentik/server:latest + container_name: authentik-server + restart: unless-stopped + command: server + environment: + AUTHENTIK_REDIS__HOST: redis + AUTHENTIK_POSTGRESQL__HOST: postgresql + AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik} + AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik} + AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} + AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} + AUTHENTIK_ERROR_REPORTING__ENABLED: "false" + # Set this to your external URL — Authentik uses it for redirects + AUTHENTIK_AUTHENTIK__DEFAULT_TOKEN_DURATION: "days=30" + volumes: + - /srv/docker/authentik/media:/media + - /srv/docker/authentik/custom-templates:/templates + ports: + # Host port 9011 avoids conflict with Portainer's 9000 + - "9011:9000" + depends_on: + postgresql: + condition: service_healthy + redis: + condition: service_healthy + + worker: + image: ghcr.io/goauthentik/server:latest + container_name: authentik-worker + restart: unless-stopped + command: worker + environment: + AUTHENTIK_REDIS__HOST: redis + AUTHENTIK_POSTGRESQL__HOST: postgresql + AUTHENTIK_POSTGRESQL__USER: ${PG_USER:-authentik} + AUTHENTIK_POSTGRESQL__NAME: ${PG_DB:-authentik} + AUTHENTIK_POSTGRESQL__PASSWORD: ${PG_PASS} + AUTHENTIK_SECRET_KEY: ${AUTHENTIK_SECRET_KEY} + AUTHENTIK_ERROR_REPORTING__ENABLED: "false" + # Worker needs root to manage Docker socket for outpost deployments + user: root + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - /srv/docker/authentik/media:/media + - /srv/docker/authentik/certs:/certs + - /srv/docker/authentik/custom-templates:/templates + depends_on: + postgresql: + condition: service_healthy + redis: + condition: service_healthy diff --git a/cgitta-nextcloud-zip-analysis.md b/cgitta-nextcloud-zip-analysis.md deleted file mode 100644 index f2104be..0000000 --- a/cgitta-nextcloud-zip-analysis.md +++ /dev/null @@ -1,64 +0,0 @@ -# cgitta Nextcloud – Storage Cleanup (Completed 2026-05-09) - -**Starting size:** 418 GB -**Final size:** ~26 GB -**Total freed:** ~392 GB - ---- - -## What Was Done - -### 1. Corrupt / Unreadable Archives — Deleted (82.9 GB) -Both were first-segment-only multi-part zip archives with missing parts — completely unreadable. - -| File | Size | Action | -|------|------|--------| -| `Documents.zip` | 75 GB | Deleted — content already in Documents/ folder | -| `OneDrive-2024-10-31.zip` | 7.9 GB | Deleted — unreadable, content verified elsewhere | - -### 2. Google Takeout Zips — Deleted (75 GB) -8 takeout zip files from Feb 2023 export. Zip 001 (Google Photos/Keep/Drive/Contacts) and Zip 008 (Gmail mbox) were already fully extracted. Zips 002–007 (Google Photos videos) were extracted then deleted. - -All 8 zips removed after confirming extraction. - -### 3. Duplicate Zips — Deleted (~293 MB) -- 9 exact duplicate `(1)`/`(2)` browser re-downloads removed -- `Photos.zip` and `Photos-001.zip` confirmed duplicated in Documents/Photos/ and removed - -### 4. Gmail Backup -- Extracted `gmail-backup.tar.gz` (30 GB tar → 3.1 GB mbox) to `Backup Files/Gmail/` -- Deleted the original tar - -### 5. Google Drive Content Consolidated -- Drive folders from 7 Takeout sub-archives merged into `Documents/Google Drive/` (4.2 GB) -- Duplicate files across takeout extracts deduplicated with fdupes - -### 6. Photos Migrated to Immich (69 GB freed from Nextcloud) -- All 53,299 files (69 GB) from `cgitta/files/Photos/` rsynced to photos VM staging area -- fdupes comparison against Joe's Immich library (`/mnt/immich-photos/photos/joe/`): - - 3,243 files already in Joe's library → deleted - - 1,540 internal duplicates → deleted - - **48,676 unique files (55 GB) → moved to `/mnt/immich-photos/photos/cynthia/`** -- Immich external library rescanned — photos indexed under Cynthia's account -- `Photos/` folder deleted from Nextcloud - ---- - -## Final Nextcloud State (cgitta account) - -| Folder | Size | -|--------|------| -| Documents | 18 GB | -| zip drives (teaching materials) | 2.6 GB | -| Video | 463 MB | -| music | 2.6 MB | -| **Total** | **~26 GB** | - -The `zip drives` folder (~2.6 GB) contains ~100 Teachers Pay Teachers curriculum zips — left intact for Cynthia to review. - ---- - -## Notes -- Nextcloud `occ files:scan` run twice during cleanup — final scan: 1,612 folders / 36,306 files -- Immich external library ID: `54b6419e-eb04-47b0-a5bd-0ecdb52545d7` -- `id_nextcloud` SSH key on `next` VM is now authorized on `photos` VM for future transfers diff --git a/docs/jarvis-build-documentation.docx b/docs/jarvis-build-documentation.docx deleted file mode 100644 index acbcdf75c5dffc694390e23eb6411637dcde3b4b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 19977 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 c2i;Iyf?^8PDXidvNQyy/` on each host using **absolute paths**. @@ -120,7 +120,7 @@ All stack data volumes are stored at `/srv/docker//` on each host us |---|---|---|---|---| | Portainer | portainer/portainer-ce:latest | 9000, 9443 | portainer.jgitta.com | Docker management UI | | Pi-hole | pihole/pihole:latest | 53 (DNS), 8080 | pihole.jgitta.com | DNS ad blocker | -| Open WebUI | ghcr.io/open-webui/open-webui:main | 3000 | ai.jgitta.com | AI chat UI (connects to Ollama on jgpc) | +| Open WebUI | ghcr.io/open-webui/open-webui:main | 3000 | ai.jgitta.com | AI chat UI (connects to Ollama on ThinkStation) | | LiteLLM | litellm:latest | 4000 | — | Centralized LLM API gateway | | OnlyOffice | onlyoffice/documentserver | 8880 | office.jgitta.com | Online document editing (used by Nextcloud) | | Homarr | homarr:latest | 7575 | homarr.jgitta.com | Dashboard | @@ -164,7 +164,7 @@ Media storage is mounted at `/mnt/media` on the jellyfin host and presented as ` --- -### jgpc — 192.168.88.41 — AI / Workstation Host +### thinkstation-p710 — 192.168.88.41 — AI / Workstation Host | Service | Image | Port | Purpose | |---|---|---|---| @@ -174,20 +174,6 @@ Media storage is mounted at `/mnt/media` on the jellyfin host and presented as ` | Stirling PDF | frooodle/s-pdf:latest | 8081 | PDF tools web UI | | Portainer Agent | portainer/agent:latest | 9001 | Managed by siklos Portainer | -#### Printing (jgpc) - -The jgpc is the print server for the HP PageWide Color Flow MFP 586. - -| Item | Value | -|---|---| -| Printer | HP PageWide Color Flow MFP 586 | -| IP | 192.168.88.89 (static DHCP reservation, MAC: `ec:8e:b5:c0:d0:48`) | -| CUPS queue | `HP_586_MFP` (default) | -| Device URI | `ipps://192.168.88.89/ipp/print` | -| Driver | IPP Everywhere (driverless) | - -`cups-browsed` is **disabled** — it was auto-recreating an unstable mDNS-based queue whenever the printer was online. The printer is permanently saved in `/etc/cups/printers.conf` with a static IP URI. - --- ### photos VM — 192.168.88.32 (VM 113) — Photo Services @@ -204,7 +190,7 @@ Photo storage is on a local 200 GB SSD (`/mnt/immich-photos`) — migrated from ## Monitoring -Prometheus scrapes **Node Exporter** (port 9100) from every host: siklos, proxmox, Nextcloud, jellyfin, PBS, caddy, and jgpc. **Grafana** at `grafana.jgitta.com` visualizes all metrics and fires alerts (via Telegram) when: +Prometheus scrapes **Node Exporter** (port 9100) from every host: siklos, proxmox, Nextcloud, jellyfin, PBS, caddy, and ThinkStation. **Grafana** at `grafana.jgitta.com` visualizes all metrics and fires alerts (via Telegram) when: - RAM > 90% for 5 minutes - Swap > 50% for 5 minutes @@ -242,4 +228,3 @@ Prometheus scrapes **Node Exporter** (port 9100) from every host: siklos, proxmo | AI (Open WebUI) | https://ai.jgitta.com | | Guacamole | https://apache.jgitta.com | | Kopia | https://kopia.jgitta.com | - diff --git a/install-native-apps.sh b/install-native-apps.sh new file mode 100755 index 0000000..cfc63eb --- /dev/null +++ b/install-native-apps.sh @@ -0,0 +1,213 @@ +#!/bin/bash +# Replace Flatpak apps with native installs +# Migrates configs from Flatpak sandboxes to native locations +# Safe to re-run — won't overwrite existing native configs without prompting +set -e + +BACKUP="$HOME/flatpak-config-backup" +TMPDIR="$HOME/Downloads/native-installs" +mkdir -p "$TMPDIR" + +echo "============================================" +echo " Step 1: Install KeePassXC via apt" +echo "============================================" +sudo apt-get update -qq +sudo apt-get install -y keepassxc +echo "Done." + +echo "" +echo "============================================" +echo " Step 1b: Install Telegram (official tarball)" +echo "============================================" +echo "Downloading Telegram..." +wget -q --show-progress -O "$TMPDIR/telegram.tar.xz" \ + "https://telegram.org/dl/desktop/linux" +sudo mkdir -p /opt/telegram +sudo tar -xJf "$TMPDIR/telegram.tar.xz" -C /opt/telegram --strip-components=1 +sudo ln -sf /opt/telegram/Telegram /usr/local/bin/telegram + +mkdir -p ~/.local/share/applications +cat > ~/.local/share/applications/telegram-desktop.desktop << EOF +[Desktop Entry] +Name=Telegram Desktop +Comment=Official Telegram messaging app +Exec=/opt/telegram/Telegram -- %u +Icon=/opt/telegram/telegram.png +Terminal=false +Type=Application +Categories=Chat;Network;InstantMessaging; +MimeType=x-scheme-handler/tg; +StartupNotify=true +StartupWMClass=TelegramDesktop +EOF + +update-desktop-database ~/.local/share/applications 2>/dev/null || true +echo "Telegram installed to /opt/telegram" + +echo "" +echo "============================================" +echo " Step 2: Download Obsidian .deb" +echo "============================================" +wget -q --show-progress -O "$TMPDIR/obsidian.deb" \ + "https://github.com/obsidianmd/obsidian-releases/releases/download/v1.12.7/obsidian_1.12.7_amd64.deb" +echo "Obsidian downloaded." + +echo "" +echo "============================================" +echo " Step 3: Download FreeTube .deb (latest)" +echo "============================================" +FREETUBE_URL=$(curl -s https://api.github.com/repos/FreeTubeApp/FreeTube/releases/latest \ + | grep -o '"browser_download_url": "[^"]*amd64\.deb"' \ + | head -1 | cut -d'"' -f4) + +if [ -z "$FREETUBE_URL" ]; then + echo "WARNING: Could not auto-detect FreeTube version. Check https://github.com/FreeTubeApp/FreeTube/releases" + echo "Download the *_amd64.deb file manually and run: sudo dpkg -i ~/Downloads/freetube_*.deb" +else + wget -q --show-progress -O "$TMPDIR/freetube.deb" "$FREETUBE_URL" + echo "FreeTube downloaded." +fi + +echo "" +echo "============================================" +echo " Step 4: Install .deb packages" +echo "============================================" +sudo dpkg -i "$TMPDIR/obsidian.deb" 2>/dev/null || true +[ -f "$TMPDIR/freetube.deb" ] && sudo dpkg -i "$TMPDIR/freetube.deb" 2>/dev/null || true +sudo apt-get install -f -y # fix any missing dependencies +echo "Done." + +echo "" +echo "============================================" +echo " Step 5: Install Zen Browser (native tar.xz)" +echo "============================================" +echo "Downloading Zen (this may take a minute — ~90MB)..." +wget -q --show-progress -O "$TMPDIR/zen.tar.xz" \ + "https://github.com/zen-browser/desktop/releases/latest/download/zen.linux-x86_64.tar.xz" + +sudo mkdir -p /opt/zen +sudo tar -xJf "$TMPDIR/zen.tar.xz" -C /opt/zen --strip-components=1 +sudo chmod +x /opt/zen/zen + +# Symlink so you can type 'zen' in the terminal +sudo ln -sf /opt/zen/zen /usr/local/bin/zen + +# Find the icon (location varies by version) +ZEN_ICON=$(find /opt/zen -name "default128.png" 2>/dev/null | head -1) +[ -z "$ZEN_ICON" ] && ZEN_ICON="/opt/zen/browser/chrome/icons/default/default128.png" + +# Create application menu entry +mkdir -p ~/.local/share/applications +cat > ~/.local/share/applications/zen-browser.desktop << EOF +[Desktop Entry] +Name=Zen Browser +Comment=Experience a calmer internet +Exec=/opt/zen/zen %u +Icon=$ZEN_ICON +Terminal=false +Type=Application +Categories=Network;WebBrowser; +MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https; +StartupNotify=true +StartupWMClass=zen-alpha +EOF + +update-desktop-database ~/.local/share/applications 2>/dev/null || true +echo "Zen installed to /opt/zen" + +echo "" +echo "============================================" +echo " Step 6: Migrate configs from Flatpak backups" +echo "============================================" + +# KeePassXC — settings (recent files, window layout, etc.) +if [ -d "$BACKUP/org.keepassxc.KeePassXC/config/keepassxc" ]; then + mkdir -p ~/.config/keepassxc + cp -rn "$BACKUP/org.keepassxc.KeePassXC/config/keepassxc/." ~/.config/keepassxc/ + echo "KeePassXC: config migrated (database stays at ~/passwords/kpdb.kdbx)" +else + echo "KeePassXC: no config found in backup, skipping" +fi + +# Telegram — tdata contains your login session (no need to re-scan QR code) +if [ -d "$BACKUP/org.telegram.desktop/data/TelegramDesktop" ]; then + mkdir -p ~/.local/share/TelegramDesktop + cp -rn "$BACKUP/org.telegram.desktop/data/TelegramDesktop/." ~/.local/share/TelegramDesktop/ + echo "Telegram: session data migrated (you should stay logged in)" +else + echo "Telegram: no data found in backup, you'll need to log in again" +fi + +# FreeTube — subscriptions, history, settings +if [ -d "$BACKUP/io.freetubeapp.FreeTube/config/FreeTube" ]; then + mkdir -p ~/.config/FreeTube + # Copy only the actual data files, skip caches + for item in "$BACKUP/io.freetubeapp.FreeTube/config/FreeTube/"*; do + name=$(basename "$item") + case "$name" in + Cache|"Code Cache"|GPUCache|DawnGraphiteCache|DawnWebGPUCache|Crashpad) + echo "FreeTube: skipping cache: $name" + ;; + *) + cp -rn "$item" ~/.config/FreeTube/ 2>/dev/null || true + ;; + esac + done + echo "FreeTube: config migrated (subscriptions + history preserved)" +else + echo "FreeTube: no config found in backup, skipping" +fi + +# Obsidian — app-level config (vault list, theme, plugins list) +if [ -d "$BACKUP/md.obsidian.Obsidian/config/obsidian" ]; then + mkdir -p ~/.config/obsidian + for item in "$BACKUP/md.obsidian.Obsidian/config/obsidian/"*; do + name=$(basename "$item") + case "$name" in + Cache|"Code Cache"|GPUCache|DawnGraphiteCache|DawnWebGPUCache|Crashpad) + echo "Obsidian: skipping cache: $name" + ;; + *) + cp -rn "$item" ~/.config/obsidian/ 2>/dev/null || true + ;; + esac + done + echo "Obsidian: config migrated (vaults are wherever you stored them, unchanged)" +else + echo "Obsidian: no config found in backup, skipping" +fi + +# Zen — full browser profile (bookmarks, extensions, history, passwords, settings) +if [ -d "$BACKUP/app.zen_browser.zen/.zen" ]; then + echo "Zen: migrating browser profile (bookmarks, extensions, history)..." + mkdir -p ~/.zen + cp -rn "$BACKUP/app.zen_browser.zen/.zen/." ~/.zen/ + echo "Zen: profile migrated" +else + echo "Zen: no profile found in backup, starting fresh" +fi + +echo "" +echo "============================================" +echo " Step 7: Remove Flatpak versions" +echo "============================================" +flatpak remove -y \ + app.zen_browser.zen \ + org.keepassxc.KeePassXC \ + md.obsidian.Obsidian \ + org.telegram.desktop \ + io.freetubeapp.FreeTube +echo "Flatpaks removed." + +echo "" +echo "============================================" +echo " All done!" +echo "============================================" +echo "" +echo "Summary:" +echo " KeePass database : ~/passwords/kpdb.kdbx (unchanged)" +echo " Config backup : ~/flatpak-config-backup/ (safe to delete later)" +echo " Downloaded debs : ~/Downloads/native-installs/ (safe to delete)" +echo " Zen installed to : /opt/zen" +echo "" +echo "Next: open Zen and re-run 'firefoxpwa profile patch' to fix PWAs" diff --git a/jellyfin/node-exporter/docker-compose.yml b/jellyfin/node-exporter/docker-compose.yml new file mode 100644 index 0000000..3f77805 --- /dev/null +++ b/jellyfin/node-exporter/docker-compose.yml @@ -0,0 +1,15 @@ +services: + node-exporter: + image: prom/node-exporter:latest + container_name: node-exporter + restart: unless-stopped + network_mode: host + pid: host + volumes: + - /proc:/host/proc:ro + - /sys:/host/sys:ro + - /:/rootfs:ro + command: + - '--path.procfs=/host/proc' + - '--path.sysfs=/host/sys' + - '--collector.filesystem.mount-points-exclude=^/(sys|proc|dev|host|etc)($$|/)' diff --git a/jgpc/frigate/frigate-cctv-setup.md b/jgpc/frigate/frigate-cctv-setup.md deleted file mode 100644 index de65600..0000000 --- a/jgpc/frigate/frigate-cctv-setup.md +++ /dev/null @@ -1,176 +0,0 @@ -# Frigate NVR & CCTV Setup - -**Last Updated:** July 2026 -**Host:** jgpc — `192.168.88.41` - ---- - -## Hardware - -| Component | Details | -|-----------|---------| -| Machine | Lenovo jgpc P710 | -| CPU | Intel Xeon E5-2623 v4 @ 2.60 GHz (4 cores / 8 threads, boost to 3.2 GHz) | -| RAM | 24 GB | -| GPU | NVIDIA GeForce GTX 1050 Ti (4 GB VRAM) | -| OS Disk | 234 GB SSD (`/dev/sda2`) | -| Media Storage | 14 TB HDD — mounted at `/mnt/14TB-HD/frigate/media` | - ---- - -## Frigate Docker Setup - -| Item | Value | -|------|-------| -| Image | `ghcr.io/blakeblackshear/frigate:stable-tensorrt` | -| Container name | `frigate` | -| Config path | `/srv/docker/frigate/config/` | -| Media path | `/mnt/14TB-HD/frigate/media` | -| Compose file | `jgpc/frigate/docker-compose.yml` (this repo) | -| Web UI | `http://192.168.88.41:5000` → `https://cameras.jgitta.com` | -| RTSP port | `8554` | -| WebRTC port | `8555` | - -**GPU acceleration:** Container runs with `runtime: nvidia` and `NVIDIA_VISIBLE_DEVICES=all`. The GTX 1050 Ti handles: -- LPR (licence plate recognition) via GPU -- Birdseye re-stream encoding via `h264_nvenc` -- ONNX object detection via the `onnx` detector - -**Shared memory:** `shm_size: 1024mb` — required for frame buffers across 6 cameras. - ---- - -## Object Detection - -| Item | Value | -|------|-------| -| Detector | ONNX (GPU-accelerated) | -| Model | YOLOv9-t-320 (`yolov9-t-320.onnx`) | -| Input size | 320 × 320 | -| Label map | COCO-80 | -| Model path | `/config/model_cache/onnx/yolov9-t-320.onnx` | - ---- - -## Licence Plate Recognition (LPR) - -| Item | Value | -|------|-------| -| Enabled | Yes | -| Device | GPU | -| Model size | Small | - -**Known plates:** - -| Name | Plate | -|------|-------| -| Joe | ABC1234 | -| Cynthia | ABC4567 | -| Wesley | XYZ9876 | - ---- - -## Camera Inventory - -All cameras are **Reolink** PoE units on the `192.168.88.0/24` subnet. - -| Camera | IP | Model notes | Mount location | -|--------|----|-------------|----------------| -| front_driveway | 192.168.88.105 | Dual-lens panoramic (7680×2160 = two 4K sensors stitched) | Front driveway | -| front_yard | 192.168.88.104 | 4K 811A | Front yard | -| shop | 192.168.88.103 | 4K 811A (uses HTTPS for API) | Shop / outbuilding | -| deck | 192.168.88.101 | 4K 811A | Deck | -| back_yard | 192.168.88.102 | 4K 811A | Back yard | -| front_right | 192.168.88.100 | 4512×2512 sensor (non-standard resolution) | Front right | - ---- - -## Camera Stream Settings - -All cameras confirmed via Reolink API (May 2026): - -| Camera | Main Resolution | Main Codec | Bitrate | FPS | I-frame | Sub Resolution | Sub Codec | -|--------|----------------|------------|---------|-----|---------|----------------|-----------| -| front_driveway (.105) | 7680×2160 | H.265 | 10240 Kbps | 20 | 1x | 1536×432 | H.264 | -| front_yard (.104) | 3840×2160 | H.265 | 6144 Kbps | 25 | 1x | 640×360 | H.264 | -| shop (.103) | 3840×2160 | H.265 | 6144 Kbps | 25 | 1x | 640×360 | H.264 | -| deck (.101) | 3840×2160 | H.265 | 6144 Kbps | 25 | 1x | 640×360 | H.264 | -| back_yard (.102) | 3840×2160 | H.265 | 6144 Kbps | 25 | 1x | 640×360 | H.264 | -| front_right (.100) | 4512×2512 | H.265 | 8192 Kbps | 20 | 1x | 896×512 | H.264 | - -**I-frame interval** set to 1x on all cameras so Frigate can start/end recording clips cleanly without waiting for a keyframe. - ---- - -## go2rtc Stream Architecture - -Chrome/Firefox cannot play H.265 (HEVC) via MSE (Media Source Extensions), so each camera requires two go2rtc streams: - -``` -camera_h265 → native H.265 RTSP from camera - └─ consumed by Frigate ffmpeg for RECORDING (stream copy, no re-encode) - -camera → ffmpeg:camera_h265#video=h264 (software H.265 → H.264 transcode) - └─ served to browser via MSE WebSocket for LIVE VIEW - -camera_sub → native low-res H.264 RTSP sub-stream - └─ consumed by Frigate for OBJECT DETECTION only -``` - -**Key behaviour notes:** -- go2rtc uses **lazy initialization** — transcoders only start when a browser opens the live view, and shut down ~30 seconds after the last viewer leaves. This causes a 2–3 second gray screen on first view, which is expected and unavoidable. -- The `_h265` streams are kept alive continuously by Frigate's recording ffmpeg processes, so recording starts instantly. -- The `#hardware` flag for NVENC transcoding in go2rtc causes EOF crashes with Reolink cameras and **must not be used**. Software transcoding (`libx264`) is used instead. -- Reolink cameras drop RTSP connections every 5–20 minutes (firmware behaviour). go2rtc reconnects automatically; this produces a brief gray screen, not static. - -### shop camera note -The shop camera (`192.168.88.103`) redirects HTTP → HTTPS. All API calls to shop must use `https://` with `-k` (skip cert verification). - ---- - -## Frigate Camera Detection Zones - -| Camera | Detect Resolution | Tracked Objects | Notes | -|--------|------------------|-----------------|-------| -| front_driveway | 1536×432 | person, car, dog, cat, license_plate | Has zone + LPR; alerts require `front_driveway_zone` | -| front_yard | 640×360 | person, car, dog, cat | — | -| shop | 640×360 | person, car, dog, cat | Motion mask on upper-right and mid-frame | -| deck | 640×360 | person, car, dog, cat | Two motion masks | -| back_yard | 640×360 | person, car, dog, cat | Two motion masks | -| front_right | 896×512 | person, car, dog, cat | Motion mask on upper-centre | - ---- - -## Recording & Retention - -| Type | Retention | -|------|-----------| -| Alert clips | 30 days | -| Detection clips | 30 days | -| Motion clips | 30 days | -| Continuous | Disabled | -| Snapshots | 30 days | - ---- - -## Known Issues & Quirks - -- **Reolink H.265 sub-stream firmware bug:** On some Reolink firmware versions, setting the sub-stream to H.265 produces a corrupted bitstream that go2rtc rejects with 404. Sub-streams should always remain H.264. -- **front_driveway panoramic:** The 7680×2160 resolution is two 4K sensors stitched side by side. The wide 1536×432 detect resolution in the Frigate config accounts for this. -- **front_right non-standard resolution:** Camera reports 4512×2512 rather than standard 4K. Model unknown — may be a 5MP variant or older sensor. -- **Swap pressure:** jgpc runs ~5.5 GB swap used under normal load. Monitor if adding more cameras or enabling Birdseye. - ---- - -## Changelog - -### 2026-07-07 — Live view fixes, audio, and two-way talk - -- **Green-box fix (front_driveway High stream):** the exec transcode crash-looped because Reolink RTSP emits corrupt HEVC frames at stream-join ("PPS changed between slices"); ffmpeg fell back to software decode and the GPU-only `scale_cuda` filter failed. Replaced with `-hwaccel cuvid -c:v hevc_cuvid -resize 3840x1080` — decode + resize stay on the GPU, no filter graph. (Also: NVENC caps H.264 at 4096 px wide, so the 7680×2160 Duo 3 main stream must be scaled before encode.) -- **front_right:** main stream is native H.264 2560×1440 — replaced NVENC transcode with direct passthrough (frees a GPU encode session). Live label renamed "High (Native)". -- **Live stream selectors:** added `live: streams:` (High/Low) to all 6 cameras to pin stream choice per device. -- **Audio:** enabled on-camera mic audio for front_driveway (Duo 3) and shop (both were off in camera firmware); front_driveway transcode now passes AAC (`-c:a copy` instead of `-an`). -- **Two-way talk (all 6 cameras):** each live go2rtc stream now has 3 sources — primary video, `ffmpeg:#audio=opus` (WebRTC audio), and `onvif://:8000` (backchannel). Added `webrtc: candidates: [192.168.88.41:8555, stun:8555]`. LAN-only unless 8555 tcp/udp is forwarded. -- **LPR:** removed `license_plate` from front_driveway tracked objects — the COCO-80 model has no such label (startup warning); LPR runs via Frigate's dedicated plate detector on `car` objects. -- **Camera-side:** Duo 3 interframe space set to 1x (gop 1) per Frigate live-view docs for faster stream startup/recovery. -- Full camera hardware specs: see `frigate-cameras.md` (this folder). diff --git a/kopia-restore-guide.md b/kopia-restore-guide.md deleted file mode 100644 index 33946ca..0000000 --- a/kopia-restore-guide.md +++ /dev/null @@ -1,153 +0,0 @@ -# Kopia Restore Guide - -**Last Updated**: May 8, 2026 -**Applies To**: Both Kopia repositories on siklos (192.168.88.27) - ---- - -## ⚠️ Critical: What You Need to Restore - -You need **two things** to restore from Backblaze B2. Without either one, data is unrecoverable: - -1. **Repository password** — stored in `credentials.md` -2. **Backblaze B2 account access** — credentials in `credentials.md` - -> **Do not store these only on siklos.** Keep them in a password manager or printed copy somewhere safe. If siklos dies and you haven't saved the password elsewhere, your backup is unreadable. - ---- - -## 📦 Your Two Repositories - -| Container | B2 Bucket | What It Backs Up | Port | -|-----------|-----------|-----------------|------| -| `Kopia` | jg-kopia-nextcloud | Nextcloud files + database | 51515 | -| `Kopia-Immich` | jg-kopia-immich | Immich photos + database | 51516 | - ---- - -## 🔍 Step 1 — List Available Snapshots - -SSH to siklos first: -```bash -ssh jgitta@siklos -``` - -**Nextcloud snapshots:** -```bash -docker exec -e KOPIA_PASSWORD='' Kopia kopia snapshot list -``` - -**Immich snapshots:** -```bash -docker exec -e KOPIA_PASSWORD='' Kopia-Immich kopia snapshot list -``` - -Each snapshot has an ID like `k8adffae645082989d7c0f4f051439332` and a timestamp. You can restore from any snapshot in history — not just the latest. - ---- - -## 📁 Step 2 — Browse a Snapshot (Find Specific Files) - -You can explore a snapshot like a folder before restoring anything: - -```bash -# List the top-level contents of a snapshot -docker exec -e KOPIA_PASSWORD='' Kopia kopia ls - -# Drill into a subfolder -docker exec -e KOPIA_PASSWORD='' Kopia kopia ls /jgitta/files/Documents - -# Search for a specific filename across a snapshot -docker exec -e KOPIA_PASSWORD='' Kopia kopia find --name "*.jpg" -``` - -This lets you confirm exactly what you want to restore before doing it. - ---- - -## ♻️ Step 3 — Restore Options - -### Restore a Single File -```bash -docker exec -e KOPIA_PASSWORD='' Kopia kopia restore \ - /path/to/filename.jpg \ - /tmp/restore/filename.jpg -``` - -### Restore a Specific Folder -```bash -docker exec -e KOPIA_PASSWORD='' Kopia kopia restore \ - /jgitta/files/Photos \ - /tmp/restore/Photos/ -``` - -### Restore an Entire Snapshot -```bash -docker exec -e KOPIA_PASSWORD='' Kopia kopia restore \ - \ - /tmp/restore/ -``` - -> **Note:** Restored files land on siklos at `/tmp/restore/`. From there you can copy them to Nextcloud, Immich, or anywhere else via `scp` or `rsync`. - ---- - -## 🚨 Emergency: Siklos Is Gone — Restore from Scratch - -If siklos itself is dead, you can restore to any Linux machine: - -### 1. Install Kopia -```bash -curl -s https://kopia.io/signing-key | sudo gpg --dearmor -o /usr/share/keyrings/kopia-keyring.gpg -echo "deb [signed-by=/usr/share/keyrings/kopia-keyring.gpg] http://packages.kopia.io/apt/ stable main" | sudo tee /etc/apt/sources.list.d/kopia.list -sudo apt update && sudo apt install kopia -``` - -### 2. Connect to Your B2 Repository - -**For Nextcloud backup:** -```bash -kopia repository connect b2 \ - --bucket=jg-kopia-nextcloud \ - --key-id=00522b2471e5f090000000003 \ - --key=K005wv6tlgknxkM9arOJV/tMeOInOQY \ - --password= -``` - -**For Immich backup:** -```bash -kopia repository connect b2 \ - --bucket=jg-kopia-immich \ - --key-id=00522b2471e5f090000000004 \ - --key=K005gSepxpWk9hWx/MjWnCKtOiNuKnQ \ - --password= -``` - -### 3. List and Restore -```bash -kopia snapshot list -kopia restore /path/to/restore/ -``` - -> The B2 key IDs and application keys above are in `credentials.md`. Replace `` with the password from `credentials.md`. - ---- - -## 💡 Key Points - -- **Backblaze "Browse Files"** shows encrypted chunks — this is normal and expected. Files are not human-readable in B2 directly. -- **Every snapshot is independent** — you can restore from last week's snapshot even if today's is corrupted. -- **Partial restores are fully supported** — restore one file, one folder, or everything. -- **Kopia is open source** — even if the project were abandoned, you can always download the binary from GitHub and reconnect to your B2 repository as long as you have your password. - ---- - -## 📋 Restore Checklist - -- [ ] Have repository password (from `credentials.md`) -- [ ] Have B2 credentials (from `credentials.md`) -- [ ] SSH to siklos (or install Kopia on alternate machine) -- [ ] Run `snapshot list` to find correct snapshot by date -- [ ] Use `kopia ls` to browse and confirm the right files -- [ ] Run `kopia restore` with specific path for targeted restore -- [ ] Copy restored files to final destination diff --git a/nextcloud-to-ocis-migration-plan.md b/nextcloud-to-ocis-migration-plan.md new file mode 100644 index 0000000..f853fff --- /dev/null +++ b/nextcloud-to-ocis-migration-plan.md @@ -0,0 +1,260 @@ +# Nextcloud → ownCloud Infinite Scale (OCIS) Migration + +**Date:** 2026-06-09 +**Status:** 🟡 Migration in progress — data transfer running, cutover pending +**Author:** Joe Gitta + +--- + +## What Was Done (Summary) + +VM 114 (`ocis`, 192.168.88.63) was created on Proxmox and OCIS v8.0.4 was deployed as a native binary (systemd service). Both Nextcloud users (`jgitta` 89 GB / `cgitta` 210 GB) were recreated in OCIS and rclone migrations launched. Prometheus monitoring was added. Nextcloud (VM 103) remains running — no cutover yet. + +--- + +## Deployed Architecture + +``` +Internet / LAN + │ + ▼ +Caddy (192.168.88.110) + │ + ├── next.jgitta.com → VM 103 (Nextcloud, 192.168.88.62) ← still running + │ + └── cloud.jgitta.com → VM 114 (OCIS, 192.168.88.63:9200) + │ + └── Local SSD: /mnt/ocis-data (500 GB, Proxmox SSD-2) +``` + +--- + +## VM 114 (ocis) Specs + +| Item | Value | +|---|---| +| VM ID | 114 | +| VM Name | ocis | +| IP | 192.168.88.63 | +| SSH | `ssh jgitta@192.168.88.63` | +| vCPU | 4 cores | +| RAM | 8 GB | +| OS Disk | 64 GB (local SSD) | +| Data Disk | 500 GB SSD (Proxmox SSD-2) → `/mnt/ocis-data` | +| OS | Debian 13 (cloned from VM 9000 template) | +| OCIS Version | 8.0.4 | +| Binary | `/usr/local/bin/ocis` | +| Service | `systemctl {start|stop|restart|status} ocis` | +| Config | `/mnt/ocis-data/config/ocis.yaml` (auto-generated on first run) | +| Env file | `/mnt/ocis-data/config/ocis.env` | +| Systemd unit | `/etc/systemd/system/ocis.service` | + +**Note:** Local disk was chosen over TrueNAS NFS so that PBS full-VM backups capture both OS and data together, providing an extra layer of redundancy. + +--- + +## OCIS Configuration + +`/mnt/ocis-data/config/ocis.env`: +``` +OCIS_URL=https://cloud.jgitta.com +OCIS_BASE_DATA_PATH=/mnt/ocis-data +OCIS_CONFIG_DIR=/mnt/ocis-data/config +PROXY_TLS=false +OCIS_LOG_LEVEL=info +OCIS_LOG_COLOR=false +PROXY_ENABLE_BASIC_AUTH=true +``` + +> **Why `PROXY_ENABLE_BASIC_AUTH=true`?** OCIS v8 disables basic auth by default (OIDC only). This flag re-enables it so that rclone WebDAV and desktop/mobile sync clients can authenticate with username + password. + +--- + +## Accounts + +See `ocis-credentials.md` for full credential details. + +| Username | Display Name | Use | +|---|---|---| +| admin | Admin | Administration only | +| jgitta | Joe Gitta | Primary account | +| cgitta | Cynthia Gitta | Secondary account | + +Accounts were created via direct LDAP manipulation (OCIS v8 has no CLI user-create tool). The libregraph LDAP sysuser (`uid=libregraph,ou=sysusers,o=libregraph-idm`) was used for write access; passwords set with `ldappasswd` over LDAPS on `127.0.0.1:9235`. + +--- + +## Data Migration Status + +rclone is copying files directly from Nextcloud's filesystem on VM 103 (avoiding WebDAV overhead) and pushing to OCIS via WebDAV. Both processes run as background jobs on VM 103. + +```bash +# Monitor from Nextcloud VM (192.168.88.62): +sudo tail -f /tmp/rclone-jgitta.log +sudo tail -f /tmp/rclone-cgitta.log + +# Check if still running: +ps aux | grep rclone | grep -v grep +``` + +| User | Source | Data | Status | +|---|---|---|---| +| jgitta | `/mnt/nextcloud-data/jgitta/files/` | ~35 GB | 🔄 In progress | +| cgitta | `/mnt/nextcloud-data/cgitta/files/` | ~210 GB | 🔄 In progress (ETA many hours) | + +**Expected errors:** Some files show `Failed to copy: object not found` — these are files present in Nextcloud's database but missing from disk (DB/filesystem desync in Nextcloud). Non-fatal; rclone skips and continues. + +### rclone Config on VM 103 + +`/root/.config/rclone/rclone.conf` contains two WebDAV remotes: +- `ocis_jgitta` → `https://cloud.jgitta.com/remote.php/dav/files/jgitta/` +- `ocis_cgitta` → `https://cloud.jgitta.com/remote.php/dav/files/cgitta/` + +### Re-run Migration (if needed) + +```bash +# SSH to Nextcloud VM +ssh jgitta@192.168.88.62 + +# Re-run jgitta (rclone copy is idempotent — safe to re-run) +sudo nohup rclone copy /mnt/nextcloud-data/jgitta/files/ ocis_jgitta: \ + --transfers 4 --checkers 8 \ + --log-file /tmp/rclone-jgitta.log --log-level INFO --stats 120s & + +# Re-run cgitta +sudo nohup rclone copy /mnt/nextcloud-data/cgitta/files/ ocis_cgitta: \ + --transfers 4 --checkers 8 \ + --log-file /tmp/rclone-cgitta.log --log-level INFO --stats 120s & +``` + +--- + +## Caddy Config + +Added to `/etc/caddy/sites/media.caddy` on Caddy VM (192.168.88.110): + +```caddy +cloud.jgitta.com { + import web_secure + reverse_proxy 192.168.88.63:9200 { + import proxy_timeouts + } +} +``` + +--- + +## Monitoring + +- **node-exporter** running on OCIS VM as Docker container, port **9101** (port 9100 is taken by OCIS itself) +- **Prometheus** on siklos scrapes `192.168.88.63:9101`, labeled as instance `ocis` +- PBS backup job `backup-5d4b43bd-7988` uses `all=1` — VM 114 is automatically included, no changes needed + +--- + +## Pending Steps (Manual) + +### 1. Add Cloudflare DNS record ⚠️ REQUIRED for external access + +Log into Cloudflare dashboard → jgitta.com DNS zone → Add A record: +- **Name:** `cloud` +- **Content:** your WAN IP +- **Proxy:** Grey cloud (DNS only) + +Internal access via `cloud.jgitta.com` works already (MikroTik wildcard → Caddy). + +### 2. Verify files after migration completes + +Once rclone logs show completion: +1. Log into https://cloud.jgitta.com as `jgitta` and `cgitta` +2. Spot-check files in the web UI — confirm folder structure looks right +3. Check error count in logs: `sudo grep ERROR /tmp/rclone-jgitta.log | wc -l` + +### 3. Change default passwords + +Log into https://cloud.jgitta.com and change passwords for both accounts (see `ocis-credentials.md` for current values). + +### 4. Reconnect desktop/mobile sync clients + +- Desktop: Add account pointing to `https://cloud.jgitta.com` in ownCloud or Nextcloud desktop client +- Mobile: Add account in ownCloud app (iOS/Android) — same URL + +### 5. OnlyOffice integration (optional, can defer) + +OCIS uses WOPI for document editing. The existing OnlyOffice at `https://office.jgitta.com` supports WOPI. +In OCIS Admin Settings → Apps, configure the WOPI server URL. + +--- + +## Cutover Procedure (when ready) + +Once files are verified and clients reconnected: + +1. **Enable Nextcloud maintenance mode** (prevents new uploads): + ```bash + ssh jgitta@192.168.88.62 + sudo -u www-data php /var/www/nextcloud/occ maintenance:mode --on + ``` + +2. **Run a final rclone sync** to catch any files changed since the initial copy: + ```bash + sudo rclone copy /mnt/nextcloud-data/jgitta/files/ ocis_jgitta: --transfers 4 --stats-log-level NOTICE + sudo rclone copy /mnt/nextcloud-data/cgitta/files/ ocis_cgitta: --transfers 4 --stats-log-level NOTICE + ``` + +3. **Redirect Caddy** — update `next.jgitta.com` in Caddy to redirect to OCIS: + ```caddy + next.jgitta.com { + redir https://cloud.jgitta.com{uri} permanent + } + ``` + +4. **Stop VM 103** in Proxmox — do NOT delete it yet + +5. **After 4 weeks with no issues** — delete VM 103 + +--- + +## Service Management Quick Reference + +```bash +# OCIS status / logs +ssh jgitta@192.168.88.63 "sudo systemctl status ocis" +ssh jgitta@192.168.88.63 "sudo journalctl -u ocis -f" + +# Restart OCIS +ssh jgitta@192.168.88.63 "sudo systemctl restart ocis" + +# Check migration logs (run on Nextcloud VM) +ssh jgitta@192.168.88.62 "sudo tail -50 /tmp/rclone-jgitta.log" +ssh jgitta@192.168.88.62 "sudo tail -50 /tmp/rclone-cgitta.log" + +# Check OCIS disk usage +ssh jgitta@192.168.88.63 "df -h /mnt/ocis-data" +``` + +--- + +## Checklist + +- [x] VM 114 created on Proxmox (Debian 13, 4 vCPU, 8 GB RAM) +- [x] Static IP 192.168.88.63 configured +- [x] 500 GB data disk partitioned and mounted at `/mnt/ocis-data` +- [x] OCIS v8.0.4 binary installed at `/usr/local/bin/ocis` +- [x] OCIS systemd service configured and running +- [x] `cloud.jgitta.com` added to Caddy config and reloaded +- [x] OCIS users created: `admin`, `jgitta`, `cgitta` +- [x] rclone configured on VM 103 with both user remotes +- [x] Data migration launched (background, running) +- [x] node-exporter deployed on VM 114 (port 9101) +- [x] Prometheus scraping VM 114 confirmed (`up`) +- [x] PBS backup covers VM 114 automatically (`all=1`) +- [ ] **Cloudflare DNS A record for `cloud`** ← add manually +- [ ] Verify files in OCIS web UI after migration completes +- [ ] Change passwords from defaults +- [ ] Reconnect desktop/mobile sync clients +- [ ] OnlyOffice WOPI integration (optional) +- [ ] Final sync + enable Nextcloud maintenance mode +- [ ] Redirect `next.jgitta.com` → `cloud.jgitta.com` in Caddy +- [ ] Stop VM 103 +- [ ] Delete VM 103 (after 4-week verification) diff --git a/ocis-migration-file-comparison-2026-06-22.md b/ocis-migration-file-comparison-2026-06-22.md new file mode 100644 index 0000000..59cf2a2 --- /dev/null +++ b/ocis-migration-file-comparison-2026-06-22.md @@ -0,0 +1,185 @@ +# Nextcloud → OCIS Migration File Comparison Report +**Date:** 2026-06-22 +**Source:** Nextcloud VM 103 (192.168.88.62) +**Destination:** OCIS VM 114 (cloud.jgitta.com) + +--- + +## Summary + +| User | NC Files | OCIS Files | Missing | Reorganized (safe) | Truly Absent | +|------|----------|------------|---------|-------------------|--------------| +| jgitta | 55,015 | 54,541 | 4,575 raw | 4,025 InstantUpload date-reorganized | ~1,021 | +| cgitta | 464 | 491 | 63 raw | 5 Nextcloud defaults | 58 | + +**"Breakfast Locations.md" status: ✅ PRESENT in OCIS at `Notes/General/Breakfast Locations.md`** +(Not visible yet because desktop sync client hasn't been reconnected.) + +--- + +## jgitta — Non-InstantUpload Missing Files (19) + +These are the files most worth recovering — documents and important files: + +1Business/Business/Chrome Passwords.ods +1Business/Business/Coloff.CDB +1Business/Business/Ninja/1P002227017-119291-1587748816ScheduleC119291-AMMEND.pdf +1Business/Business/Ninja/ALL - Copy.csv +1Business/Business/Ninja/DisableSleep.bat +1Business/Business/Ninja/DisableSleep - Copy.bat +1Business/Business/Ninja/jgbusinessservicesmainoffice-5.3.2695-installer - Copy.deb +1Business/Business/Ninja/ninjaUninstallScript - Copy.ps1 +1Business/Business/Ninja/ninjaUninstallScript.ps1 +1Business/Business/OS ISO Files/AlmaLinux-9-latest-x86_64-boot.iso +1Business/Business/OS ISO Files/Windows.iso +1Business/Business/RMS-License Sample - Copy.jpg +1Business/Business/RMS licenses - Copy.pdf +1Business/Business/RMS Product Keys.pdf +Personal/Automation/Coinbase API keys.html +Personal/Automation/Coinbase API keys.odt +Personal/House/Oakcrest/FC0011599_0052220021552.pdf +Personal/House/Oakcrest/WMMortgagorAffidavit filled in.pdf +Systems/Dell/Dell-OS-Recovery-Tool_WFFJR_WIN64_2.4.2.2193_A00.EXE + +--- + +## jgitta — Missing InstantUpload Photos (1,002 truly absent) + +These are phone camera uploads that exist in Nextcloud but have no matching filename anywhere in OCIS. +The filenames use UUID/timestamp format (e.g. `1579148769626-f5524435-....jpg`), suggesting they are from +an older era before the auto-date-folder organization was introduced. + +Full list saved separately. Sample (first 20): + +InstantUpload/Camera/1579148769626-f5524435-0370-46e6-aeac-9d123639a275.jpg +InstantUpload/Camera/1579148814788-c91a92a7-c620-4436-b125-2807801301a5.jpg +InstantUpload/Camera/1579148844698-cd639f3a-6b2a-435d-aa6e-fc17d84ebd99.jpg +InstantUpload/Camera/1579148913181-ce4c026e-ad67-454c-859f-190ef4edc604.jpg +InstantUpload/Camera/1579148963218-5e6351a0-0638-49d6-9ccb-cd9019dcfed6.jpg +InstantUpload/Camera/1579149042550-b680921f-0b29-4076-adb1-5ab886f7d000.jpg +InstantUpload/Camera/1579149078795-13a6fe2b-3d38-4cd9-83cf-eabee42756f9.jpg +InstantUpload/Camera/1579149136687-16520269-f499-4127-aa6a-feca7c92ea0c.jpg +InstantUpload/Camera/1579149162662-81d1db7c-a7c6-4c35-b0ca-38d0678059a6.jpg +InstantUpload/Camera/1579149196301-0900112b-15d6-44f9-b242-d5c00bd5d0f3.jpg +InstantUpload/Camera/1608011136522~2.jpg +InstantUpload/Camera/1608011136522~3.jpg +InstantUpload/Camera/1608011136522.jpg +InstantUpload/Camera/20210715_120809.jpg +InstantUpload/Camera/20210715_120811.jpg +InstantUpload/Camera/20210805_213548.jpg +InstantUpload/Camera/20210805_230916.jpg +InstantUpload/Camera/20210805_230919.jpg +InstantUpload/Camera/20210805_230929.jpg +InstantUpload/Camera/20210805_230949.jpg + +--- + +## jgitta — InstantUpload Files Reorganized (4,025 — safe, not lost) + +These files exist in Nextcloud as flat files under `InstantUpload/Camera/` or `InstantUpload/BlueIris/` +but in OCIS they were placed into year/month subfolders (e.g. `InstantUpload/Camera/2019/2019-06/`). +They are NOT missing — just in a different path. + +--- + +## cgitta — Missing Files (58) + +Documents/4H/Photos/2010-2011/photos from phone April 2011 310.jpg +Documents/4H/Photos/2010-2011/photos from phone April 2011 315.jpg +Documents/4H/Photos/Oct 2008/October 010.jpg +Documents/4H/Photos/Sewing 2009/Sewing 2009 012.jpg +Documents/4H/Photos/Sewing 2009/Sewing 2009 015.jpg +Documents/4H/Photos/Sewing 2009/Sewing 2009 020.jpg +Documents/4H/Photos/Sewing 2009/Sewing 2009 034.jpg +Documents/4H/Photos/Sewing 2009/Sewing 2009 041.jpg +Documents/4H/Photos/Sewing 2009/Sewing 2009 042.jpg +Documents/4H/Photos/Sewing 2009/Sewing2009 044.jpg +Documents/4H/Photos/Sewing 2009/Sewing 2009 045.jpg +Documents/4H/Photos/Sewing 2009/Sewing 2009 046.jpg +Documents/4H/Photos/We piano Dec 2009 4H Officer induction 2010 001.jpg +Documents/4H/Photos/We piano Dec 2009 4H Officer induction 2010 003.jpg +Documents/4H/Photos/We piano Dec 2009 4H Officer induction 2010 007.jpg +Documents/4H/Photos/We piano Dec 2009 4H Officer induction 2010 008.jpg +Documents/4H/Photos/We piano Dec 2009 4H Officer induction 2010 010.jpg +Documents/4H/Photos/We piano Dec 2009 4H Officer induction 2010 011.jpg +Documents/4H/Photos/We piano Dec 2009 4H Officer induction 2010 012.jpg +Documents/4H/Photos/We piano Dec 2009 4H Officer induction 2010 014.jpg +Documents/New House/17-6-1__1433 oakcrest dr, waterloo.PDF +Documents/New House/Photos/20170410_131249.jpg +Documents/New House/Photos/20170410_131254.jpg +Documents/New House/Photos/20170410_131406.jpg +Documents/New House/Photos/20170410_131411.jpg +Documents/New House/Photos/20170410_131425.jpg +Documents/New House/Photos/20170410_131501.jpg +Documents/New House/Photos/20170410_131508.jpg +Documents/New House/Photos/20170410_131527.jpg +Documents/New House/Photos/20170410_131542.jpg +Documents/New House/Photos/20170410_131547.jpg +Documents/New House/Photos/20170410_131558.jpg +Documents/New House/Photos/20170410_131947.jpg +Documents/New House/Photos/20170410_132027.jpg +Documents/New House/Photos/20170410_132038.jpg +Documents/New House/Photos/20170410_132052.jpg +Documents/New House/Photos/20170410_132120.jpg +Documents/New House/Photos/20170410_132131.jpg +Documents/New House/Photos/20170410_132142.jpg +Documents/Personal photos/Copy of IMG_0016.JPG +Documents/Personal photos/DSC_3566.JPG +Documents/Personal photos/DSCF0145.JPG +Documents/Personal photos/goofy wes face.jpg +Documents/Personal photos/IMG_20150923_075400869.jpg +Documents/Personal photos/photos from phone April 2011 038.jpg +Documents/Personal photos/photos from phone April 2011 039.jpg +Documents/Personal photos/photos from phone April 2011 040.jpg +Documents/Personal photos/photos from phone April 2011 042.jpg +Documents/Personal photos/photos from phone April 2011 043.jpg +Documents/Personal photos/photos from phone April 2011 314.jpg +Documents/Personal photos/photos from phone April 2011 315.jpg +InstantUpload/Attachments (2)/DSC01195.JPG +InstantUpload/Attachments (2)/DSC01202.JPG +InstantUpload/Attachments (2)/DSC01238.JPG +InstantUpload/Screenshots/Screenshot_20251128-084130.Facebook.png +InstantUpload/Screenshots/Screenshot_20260315-173818.Chrome.png +Video/IMG_20151020_124250944_HDR.jpg +Video/IMG_3977.JPG + +--- + +## cgitta — Nextcloud Default Files (not migrated, not needed) + +These 5 files are Nextcloud's built-in welcome files — safe to ignore: +- Nextcloud intro.mp4 +- Nextcloud Manual.pdf +- Nextcloud.png +- Readme.md +- Reasons to use Nextcloud.pdf + +--- + +## Recommended Actions + +1. **Breakfast Locations.md** — Already in OCIS. Reconnect your desktop sync client to `https://cloud.jgitta.com` to see it. + +2. **jgitta — 19 non-InstantUpload files** — These are business docs and personal files that should be re-uploaded manually. + Priority items: `Personal/Automation/Coinbase API keys.*`, `1Business/Business/RMS Product Keys.pdf`, mortgage docs. + +3. **jgitta — 1,002 missing InstantUpload camera photos** — These are older photos (2020 era, UUID filenames). + Can be re-uploaded from Nextcloud via rclone re-run targeting just InstantUpload/Camera. + +4. **cgitta — 58 missing files** — All photos (4H, New House, personal). Can be re-uploaded via rclone re-run. + +--- + +## Re-run rclone for Missing Files (run on VM 103) + +```bash +# Re-sync jgitta InstantUpload only +sudo rclone copy /mnt/nextcloud-data/jgitta/files/InstantUpload/ ocis_jgitta:InstantUpload/ \ + --transfers 4 --log-file /tmp/rclone-resync-jgitta.log --log-level INFO + +# Re-sync cgitta Documents +sudo rclone copy /mnt/nextcloud-data/cgitta/files/ ocis_cgitta: \ + --transfers 4 --log-file /tmp/rclone-resync-cgitta.log --log-level INFO +``` + +Note: rclone copy is idempotent — already-transferred files are skipped. diff --git a/owncloud/README.md b/owncloud/README.md deleted file mode 100644 index a198541..0000000 --- a/owncloud/README.md +++ /dev/null @@ -1,76 +0,0 @@ -# ownCloud Photo Organizer - -## Overview - -ownCloud's Android app does not support automatic year/month subfolder creation -during instant upload (unlike Nextcloud). This script compensates by running -nightly and sorting all uploaded photos and videos into a `YEAR/YEAR-MM/` -folder structure. - -## Script - -**Location on server:** `/usr/local/bin/organize_photos.sh` - -**Source folder watched:** -`/mnt/INTEL-SSD/ownCloud - Joe Gitta@cloud.jgitta.com/Personal/InstantUpload` - -**Subfolders processed:** `Camera`, `BlueIris`, `Facebook`, `scans`, `EufyVideoDir` - -**Supported file types:** jpg, jpeg, png, gif, heic, webp, bmp, tiff, mp4, mov, pdf - -## How It Works - -The script reads the date from each filename using these patterns (in order): - -1. `YYYYMMDD` embedded in the filename (e.g. `20231022_082107.jpg`) -2. `YYYY-MM-DD` or `YYYY_MM_DD` format -3. 13-digit Unix timestamp in milliseconds (e.g. `FB_IMG_1572815868839.jpg`) -4. `MM_DD_YYYY` prefix (scans folder format, e.g. `06_11_2026_7_28_16_AM.pdf`) - -Files that don't match any pattern are moved to `_unsorted/` for manual review. - -## Output Structure - -``` -InstantUpload/ -└── Camera/ - ├── 2023/ - │ ├── 2023-06/ - │ └── 2023-12/ - ├── 2024/ - │ └── 2024-03/ - └── _unsorted/ -``` - -## Cron Job - -Runs nightly at 2:00 AM as root: - -``` -0 2 * * * /usr/local/bin/organize_photos.sh -``` - -To view or edit: `sudo crontab -e` - -## Log File - -Each run appends to `/var/log/organize_photos.log`: - -``` -=== 2026-06-14 02:00:01 Starting === - Moved: 12 Unsorted: 0 Skipped: 0 Errors: 0 -=== Done === -``` - -Check the log anytime with: -```bash -cat /var/log/organize_photos.log -``` - -## Installation - -```bash -sudo cp organize_photos.sh /usr/local/bin/organize_photos.sh -sudo chmod +x /usr/local/bin/organize_photos.sh -(sudo crontab -l; echo "0 2 * * * /usr/local/bin/organize_photos.sh") | sudo crontab - -``` diff --git a/owncloud/organize_photos.sh b/owncloud/organize_photos.sh deleted file mode 100755 index 81e094a..0000000 --- a/owncloud/organize_photos.sh +++ /dev/null @@ -1,72 +0,0 @@ -#!/usr/bin/env bash - -BASE="/mnt/INTEL-SSD/ownCloud - Joe Gitta@cloud.jgitta.com/Personal/InstantUpload" -UNSORTED="$BASE/_unsorted" -LOG="/var/log/organize_photos.log" -moved=0; skipped=0; unsorted=0; errors=0 - -echo "=== $(date +%Y-%m-%d\ %H:%M:%S) Starting ===" >> "$LOG" - -ts_to_ym() { - local ts_ms="$1" - local ts_s=$(( ts_ms / 1000 )) - date -d "@$ts_s" "+%Y %m" 2>/dev/null -} - -get_ym() { - local f="$1" - local b - b=$(basename "$f") - if [[ "$b" =~ (^|[^0-9])([0-9]{4})(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01]) ]]; then - echo "${BASH_REMATCH[2]} ${BASH_REMATCH[3]}"; return - fi - if [[ "$b" =~ ([0-9]{4})[-_](0[1-9]|1[0-2])[-_](0[1-9]|[12][0-9]|3[01]) ]]; then - echo "${BASH_REMATCH[1]} ${BASH_REMATCH[2]}"; return - fi - if [[ "$b" =~ (^|[^0-9])([0-9]{13})([^0-9]|$) ]]; then - ts_to_ym "${BASH_REMATCH[2]}"; return - fi - if [[ "$b" =~ ^([0-9]{2})_([0-9]{2})_([0-9]{4})_ ]]; then - echo "${BASH_REMATCH[3]} ${BASH_REMATCH[1]}"; return - fi - echo "" -} - -move_file() { - local src="$1" - local root="$2" - local fname - fname=$(basename "$src") - local ym - ym=$(get_ym "$src") - if [[ -z "$ym" ]]; then - mkdir -p "$UNSORTED" - if [[ ! -e "$UNSORTED/$fname" ]]; then - mv "$src" "$UNSORTED/$fname" && ((unsorted++)) || ((errors++)) - else - ((skipped++)) - fi - return - fi - local year month dest_dir dest - year=$(echo "$ym" | cut -d' ' -f1) - month=$(echo "$ym" | cut -d' ' -f2) - dest_dir="$root/$year/$year-$month" - dest="$dest_dir/$fname" - mkdir -p "$dest_dir" - if [[ -e "$dest" ]]; then - ((skipped++)); return - fi - mv "$src" "$dest" && ((moved++)) || { echo " ERROR: $fname" >> "$LOG"; ((errors++)); } -} - -for SUBDIR in Camera BlueIris Facebook scans EufyVideoDir; do - SUBPATH="$BASE/$SUBDIR" - [[ -d "$SUBPATH" ]] || continue - while IFS= read -r -d '' f; do - move_file "$f" "$SUBPATH" - done < <(find "$SUBPATH" -maxdepth 1 -type f -print0) -done - -echo " Moved: $moved Unsorted: $unsorted Skipped: $skipped Errors: $errors" >> "$LOG" -echo "=== Done ===" >> "$LOG" diff --git a/siklos-docker-services.md b/siklos-docker-services.md index 0f603db..d4f7f84 100644 --- a/siklos-docker-services.md +++ b/siklos-docker-services.md @@ -8,7 +8,7 @@ - Docker compose files: `/srv/docker//docker-compose.yml` - Note: `vm.swappiness=10` set in `/etc/sysctl.conf` (April 2026) -## Running Containers (verified 2026-06-18) +## Running Containers (verified 2026-06-24, cleaned 2026-06-24) | Container | Image | Port(s) | Compose Path | |---|---|---|---| @@ -33,6 +33,7 @@ | dashy | lissy93/dashy:latest | 8081 | /srv/docker/dashy/ | | dashboard | dashboard-dashboard | 8096 | /srv/docker/dashboard/ | | grav | linuxserver/grav:latest | 8585 | /srv/docker/grav/ | +| searxng | searxng/searxng:latest | 8092 | /srv/docker/searxng/ | | watchtower | containrrr/watchtower | — | /srv/docker/watchtower/ | ## Open WebUI @@ -45,6 +46,18 @@ - **Immich** runs on VM113 (photos, 192.168.88.32) — migrated from siklos in April 2026 - **PhotoPrism** was removed from siklos in June 2026 — Immich chosen as the single photo management solution +## Cleanup (2026-06-24) +Removed stale Portainer stacks and /srv/docker directories: +- **kopia** — replaced by Proxmox PBS for VM backups +- **monitoring** — replaced by Beszel (Prometheus/Grafana/cAdvisor) +- **immich** dir — container lives on VM113 (Portainer endpoint 8) +- **litellm** dir — stack had already been removed; directory was leftover +- **faster-whisper** dir — service runs on jgpc (192.168.88.41) +- **onlyoffice** dir — duplicate; container managed via /srv/docker/media/ + +Caddy: removed broken `guacamole.jgitta.com` block (was pointing to port 8585); added `ai.jgitta.com` → port 3000 (open-webui). +DNS: removed collabora, kopia, headscale, headscale-ui from MikroTik + Cloudflare. Removed blue.jgitta.com, mail.jgitta.com from Cloudflare. + ## Pi-hole - Port: 53 (DNS), 8080 (web UI) - URL: `https://pihole.jgitta.com` diff --git a/siklos/beszel/docker-compose.yml b/siklos/beszel/docker-compose.yml deleted file mode 100644 index 60363cf..0000000 --- a/siklos/beszel/docker-compose.yml +++ /dev/null @@ -1,27 +0,0 @@ -services: - beszel: - image: henrygd/beszel:latest - container_name: beszel - restart: always - environment: - APP_URL: https://beszel.jgitta.com - ports: - - "8085:8090" - volumes: - - ./beszel_data:/beszel_data - - ./beszel_socket:/beszel_socket - - beszel-agent: - image: henrygd/beszel-agent:latest - container_name: beszel-agent - restart: always - network_mode: host - volumes: - - ./beszel_agent_data:/var/lib/beszel-agent - - ./beszel_socket:/beszel_socket - - /var/run/docker.sock:/var/run/docker.sock:ro - environment: - LISTEN: /beszel_socket/beszel.sock - HUB_URL: http://localhost:8085 - TOKEN: LNlfwOORlQXrfFFjFzjb7qCDZ4LVn-lECgkLmmwbyq8 - KEY: "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIF3921TigvLCBlSDqgm1bIz2To1ONUT0ahXszPTxlngU" diff --git a/siklos/monitoring/docker-compose.yml b/siklos/monitoring/docker-compose.yml new file mode 100644 index 0000000..26e3a16 --- /dev/null +++ b/siklos/monitoring/docker-compose.yml @@ -0,0 +1,65 @@ +services: + prometheus: + image: prom/prometheus:latest + container_name: prometheus + restart: unless-stopped + ports: + - "9090:9090" + volumes: + - /srv/docker/monitoring/prometheus.yml:/etc/prometheus/prometheus.yml:ro + - prometheus_data:/prometheus + command: + - "--config.file=/etc/prometheus/prometheus.yml" + - "--storage.tsdb.path=/prometheus" + - "--storage.tsdb.retention.time=30d" + - "--web.enable-lifecycle" + - "--web.enable-admin-api" + + grafana: + image: grafana/grafana:latest + container_name: grafana + restart: unless-stopped + ports: + - "3020:3000" + volumes: + - grafana_data:/var/lib/grafana + environment: + - GF_SECURITY_ADMIN_USER=admin + - GF_SECURITY_ADMIN_PASSWORD=Jogiocsi1211+ + - GF_USERS_ALLOW_SIGN_UP=false + - FTLCONF_dns_maxConcurrentQueries=300 + depends_on: + - prometheus + + cadvisor: + image: gcr.io/cadvisor/cadvisor:latest + container_name: cadvisor + restart: unless-stopped + privileged: true + ports: + - "8090:8080" + volumes: + - /:/rootfs:ro + - /var/run:/var/run:ro + - /sys:/sys:ro + - /var/lib/docker:/var/lib/docker:ro + - /dev/disk/:/dev/disk:ro + + graphite-exporter: + image: prom/graphite-exporter:latest + container_name: graphite-exporter + restart: unless-stopped + ports: + - "9109:9109" + - "9109:9109/udp" + - "9108:9108" + volumes: + - /srv/docker/monitoring/graphite/graphite_mapping.conf:/tmp/graphite_mapping.conf:ro + command: + - "--graphite.mapping-config=/tmp/graphite_mapping.conf" + - "--graphite.listen-address=:9109" + - "--web.listen-address=:9108" + +volumes: + prometheus_data: + grafana_data: diff --git a/jgpc/frigate/docker-compose.yml b/thinkstation/frigate/docker-compose.yml similarity index 100% rename from jgpc/frigate/docker-compose.yml rename to thinkstation/frigate/docker-compose.yml diff --git a/jgpc/open-webui/docker-compose.yml b/thinkstation/open-webui/docker-compose.yml similarity index 100% rename from jgpc/open-webui/docker-compose.yml rename to thinkstation/open-webui/docker-compose.yml diff --git a/jgpc/stirling-pdf/docker-compose.yml b/thinkstation/stirling-pdf/docker-compose.yml similarity index 100% rename from jgpc/stirling-pdf/docker-compose.yml rename to thinkstation/stirling-pdf/docker-compose.yml diff --git a/workstation-disk-analysis.md b/workstation-disk-analysis.md new file mode 100644 index 0000000..f442455 --- /dev/null +++ b/workstation-disk-analysis.md @@ -0,0 +1,232 @@ +# Workstation OS Drive — Disk Analysis & Cleanup Plan +**Date:** 2026-05-27 +**OS Drive:** `/dev/sda2` — 234 GB total | **190 GB used (86%)** | only 32 GB free ⚠️ +**Target drive:** `/mnt/14TB-HD` (`/dev/sdb2`) — 12.7 TB total | 2.7 TB used | **9.4 TB free** + +--- + +## Drive Layout Summary + +| Mount Point | Drive | Size | Used | Free | % | +|---|---|---|---|---|---| +| `/` (OS) | /dev/sda2 | 234 GB | 190 GB | 32 GB | **86% ⚠️** | +| `/mnt/14TB-HD` | /dev/sdb2 | 12.7 TB | 2.7 TB | 9.4 TB | 23% ✅ | +| `/mnt/INTEL-SSD` | /dev/nvme0n1p1 | 469 GB | 449 MB | 445 GB | <1% ✅ | +| `/mnt/StorFly-SSD` | /dev/sdc1 | 1.8 TB | 1.4 TB | 312 GB | 82% | +| `/mnt/photos` (NFS) | TrueNAS NFS | 11 TB | 6.4 TB | 3.9 TB | 63% | + +--- + +## What's Eating Your OS Drive + +### Top-Level Breakdown +| Category | Location | Size | +|---|---|---| +| Home directory total | `/home/jgitta` | **65 GB** | +| System packages & libs | `/var`, `/usr` | **~12 GB** | +| Installed apps | `/opt` | **5.2 GB** | + +### The Biggest Offenders (inside /home) +| Item | Path | Size | Notes | +|---|---|---|---| +| Google Chrome AI model | `~/.config/google-chrome/OptGuideOnDeviceModel` | **4.0 GB** | Regenerates automatically — safe to delete | +| Flatpak app data | `~/.var/app/` | **7.0 GB** | Brave (3.7G), Dolphin (1.4G), DigiKam (0.9G), Zen (0.7G) | +| DigiKam face database | `~/.local/share/digikam` | **5.5 GB** | Move to 14TB (photo metadata) | +| Gemini scratch space | `~/.gemini/antigravity/scratch` | **3.5 GB** | Temp AI workspace — can be purged or redirected | +| .cache (total) | `~/.cache/` | **7.1 GB** | Browser caches, thumbnails, package caches | +| .config (total) | `~/.config/` | **13 GB** | Chrome (5.1G incl. 4G AI model), Claude (2.4G), Brave (2.1G) | +| Downloads folder | `~/Downloads` | **6.9 GB** | Mostly old installers | +| debian-iso folder | `~/debian-iso` | **5.0 GB** | ISOs — move or delete | +| VS Code extensions | `~/.vscode` | **1.9 GB** | Can move to 14TB | +| Firefox profile | `~/.mozilla` | **1.5 GB** | Mostly cache | +| .rustup | `~/.rustup` | **1.4 GB** | Rust toolchain | +| .npm | `~/.npm` | **1.4 GB** | npm cache | + +### Big items in /var and /opt +| Item | Path | Size | Notes | +|---|---|---|---| +| Flatpak system install | `/var/lib/flatpak` | **6.7 GB** | Runtimes, apps | +| System journal logs | `/var/log/journal` | **2.9 GB** | Can be capped to 500 MB | +| APT package cache | `/var/cache/apt` | **610 MB** | Safe to clear | +| Beekeeper Studio | `/opt/Beekeeper Studio` | **1.2 GB** | App install | +| Frigate config | `/opt/frigate/config` | **913 MB** | Move to 14TB | +| Netdata | `/opt/netdata` | **693 MB** | Move data dir to 14TB | + + +--- + +## PART 1 — Immediate Cleanup (Safe to Delete) +**Estimated savings: ~18–22 GB — no data loss** + +### Step 1: Empty the Trash (1.3 GB) +```bash +rm -rf ~/.local/share/Trash/files/* +rm -rf ~/.local/share/Trash/info/* +``` + +### Step 2: Delete Chrome's AI Optimization Model (4 GB!) +Chrome downloads a machine-learning model to optimize page loading. It **rebuilds itself automatically** the next time Chrome runs — you lose nothing permanent. +```bash +rm -rf ~/.config/google-chrome/OptGuideOnDeviceModel +``` + +### Step 3: Clear All Browser Caches (~5.5 GB) +Browsers rebuild their cache as you browse. You may notice slightly slower page loads for a day or two. +```bash +rm -rf ~/.cache/BraveSoftware/Brave-Browser/Default/Cache +rm -rf ~/.cache/google-chrome/Default/Cache +rm -rf ~/.cache/google-chrome/"Profile 1"/Cache +rm -rf ~/.cache/google-chrome/"Profile 2"/Cache +rm -rf ~/.cache/mozilla/firefox/*.default-release/cache2 +rm -rf ~/.cache/browser-os/*/Cache +``` + +### Step 4: Clear Package Manager Caches (~1.8 GB) +```bash +sudo apt clean # APT downloaded .deb files (610 MB) +rm -rf ~/.cache/uv # Python uv cache (638 MB) +rm -rf ~/.cache/node-gyp # Node build cache (120 MB) +npm cache clean --force # npm cache (part of ~/.npm) +rm -rf ~/.cache/go-build # Go build cache (77 MB) +``` + +### Step 5: Trim System Journal Logs (saves ~2.4 GB) +Linux keeps a rolling log of everything happening on the system. 2.9 GB is excessive — capping it at 500 MB still keeps plenty of history for troubleshooting. +```bash +sudo journalctl --vacuum-size=500M +``` + +### Step 6: Clear Thumbnail Cache (~404 MB) +These are small preview images for your file manager. They regenerate automatically when you browse folders. +```bash +rm -rf ~/.cache/thumbnails/* +``` + +### Step 7: Remove Unused Flatpak Runtimes (~1–3 GB) +Flatpak keeps old runtime versions even after apps are updated. This command removes anything nothing depends on anymore. +```bash +flatpak uninstall --unused +``` + +### Step 8: Clear Playwright Test Browser Cache (~128 MB) +```bash +rm -rf ~/.cache/ms-playwright-go +``` + +### Step 9: Clear Gemini Temp Files (~64 MB) +```bash +rm -rf ~/.gemini/tmp/* +``` + + +--- + +## PART 2 — Move Data to the 14TB Drive +**Estimated freed from OS drive: ~20 GB** + +The strategy here is: move the folder to the 14TB drive, then create a **symlink** at the original location. Think of a symlink like a Windows shortcut — when a program goes looking for `~/Downloads`, Linux silently redirects it to the real folder on the 14TB drive. The program never knows anything changed. + +> **Before running any move command**, make sure the 14TB is mounted: +> `ls /mnt/14TB-HD` — you should see files listed, not an empty folder. + +### Move 1: Downloads Folder (6.9 GB) +```bash +mv ~/Downloads /mnt/14TB-HD/Downloads +ln -s /mnt/14TB-HD/Downloads ~/Downloads +``` + +### Move 2: Debian ISOs (5.0 GB) +These are just old installation images — move them to the 14TB for archiving, or delete if you no longer need them. +```bash +mv ~/debian-iso /mnt/14TB-HD/debian-iso +``` + +### Move 3: FreeDOS Custom Build (423 MB) +```bash +mv ~/freedos-custom /mnt/14TB-HD/freedos-custom +``` + +### Move 4: DigiKam Photo Database (5.5 GB) +DigiKam (your photo manager) stores its face recognition engine and metadata database in your home folder. Moving it to the 14TB frees up 5.5 GB. +```bash +mv ~/.local/share/digikam /mnt/14TB-HD/digikam-db +ln -s /mnt/14TB-HD/digikam-db ~/.local/share/digikam +``` +After moving, open DigiKam and go to **Settings → Configure DigiKam → Database** to confirm it still sees the database correctly. + +### Move 5: Frigate Config & Data (913 MB) +If Frigate is running, stop it first so files aren't in use: +```bash +sudo systemctl stop frigate # stop the service first +sudo mv /opt/frigate /mnt/14TB-HD/frigate +sudo ln -s /mnt/14TB-HD/frigate /opt/frigate +sudo systemctl start frigate +``` + +### Move 6: VS Code Extensions (1.9 GB — optional) +```bash +mv ~/.vscode /mnt/14TB-HD/vscode +ln -s /mnt/14TB-HD/vscode ~/.vscode +``` + +--- + +## PART 3 — Change Default Save Locations in Programs + +### All Browsers — Change Download Folder +Set each browser's download location to `/mnt/14TB-HD/Downloads` so new downloads go there automatically: +- **Brave:** Settings → Downloads → Location +- **Chrome:** Settings → Downloads → Location +- **Firefox:** Settings → General → Downloads → Save files to + +### System-Wide Default Download Location +This tells all XDG-aware Linux apps (file manager, etc.) where the Downloads folder is: +```bash +nano ~/.config/user-dirs.dirs +# Find this line: +# XDG_DOWNLOAD_DIR="$HOME/Downloads" +# Change it to: +# XDG_DOWNLOAD_DIR="/mnt/14TB-HD/Downloads" +# Save with Ctrl+O, then Ctrl+X to exit +``` + +--- + +## Summary: Expected Space Recovered + +| Action | Estimated Savings | +|---|---| +| Empty Trash | 1.3 GB | +| Chrome AI model (OptGuide) | 4.0 GB | +| Browser caches (all) | 5.5 GB | +| Package manager caches | 1.8 GB | +| Journal logs (trim to 500 MB) | 2.4 GB | +| Thumbnail cache | 404 MB | +| Flatpak unused runtimes | ~1–3 GB | +| Gemini temp + Playwright cache | ~192 MB | +| **Total cleanup (no data loss)** | **~18–22 GB** | +| Move Downloads to 14TB | 6.9 GB | +| Move debian-iso + freedos to 14TB | 5.4 GB | +| Move DigiKam DB to 14TB | 5.5 GB | +| Move Frigate to 14TB | 0.9 GB | +| Move VS Code extensions to 14TB | 1.9 GB | +| **Total moved to 14TB** | **~20 GB** | +| **GRAND TOTAL FREED FROM OS DRIVE** | **~38–42 GB** | + +After all steps, the OS drive should drop from **86% → roughly 55–60% full**, +giving you ~55–65 GB of breathing room. + +--- + +## Bonus Tips + +- **Your 469 GB Intel SSD at `/mnt/INTEL-SSD` is nearly empty** (only 449 MB used of 469 GB). + Consider using it for speed-sensitive data like VS Code, DigiKam DB, or your home `~/.config` folder — SSDs are much faster than the spinning 14TB hard drive. + +- **Brave Browser appears twice:** you have it installed both as a native app (`/opt/brave.com`, 429 MB) and as a Flatpak (`~/.var/app/com.brave.Browser`, 3.7 GB). You are likely only using one. Running `flatpak uninstall com.brave.Browser` would free ~3.7 GB if you use the native version. + +- **Make sure `/mnt/14TB-HD` auto-mounts at boot** so symlinks don't break. Check with: + ```bash + grep 14TB /etc/fstab + ``` + If there's no entry, the drive may only mount when you log in — ask Claude to help add a permanent fstab entry.