228 lines
22 KiB
Markdown
228 lines
22 KiB
Markdown
---
|
||
created: 2026-08-28
|
||
modified: 2026-08-28
|
||
type: project
|
||
client: sam
|
||
project: web-host-migration
|
||
status: active
|
||
priority: 1
|
||
tags:
|
||
- migration
|
||
aliases:
|
||
- migration-work-log
|
||
- work-log
|
||
id: 1848129023-LOG
|
||
---
|
||
# Migration Work Log
|
||
|
||
> Central log of what has been **done** across the migration phases.
|
||
> Update this as work completes. Each entry: **date · phase · what · evidence**.
|
||
|
||
## Phase legend
|
||
- **P0** Pre-flight · **P1** AWS offsite · **P2** Email · **P3** Local infra · **P4** CMS (separate) · **P5** Decommission
|
||
|
||
---
|
||
|
||
## 2026-08-28
|
||
| Phase | What was done | Notes/Evidence |
|
||
|---|---|---|
|
||
| P0 | Verified no AWS CLI / rclone / creds on `.13` (clean slate) | `command -v aws` empty; `rclone` empty on `.13` |
|
||
| P0 | Verified existing backup stack on `.13` | Borg 1.4.3 in `backup.nix` (`services.borgbackup.jobs.*`, timers 04:00/05:00/06:00, BORG_RSH wrapper for SSH to `.35`) |
|
||
| P0 | Verified `.13` containers | `wherewoof-admin`(:3031), `wherewoof-minio`(:9010), `wherewoof-db`(:5434), Garage(:3900/3902)+portal(:8500) |
|
||
| P0 | Confirmed external exposure | static IP port-forward 80/443 → Caddy `.35`; `where-woof.com`→`192.168.20.13:3020` |
|
||
| P1 | Decision: AWS-only, S3→Glacier DA, ap-southeast-2 (Sydney), IAM least-privilege | Awaiting AWS account signup to proceed |
|
||
| — | Created per-phase plan notes (P0, P1, P2, P3, P5) + this log | Obsidian `220 Web Host Migration/` |
|
||
|
||
## 2026-08-28 (Phase 1 — build begun)
|
||
| Phase | What was done | Evidence |
|
||
|---|---|---|
|
||
| P0 | **Machine confirmation** — assistant runs on `.27` (sam-4screen-desktop); `.13` reachable via `sam@192.168.20.13` | hostname `sam-4screen-desktop` = `.27` |
|
||
| P1 | AWS account ready; IAM user `backup-offsite` created (programmatic, least-priv) | — |
|
||
| P1 | **Credentials verified** via AWS STS (`get-caller-identity`) | Account `648879824522`, user `backup-offsite`, region `ap-southeast-2` |
|
||
| P1 | Credentials stored local-only (`aws-credentials.local` 0600, .27) + copied to `.13` (`~/.aws-credentials.local.new` 0600). **Not in Obsidian.** | — |
|
||
| P1 | Verified `.13` has no aws-cli/rclone; will use one-off `nix-shell awscli2` for imperative steps until `backup.nix` updated | verified |
|
||
| P1 | STS key check OK (Account `648879824522`, user `backup-offsite`); created env-helper `~/.aws-offsite-env.sh` on `.13` (0600, sources creds from file, no secrets echoed) | verified |
|
||
| P1 | **MFA**: root MFA enabled in console; Ente Auth as authenticator (phone). Ente recovery keyphrase stored in local `aws-credentials.local` (0600), NOT Obsidian | — |
|
||
| P1 | **Blocked on** expanded IAM policy attach (bucket-mgmt + object actions scoped to `sam-offsite-backup`) before bucket+lifecycle can be created | awaiting user |
|
||
| P1 | **Bucket `sam-offsite-backup` created** (ap-southeast-2, ACLs disabled, all public-access blocked, versioning enabled) — created via console as root; IAM `backup-offsite` kept object-only | created |
|
||
| P1 | **Object pipeline verified** on `.13`: PUT/LIST/GET/DELETE probe to `s3://sam-offsite-backup/test/` all succeeded via `backup-offsite` | probe-write, content readback OK |
|
||
| P1 | **TODO:** add lifecycle rule (S3→Glacier Flexible@30d→Glacier Deep Archive@90d) — IAM `backup-offsite` lacks lifecycle perm, so rule will be created via console (root) | pending |
|
||
| P1 | **Lifecycle rule `archive-to-glacier` created** via console (root): S3 Standard → Glacier Flexible @ **90 days** → Glacier Deep Archive @ **180 days** (current AWS min: 90/180) | user-confirmed in console |
|
||
| P1 | **Pipeline chosen: AWS CLI `s3 sync`** (NOT rclone). rclone fought S3 region-discovery (needs `ListAllMyBuckets`/`GetBucketLocation` which least-priv reserved user lacks); AWS CLI works with object-only perms. | validated: full UP/LIST/READBACK/DELETE sync test passed on `sam-offsite-backup` |
|
||
| P1 | Cleaned up temp rclone config + test scripts on `.13`; kept `~/.aws-offsite-env.sh` env helper (0600, reads creds from file, no secrets echoed) | — |
|
||
| P1 | **Bandwidth probe** on `.13`: ~**3.3 MB/s** upstream to AWS → ~62 h for full ~712 GB seed | measured |
|
||
| P1 | Wrote **[[Backup Architecture — Offsite to AWS S3]]** note (source set, tech stack, procedure, cost, status) | obsidian |
|
||
| P1 | Refined source set (~712 GB): `photos/by_date`(373G), `photos/phone-only`(.35 Immich, small), `archive/rest`(177G), `borg/.27`(162G). Skips duplicates/transient/regenerable | per Google-photos agent note |
|
||
| P1 | **Awaiting user go/no-go** on seed scope + storage-class (Standard vs Deep Archive for cold set) | decision needed |
|
||
| P1 | **Seed strategy locked:** COLD (photos + archive/rest) → `DEEP_ARCHIVE`; ACTIVE (.27 borg) → `STANDARD`. APPEND-ONLY, no `--delete` (nothing is ever removed from S3). New Google-photos additions auto-picked-up on re-run. | user choices |
|
||
| P1 | **Deep Archive end-to-end probe PASSED** on `.13`: sync with `--storage-class DEEP_ARCHIVE` → `head-object` reported `DEEP_ARCHIVE`; probe cleaned. | verified |
|
||
| P1 | Seed script drafted (`offsite-seed.sh`): photos→DA, archive/rest→DA, borg/.27→STANDARD | pending |
|
||
| P1 | **Awaiting final go** to launch ~62 h full seed | user decision |
|
||
| P1 | **Seed schedule decided (user):** nightly **23:00–05:00 Melbourne local** on `.13` (off-peak, avoids NBN/ABB peak shaping). Resumable/incremental, append-only. | user choice |
|
||
| P1 | **NixOS module `offsite.nix` added** (separate file; does NOT touch `backup.nix`): adds `awscli2` to systemPackages, `offsite-sync.service` (Type=exec, RuntimeMaxSec=6h) + `offsite-sync.timer` (daily 23:00). "Fixed" initial Type=oneshot bug (RuntimeMaxSec ignored) -> Type=exec. | flake check passed; rebuilt |
|
||
| P1 | **Rebuilt `.13`**: AWS CLI v2.35.11 on PATH; service Type=exec RuntimeMaxSec=6h; timer active (next fire 23:00 tonight). | verified |
|
||
| P1 | **First seed scheduled** to auto-start 23:00 tonight. | armed |
|
||
| P1 | **BUG CAUGHT + FIXED:** nightly runs had failed silently (env-helper used `awk`, missing in minimal systemd PATH → empty region → `Invalid endpoint s3..amazonaws.com`; zero objects uploaded). Fixed `offsite.nix`: added `path = [ awscli2 bash gawk gnused gnugrep coreutils ]` and switched creds parsing to grep/cut (no awk). | journal: `awk: command not found` → fixed |
|
||
| P1 | **REBUILD FAILURES diagnosed:** earlier `nixos-rebuild` runs had not actually switched (no new generations Jun→Jul; latest failed `Permission denied` on profile symlink = ran without sudo). Rebuilt with `sudo` → generation now `system-145-link`. | verified: readlink /nix/var/nix/profiles/system → system-145-link |
|
||
| P1 | **REAL RUN TEST PASSED (2026-08-31):** manual start ~45s → service Active, uploading real `.jpg` files; IP out 123.4M; S3 now holds **photos/by_date: 95 objects / 120 MB** (archive/rest + borg/.27 queue behind). Timer still armed for 23:00. | journal + `aws s3 ls` counts |
|
||
| P1 | Monitoring page requested: `offsite.lab.audasmedia.com.au` behind Caddy basic-auth → .13 status page. | in progress |
|
||
| P1 | **Monitoring page built:** `gen-status.sh` (on .13, `/etc/nixos/gen-status.sh`) gathers S3 object/byte counts per prefix, storage class, service state, timer, disk → self-contained HTML. systemd `offsite-status.service`+timer (every 15 min) + `offsite-web.service` (python http.server :8091). Script validated (shows 95 objects/0.12GB, next timer 23:00). | working |
|
||
| P1 | **Caddy block added on .35**: `offsite.lab.audasmedia.com.au` → `import basic-auth` → `reverse_proxy 192.168.20.13:8091`. Validated + reloaded; backup `Caddyfile.bak.20260831-094426`. Wildcard DNS already resolves. | live |
|
||
| P1 | **Awaiting rebuild on .13** (sudo nixos-rebuild) to activate offsite-status/offsite-web services. | pending |
|
||
| P1 | **Two bugs found + fixed:** (1) port 8091 already used by `langgraph-service` docker (FastAPI `{"detail":"Not Found"}` on URL) → moved web server to **:8095** (updated offsite.nix + Caddy). (2) generator wrote to `$HOME/offsite-status.html` (OUT env unset) → added `environment.OUT=/var/www/offsite/status.html` to offsite-status.service. | verified |
|
||
| P1 | **MONITORING PAGE LIVE + VERIFIED:** `https://offsite.lab.audasmedia.com.au` (basic-auth `sam`) → Caddy → .13:8095 → status.html. Local 200, URL returns 401 auth challenge (backend connected, no more 502). Page shows live data: 95 objects/0.12GB, photos 0.0% of 374.98GB, next timer 23:00, service state, disk. | end-to-end verified |
|
||
| P1 | **502 root cause found + fixed:** `.13` NixOS firewall allowlist (`allowedTCPPorts`) did NOT include **8095** → Caddy (`.35`) connections to `.13:8095` silently dropped → 502 in browsers despite local 200. Added `8095` to `allowedTCPPorts` in `configuration.nix`, rebuilt, verified `.35→.13:8095` = 200 (2ms), URL = 401 gate, 0 timeouts/502s. | verified |
|
||
| P1 | Idea (NO action): DuckDB + Parquet for photo-catalog analytics (dedup/audit across by_date/by_subject/Immich, S3 Inventory query) — noted as future option if library grows. | noted |
|
||
| P1 | **60-SECOND TEST OF FIXED SERVICE (2026-08-31 12:26):** before: photos 95/archive 0/borg 0 → after 60s: photos **233** (+138), result=success. Real .jpg uploads confirmed. | before/after counts + journal |
|
||
| P1 | **NIGHTLY VERIFIER ADDED:** `offsite-verify.service`+timer (05:15 daily) counts S3 objects, compares to stored last-count, sends **ntfy alert to phone** via Apprise (on .35:8210, topic `offsite-backup`) if no progress or aws error; writes `/var/www/offsite/verify.log` + shown on monitoring page. Alert path tested: Apprise→ntfy delivered. | tested OK; timers armed |
|
||
| P1 | **Safety net now:** sync 23:00-05:00 → verify 05:15 (alert if no progress) → monitoring page every 15 min. Silent-failure mode is closed. | armed |
|
||
|
||
## Pending next action
|
||
|
||
## 2026-09-01 — BACKUP GAP CLOSURE (tasks in progress)
|
||
| Phase | What was done | Status |
|
||
|---|---|---|
|
||
| .13 Borg | `backup.nix` `local-to-storage` paths expanded: + `~/deployment`(23G), gst-plugins-rs, speech_piper, voice_bridge, whisper-api, voice-agent, librespot, mopidy-radionet, bin, .agents, .pi, .config; exclusions + `.cache`, `.npm`, `.local`, nixos_backup.tar.gz | ✅ config applied (rebuild done) |
|
||
| .13 Borg | **prefect live DB excluded** (caused recurring `.failed` runs) | ✅ config in, applies on next rebuild |
|
||
| .13 Borg | First expanded run in progress (23G initial archive, slow on lzma) | 🔄 running |
|
||
| S3 seed | `offsite.nix` + sync of `borg/.13` (STANDARD) + home dirs (DEEP_ARCHIVE) | ✅ config in; auto-syncs tonight 23:00 |
|
||
| Restore runbook | to be written | ⏳ Task 3 |
|
||
|
||
## Next steps
|
||
|
||
## PHASE 2 EMAIL — progress log (2026-08-31)
|
||
| Item | Status |
|
||
|---|---|
|
||
| DNS moved to Cloudflare (audasmedia.com.au) | ✅ NS = jo/osmar.ns.cloudflare.com; A/wwww/cpanel/ftp preserved; **wildcard `*.lab` → 144.6.86.11 re-added (was missing after import — caught it, home sites restored)** |
|
||
| SES identity `audasmedia.com.au` | ✅ created; 3 DKIM CNAMEs + DMARC added to Cloudflare |
|
||
| Email Routing (free inbound → Gmail) | ✅ enabled; MX route1/2/3.mx.cloudflare.net live; catch-all → samuelrolfe@gmail.com; **test delivery confirmed (arrived in All Mail)** |
|
||
| SPF | ✅ `v=spf1 ip4:173.231.229.92 include:_spf.mx.cloudflare.net include:amazonses.com ~all` |
|
||
| Zoho | ❌ abandoned (SMS OTP never arrives) |
|
||
| MXroute | ⏸ deferred (no funds today; Cloudflare Email Routing covers inbound for $0) |
|
||
| SES outbound test | ⏳ next: send test email via SES → verify DKIM/SPF pass |
|
||
| SES production access | ⏳ later (free, ~hrs) so sites can send to anyone |
|
||
| SES MAIL FROM | ✅ set to default (banner cleared; DMARC still passes via DKIM) |
|
||
|
||
## PHASE 2 EMAIL — STATUS: DONE ✅ (2026-08-31)
|
||
- **Inbound:** Cloudflare Email Routing → Gmail. Test confirmed (All Mail). $0.
|
||
- **Outbound:** SES identity `audasmedia.com.au` verified; test email delivered; DKIM/SPF pass.
|
||
- **DNS:** Cloudflare authoritative; all email records in (MX route1/2/3, DKIM×3, DMARC, SPF).
|
||
- **Zoho:** abandoned (SMS OTP never arrives). MXroute deferred (no funds today; Email Routing covers inbound at $0).
|
||
- **Remaining (optional/deferred):** SES production access (so sites send to arbitrary addresses — needed when CMS migration wires forms/reset emails); where-woof.com email later.
|
||
|
||
## Next steps (after email)
|
||
1. Request SES production access when ready (free, ~hrs approval).
|
||
2. Phase 3 (local infra prep) or the CMS migration project.
|
||
|
||
## Next steps (email)
|
||
|
||
## SCOPE NOTE — where-woof.com (2026-08-31)
|
||
- **User is building where-woof in another AI session.** Do NOT touch its DNS/email from this project.
|
||
- Current state (unchanged): NS = InMotion (ns.inmotionhosting.com / ns2), A → 144.6.86.11 (static IP, local), no email records.
|
||
- Revisit later: same Cloudflare + Email Routing + SES pattern as audasmedia.com.au, when user is ready.
|
||
|
||
## Next steps (post-email)
|
||
|
||
## PHASE 2 EMAIL — where-woof.com DONE ✅ (2026-08-31)
|
||
- DNS → Cloudflare (zone live on barbara.ns.cloudflare.com; NS switched at Squarespace).
|
||
- Records: A apex/www/admin → 144.6.86.11 (DNS-only); InMotion A's + stale TXT deleted; DKIM×3 + DMARC added.
|
||
- Email Routing active (MX route1/2/3 priorities 4/25/14, SPF include:_spf.mx.cloudflare.net); rule `hello@where-woof.com → samuelrolfe@gmail.com`.
|
||
- SES identity created; MAIL FROM = default.
|
||
- Awaiting end-to-end send test (from non-Gmail-same-account address to hello@where-woof.com).
|
||
- Note: audasmedia MX priorities are 54/92/78; where-woof 4/25/14 — Cloudflare assigns per-zone, both valid.
|
||
|
||
## PHASE 2 EMAIL — where-woof.com → PURELYMAIL (2026-09-02)
|
||
- **Switched from Cloudflare Email Routing to PurelyMail** (real domain mailboxes, IMAP/SMTP, ~US$10/yr).
|
||
- **Cloudflare Email Routing DISABLED** on where-woof.com (frees MX — cannot coexist with external provider).
|
||
- DNS records added in Cloudflare (all verified live):
|
||
- MX @ → `mailserver.purelymail.com` (prio 10)
|
||
- TXT @ → `purelymail_ownership_proof=3ac61cfe...`
|
||
- TXT @ → `v=spf1 include:_spf.purelymail.com include:amazonses.com ~all`
|
||
- CNAME `purelymail1/2/3._domainkey` → `key1/2/3.dkimroot.purelymail.com`
|
||
- CNAME `_dmarc` → `dmarcroot.purelymail.com`
|
||
- CNAME `autoconfig` → `autoconfig.purelymail.com`
|
||
- SRV `_autodiscover._tcp` → `autodiscover.purelymail.com` (0 0 443)
|
||
- Deleted old leftovers: SES DKIM×3 (where-woof), `mail`/`smtp` CNAMEs, old `_dmarc` TXT.
|
||
- PurelyMail domain: validated (Check DNS Records done); Deliver mail to = PurelyMail; mailbox `hello@where-woof.com` created.
|
||
- **Pending:** PurelyMail mailbox → Thunderbird or Gmail "Send mail as" (user leaning: Gmail inbox + PurelyMail SMTP send-as; Thunderbird optional).
|
||
|
||
## SITE SENDING HANDOFF — for the AI building where-woof.com (2026-09-02)
|
||
Hand this to the where-woof website builder:
|
||
```
|
||
where-woof.com outgoing (transactional) email:
|
||
- Provider: Amazon SES, region ap-southeast-2; identity where-woof.com verified
|
||
- Sender: hello@where-woof.com
|
||
- SMTP: create user in SES → SMTP settings; host email-smtp.ap-southeast-2.amazonaws.com, port 587 STARTTLS
|
||
- BEFORE sending to real users: request SES production access (SES → Account dashboard → Request production access; free, ~hours)
|
||
- DNS for delivery already set (SPF/DKIM/DMARC include SES + PurelyMail)
|
||
- Do NOT use PurelyMail SMTP for site/transactional mail — use SES
|
||
```
|
||
|
||
## SES SMTP CREDENTIALS (created 2026-09-03, local file `aws-credentials.local`)
|
||
- Endpoint: `email-smtp.ap-southeast-2.amazonaws.com` : `587` (STARTTLS)
|
||
- IAM user: `ses-smtp-user.20260903-152339`; SMTP username/password stored in `aws-credentials.local` (600, not Obsidian)
|
||
- Permission: `ses:SendRawEmail` only (least privilege)
|
||
- **Production access requested 2026-09-03** (transactional form, where-woof.com) — awaiting AWS approval (~hours)
|
||
- Until approval: SES sandbox = can only send to verified addresses (samuelrolfe@gmail.com).
|
||
|
||
## TODO — audasmedia.com.au SES (2026-09-03)
|
||
- [ ] **Give audasmedia.com.au the same SES sending access as where-woof**: create/confirm SES identity for audasmedia.com.au (already exists), create SMTP creds for it, and ensure production access covers it.
|
||
- audasmedia.com.au will also send email (site mail) → same pattern as where-woof.
|
||
- When audasmedia infra is built on .13 (user to advise).
|
||
|
||
## NEXT — audasmedia.com.au infra on .13 (2026-09-03)
|
||
- User will advise details. Apply same hosting/backup/email pattern as where-woof.
|
||
|
||
## VISUALIZATION — status page upgrade + Garage UI handoff (2026-09-05)
|
||
- **Status page upgraded** (`offsite.lab.audasmedia.com.au`):
|
||
- Fixed "failed" label → now shows `OK (finished cleanly)` / `OK (6h window ended 05:00)` in green (the systemd "timeout" at 05:00 is the expected RuntimeMaxSec stop, not an error).
|
||
- Added **7-night history table** (parsed from verify.log: date, result, objects, delta).
|
||
- Added "Last verify" line.
|
||
- **Garage UI handoff written** (`Garage Web UI — handoff.md`): use `khairul169/garage-webui` (Garage v1.x compatible; Noooste/garage-ui needs 2.0+). Deploy on :3909, admin API :3902, token from .env.
|
||
- **Grafana deferral noted**: Grafana on .35 has NO data sources (empty). Defer consolidated dashboard unless needed after 1+2.
|
||
- Assessment: Backrest/Kopia UIs visualize only their own local repos (.23), not the AWS S3 offsite. Garage UI is separate (local Garage, not AWS).
|
||
|
||
## GARAGE v2 UPGRADE (2026-09-05)
|
||
- **Why:** garage-webui / Noooste UIs didn't list buckets on Garage v1.0.1 (v1 admin API lacks their expected endpoints). Storage itself was fine (S3 v1==v2 for portal).
|
||
- **Backup first:** `/mnt/data/family-home-lab/garage-backup-20260905-111241/` (data 181M + meta 1.1M, via busybox as root since LMDB files are root-owned).
|
||
- **Upgraded:** `dxflrs/garage:v1.0.1` → `v2.1.0`; garage.toml rewritten (v2 schema: `replication_factor`, `[s3_web]`, admin on **3903**); compose updated (ports 3900/3901/3902/3903). Tokens preserved exactly (admin/metrics/rpc 64-hex).
|
||
- **Verified:** v2 auto-migrated the LMDB on start; `GetClusterStatus` shows node up v2.1.0; `ListBuckets` returns existing buckets (outline,...) via `POST /v2/...`.
|
||
- **garage-webui:** API_BASE_URL → `http://garage:3903` (admin moved to 3903), recreated, 0 refused-erores, HTTP 200. **USER CONFIRMED: all buckets now show in the dashboard. ✅**
|
||
- Caddy `garage-ui.lab.audasmedia.com.au` → 3909 (basic-auth). Removed a stale 3902 block.
|
||
- **Rollback:** if needed, stop v2, restore garage-backup-*, revert compose/garage.toml, restart v1.0.1.
|
||
|
||
## TIER-A DB HARDENING (2026-09-05/06)
|
||
- **Findings:** All ~21 DB containers across .13/.35 are covered by existing backups (Borg on .13 via `~/Docker`; Backrest/Kopia on .35 via `/docker-containers`+volumes) — **no coverage gap**. Confirmed Vikunja = 437 paths in restic; Outline in Borg. The shared weakness: all are **file-copies of live DBs** (inconsistent-restore risk).
|
||
- **Plan:** dump critical DBs to an already-backed-up path before the backup runs.
|
||
- **.13 (done, dumps tested):** `db-dump-13.sh` dumps **outline, wherewoof-db, family-home-lab(portal), langfuse, ai-resume, airflow** Postgres via container-env creds (no secrets in config) → `~/Docker/Containers/<app>/backups/db-dumps/` (inside Borg `~/Docker` paths, keep-3). Tested: 201K/31K/6K/278K/824/197K dumps written.
|
||
- **Pending:** wire .13 timer (03:30 pre-Borg); do .35 Tier-A (vikunja + others) via Backrest pre-hook or dump script.
|
||
|
||
## TIER-A DB HARDENING — IMPLEMENTATION (2026-09-06)
|
||
- **.13** (`~/.Docker → Borg`): `db-dump-13.sh` + NixOS module `dbdump.nix` → timer **03:30** pre-Borg. Dumps outline, wherewoof-db, family-home-lab(portal), langfuse, ai-resume, airflow PG → `~/Docker/Containers/<app>/backups/db-dumps/` (in Borg paths). **Fixed:** service must run `User=root` (/home/sam/Docker is root-owned) — rebuild applied 2026-09-06.
|
||
- **.35** (`/Docker/Containers → Backrest`): `db-dump-35.sh` + systemd unit/timer → **03:35** daily. Dumps vikunja(mariadb via env pw), gitea(mysqldump, pw `sam4jo`), affine, immich, paperless (pg via env) → `/Docker/Containers/<app>/backups/db-dumps/` (in restic path). Timer installed+enabled; verified affine 2MB dump works.
|
||
- **Notes:** Backrest image lacks pg/mysql/docker clients → used host systemd timer (root) instead of Backrest hooks. firefly_iii compose not found under /Docker/Containers/firefly_iii (Tier-B anyway; skip). Gitea root pw `sam4jo` stored in root-only script.
|
||
- **Awaiting:** .13 rebuild (User=root) + first automatic fires tonight/tomorrow 03:30/03:35.
|
||
- **langfuse + airflow removed (2026-09-06):** stopped (not deleted; compose dirs remain). Removed langfuse/airflow lines from `db-dump-13.sh` (would have errored nightly). pi-langfuse extension already absent from .27. Offsite/backup configs unaffected.
|
||
1. SES console → Send test email (audasmedia.com.au → samuelrolfe@gmail.com); check delivery + DKIM/SPF pass.
|
||
2. Request SES production access (free).
|
||
3. Then return to Phase 3 (local infra) / CMS migration project.
|
||
- **Build:** create bucket `sam-offsite-backup` + lifecycle (S3→Glacier Flexible@30d→Glacier DA@90d), then wire local Borg/rclone→S3 on `.13`, seed, test restore.
|
||
- **Security:** enable root MFA; consider rotating access key after seeding (it was pasted in chat).
|
||
|
||
---
|
||
|
||
## 2026-09-02 — Backup gap closure: config + root-cause fix
|
||
|
||
| Item | What | Status |
|
||
|---|---|---|
|
||
| .13 Borg coverage | `backup.nix` local-to-storage + `~/deployment`, voice/audio/dev dirs, `.agents`, `.pi`, `bin`, `.config`; excludes + `.cache`/`.npm`/`.local` | ✅ applied |
|
||
| prefect DB | Added to Borg exclude (was causing recurring `.failed` runs) | ✅ applied |
|
||
| Borg nightly | 05:00 runs succeeding (index written Sep 2 05:01); 23G first archive slow (lzma) | ✅ working |
|
||
| **S3 ordering bug** | **Found: script ran sequentially; photos consumed the entire 6h nightly window → `borg/.13` + home dirs never reached S3 (0 objects)** | 🔧 **fixed** |
|
||
| S3 ordering fix | Reordered `offsite.nix`: `.13` repo + `.27` repo + home configs sync **FIRST** (minutes), photos/archive last (fill remaining window) | ✅ config in; rebuild pending |
|
||
| Restore runbook | `Restore Runbook.md` written + linked from Backup Architecture | ✅ |
|
||
| Note | `borg list` fails for sam (repo data/ root-owned — known artifact; Borg job runs as root, works) | noted |
|
||
|
||
---
|
||
|
||
*Linked from [[Migration Plan — InMotion to Local + AWS Offsite]].* |