Commit Graph

75 Commits

Author SHA1 Message Date
Bastian de Byl 773a2bbc9c harden TrueNAS CIFS mounts so immich self-heals
TrueNAS was power-cycled, the CIFS mounts failed, and systemd never retried
-- mount units are not restarted on failure. SMB came back, nothing
remounted, and immich-server served an empty library for days while its
database still listed 14,181 assets pointing at /mnt/media/originals.

fstab carried no _netdev, no nofail and no automount, so there was no path
back without a human. Now:

  x-systemd.automount  any access re-attempts the mount; failure stops
                       being terminal
  _netdev / nofail     ordered after network-online, dead NAS cannot block boot
  soft                 I/O errors instead of blocking forever, so the
                       container can be restarted rather than wedging in
                       uninterruptible sleep
  idle-timeout         unmount when unused, clearing stale handles
  resilienthandles     SMB3 rides out brief blips

ansible.posix.mount mounts directly and never starts the generated
.automount unit, leaving the on-access trigger inactive -- enable it
explicitly, or the headline fix silently does nothing.

The containers are systemd USER units while the mounts are SYSTEM units, so
RequiresMountsFor= is unavailable. cifs-watchdog bridges the scopes: checks
health, recovers, and restarts ONLY immich-server (the sole consumer of both
paths; postgres/redis/ML use local volumes).

Two bugs the umount test caught, both worth knowing:
- `ls` cannot test mountedness. An unmounted mount point is an ordinary
  empty directory, so ls succeeds and recovery was skipped entirely.
- A drop repaired within a single run leaves prev=healthy, so keying the
  restart solely on the stored state skipped it while the container still
  held its stale view.

Also moves the SMB password out of /etc/fstab, which is 0644 and was
readable by every local user, into a 0600 credentials file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 14:49:22 -04:00
Bastian de Byl fec7d62acb feat(skudak-cloud): repair LibreSign, brand its mail, add Redis
LibreSign had been silently broken since it was first deployed in
January. Every step of the old before-starting hook ended in `|| echo`,
so six months of failures logged nothing.

LibreSign repair
- Root cause was a stale config_path: a valid OpenSSL root CA existed at
  generation 1, a failed CFSSL attempt left an empty generation 2, and
  config_path was left pointing at the empty one. Regenerated as
  "Skudak LLP" (was the pre-rename "Skudak Rennsport LLP").
- Deleted the hook. Java/PDFtk/jSignPdf live under data/appdata_*, a
  persisted volume, so they only ever needed installing once. Install and
  verification are now explicit tasks that actually fail.
- PHP_MEMORY_LIMIT 1024M -- the 512M image default fails opaquely
  mid-signature. LC_ALL/LANG so the JVM is not ANSI_X3.4-1968.
- signature_render_mode=GRAPHIC_ONLY. Any other mode halves the stamp
  width and overlays a name/date block that collides with the drawn mark
  and duplicates what our documents already typeset. The value must be
  exactly GRAPHIC_ONLY; a bare "GRAPHIC" is accepted by occ, matches no
  radio in the UI, and silently reverts to default.
- write_qrcode_on_footer=false, written with --type=boolean because
  FooterHandler reads it via getValueBool and the typed appconfig API
  does not coerce a string "0". The validation URL text is kept.
- identification_documents=0 -- the default gates signing behind an ID
  upload plus admin approval, so signers saw no way to sign.
- shareapi_restrict_user_enumeration_full_match=no, so an email owned by
  an existing account can be added as a signer. Root cause is in core
  (MailPlugin.php:163), not LibreSign. Do NOT set full_match_email=no --
  that disables email signer search entirely.

Mail branding (skudakmail app)
- Two supported extension points, no core patch and no LibreSign fork:
  mail_template_class for layout, subjects, button labels and the footer
  LibreSign never adds; and a BeforeMessageSent listener to embed the
  wordmark as a cid: part so it survives remote-image blocking.
- A third listener adds scoped CSS fixing the signing page being clipped
  on iOS Safari (100vh -> 100dvh). Patched upstream too.
- skudakmail-verify.php.j2 asserts all of the above through the real
  useTemplate() path and fails the play on drift. Every assertion was
  proven to fail when deliberately regressed.

Redis
- memcache.locking was unset, so Nextcloud used DBLockingProvider and
  every file lock became a MariaDB write -- the contention behind the
  intermittent multi-second stalls. Verified after: db locks static,
  redis keys growing.
- requirepass lives in a mounted 0640 conf, not --requirepass, which
  would leak it into podman inspect, the systemd unit and ps. The file is
  chowned to uid 999 because redis-server does not run as root and the
  :ro mount stops the image fixing it itself.
- No maxmemory: cache is evictable, locks are NOT, and evicting a held
  lock permits concurrent writers to one file. No persistence either --
  a restored RDB could reinstate locks whose owner is long dead.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 15:54:52 -04:00
Bastian de Byl c184099b2d refactor(backup): remove duplicate direct-to-S3 stage
A host-to-iDrive S3 stage was added here and is now removed. It would
have written the same data into the same `backup-all` bucket that the
TrueNAS cloud-sync task already fills -- duplicate storage, two writers
to one prefix, for no additional coverage.

Offsite to business-owned storage was already solved: the rsync feeds
/mnt/glacier/skudakcloud and TrueNAS cloud-syncs that to Skudak's own
iDrive e2 account. The earlier note in skudak/cloud.yml proposed adding
S3 *and then dropping the rsync* -- replacement, not addition -- and
building both was a misreading of it.

If offsite is ever moved onto this host it must REPLACE the rsync, not
run beside it. Settle first whether the TrueNAS -> iDrive leg is
independently verifiable; keeping this chain means trusting it.

Also removes the now-orphaned /etc/backup_s3 credential file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 15:54:27 -04:00
Bastian de Byl f11391b28f bound log growth and reclaim ~52 GB of container disk
Caddy was rotating on implicit defaults (100MiB/keep 10/90d) that were not
holding -- 20 rotated files per stream and a 190-day-old .gz, 1.3 GB across
16 log streams. Made explicit at 10MiB/keep 3/7d.

Note roll_size et al are subdirectives of `output file`, NOT of `log`.
Getting that wrong does not degrade gracefully: Caddy refuses to start on a
bad config, so every site went down until it was corrected. Worth a
`caddy validate` gate before reload.

journald had no SystemMaxUse and had reached 4 GB, drifting toward its
10%-of-filesystem default (~190 GB on this root). Capped at 500M.

Both are safe to keep short because fluent-bit ships the journal and every
Caddy access log into Graylog -- though note its GELF output has been
erroring for days, which weakens that premise and wants investigating.

The larger find was unrelated to logs: 896 images totalling 59.6 GB with
75% unused (94 tags of greg-time-bot, 73 of fulfillr -- one per deploy) and
5.4 GB of dangling volumes, mostly 804 MB Nextcloud /var/www/html trees
orphaned by container recreations. Pruned to 22 images / 15.4 GB, and added
a weekly timer keeping 30 days so a rollback still needs no rebuild.

Also dropped the decommissioned 6379/tcp redis rule (nothing listening;
Immich's redis is on the shared podman network) and the orphaned nosql, s3
and searxng volume dirs.

Backup log exclusions turned out to be unnecessary: Gitea logs to console
so its log dirs are empty, Nextcloud already excludes its own, BookStack
mounts only uploads, and Caddy is not backed up.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 11:13:35 -04:00
Bastian de Byl bc110ce69e back up Gitea + Skudak app data; drop PartKeepr and Pi-hole
Extends the Nextcloud backup machinery rather than adding a second
mechanism. cloud-backup.sh.j2 gains three guarded options, all no-ops for
the existing callers:

  backup_podman_user  Gitea runs rootless under `git`, not `podman`
  backup_db_type      postgres (Gitea) and mysql (BookStack) alongside
                      mariadb; each engine's completion trailer differs,
                      and grepping for the wrong one fails every run
  backup_sqlite_dbs   `sqlite3 .backup` for live WAL-mode SQLite, gated on
                      `pragma integrity_check` before promotion -- rsync
                      is either stale (no -wal) or torn (with it)

New instances: gitea-debyl, skudak-gitea, bookstack, partsy-skudak. The
alert handler is rendered once and shared, so its wording is now generic
rather than per-product; TAG stays nextcloud-backup because an external
Graylog rule matches on it.

`apply:` on the includes is load-bearing -- tags on a dynamic
include_tasks do not reach the tasks inside it.

Business data (skudak-gitea, bookstack, partsy-skudak) goes to TrueNAS
and on to Skudak's own iDrive account; the personal bucket's
/skudak*/** excludes are permanent, not a stopgap.

Removals: PartKeepr is superseded by Partsy, and its teardown never
finished -- it targeted /etc/systemd/system/podman-partkeepr*.service,
wrong prefix and wrong scope, leaving enabled user units in failed state.
Pi-hole's role was already orphaned (absent from deploy_home.yml); its
port 53 rule went with it after confirming nothing listens there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 18:08:47 -04:00
Bastian de Byl 5776dbe1bf UPS monitoring, Nextcloud cron, container image bumps
ups role: NUT server on home.debyl.io for the CyberPower PR1500RT2U that backs
both it and truenas.localdomain, with staged shutdown (TrueNAS sheds at t+2min,
host at 10% charge) and best-effort IPMI power-on when mains returns. The 10%
threshold leans on ignorelb + override.battery.charge.low rather than a custom
poller, because CyberPower asserts its own low-battery flag far too early.
Credentials come from vault vars; nothing sensitive is templated in the clear.

Nextcloud background jobs: both instances have backgroundjobs_mode "cron", which
expects an external caller every ~5 minutes, and nothing was calling. The
personal instance had not run a background job since 2026-05-14 and skudak since
2024-11-20. Consequently trash and file versions never expired, stale chunked
uploads accumulated, calendar reminders never fired, and nextcloud.log was never
rotated -- which quietly made the existing log_rotate_size cap inert. Added a
systemd timer per instance, skipping cleanly when the container is down or in
maintenance so deploy windows don't show up as failed units.

Trash retention on the personal instance: the default "auto" only expires when
disk space demands it, so 66 GB of >30-day deletions sat on a host with 1.3 TB
free -- effectively unbounded. "auto, 30" makes the 30-day expiry unconditional
while still purging early under pressure.

Image bumps:
  nextcloud       33.0.0  -> 34.0.2   (both cloud and skudak-cloud)
  greg-time-bot   3.9.25  -> 3.10.0
  fulfillr        20260723.2044 -> 20260728.2155  (prod and dev)

The fulfillr bump records what is already deployed: both containers were rolled
to that image on 2026-07-29 for SCRUM-156 (digital product releases + customer
update campaign). Committing it keeps the repo from claiming an older tag than
the host is actually running, which would otherwise roll fulfillr backwards on
the next clean-checkout deploy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-30 13:00:51 -04:00
Bastian de Byl 16145fb6bd only email genuine backup failures
A spurious invocation carries no action for a reader, so mailing it just
trains them to skip past the subject line -- which defeats the point of the
alert. Send mail only when the unit actually reports failure; spurious
triggers still leave their journald record, so they stay greppable and can
still feed a Graylog rule.

Verified both paths: a spurious trigger leaves /var/log/msmtp.log untouched
and logs alert_mail=skipped reason=spurious, while a genuinely failed unit
still composes mail with the FAILED subject.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 20:34:45 -04:00
Bastian de Byl 6e99794d0f restore full dump flags; stop spurious alerts claiming FAILED
mariadb-upgrade --force has now been run against both instances and repaired
the system tables, so --routines and --events no longer abort the dump.
Restore them for completeness. Both verified: rc=0 with a clean completion
trailer, and both Nextcloud instances report installed with unchanged table
counts afterwards.

The upgrade still exits non-zero on these containers because it cannot create
the `sys` schema: /var/lib/mysql is owned by daemon rather than mysql, so
mysqld may not create top-level databases. `sys` is diagnostic only and
unused by Nextcloud, but the same permission would block creating any new
database, so it is recorded in the template comment.

The alert handler claimed FAILED in its subject line regardless of what the
unit actually reported, so starting it by hand mailed out a failure notice
for a run that succeeded. Derive the subject and log line from the real
Result, and emit status=spurious rather than status=failed so such triggers
cannot match a Graylog alert rule keyed on genuine failures.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 20:04:32 -04:00
Bastian de Byl 0ab423ca55 harden nextcloud backups: db dumps, alerting, drift fix
The data-only rsync left no way to restore a working instance: mysql/ and
config/ were never backed up, so a recovery would have files but no shares,
users or metadata. Dump the database before syncing files (a DB older than
the files is repairable with occ files:scan; a newer one references blobs
that never made it into the backup) and ship config/ alongside it.

Capture the --chmod=Du=rwx,Dgo=rx flag that had been hand-added to the
deployed skudak-cloud script. It was outside git, so every deploy silently
reverted it. It now lives in backup_rsync_extra_args.

Add OnFailure= alerting. The units failed silently before, which is how an
iDrive sync failure sat unnoticed since May. msmtp rather than the esmtp
already installed: the OpenSRS relay is port 465 (implicit TLS) and libesmtp
only speaks STARTTLS.

Exclude nextcloud.log* from the sync and cap log_rotate_size. skudak-cloud
was running at loglevel 0 and had written a 64 GB log that was being rsynced
and pushed to S3; set it to 2 to match the home instance.

Stagger the timers (04:00 / 04:30) so both finish before the 05:00 TrueNAS
snapshot task, and bound TimeoutStartSec so a wedged rsync cannot leave the
unit activating forever and skip every subsequent trigger.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-26 16:03:18 -04:00
Bastian de Byl b380e33c92 SCRUM-140: Bump fulfillr images to 20260717.0202; snipcart_api_key via EXTRA_VARS
The import-only Snipcart key renders empty on normal deploys (import
endpoint 503s); it was injected via EXTRA_VARS just for the historical
refund re-import and is now blanked again.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 22:43:13 -04:00
Bastian de Byl 16053e1cbb fulfillr: drop Snipcart key, add outreach/recovery schedule config, bump image
- remove snipcart_api_key from dev/production config (Snipcart decommissioned
  post-migration)
- add review-outreach and cart-recovery schedule_name/schedule_group blocks
  (dev + prod) for the EventBridge-driven outreach and cart-recovery jobs
- bump fulfillr image 20260607.0217 -> 20260613.0117

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 10:19:56 -04:00
Bastian de Byl 7d4a398bba Drop self-hosted AI (Ollama + SearXNG); gregtime switches to xAI Grok
The Ollama role and SearXNG container backed FISTO AI responses in the
greg-time Discord bot. greg-time 3.9.6 drops both (plus the Gemini path)
in favor of a single xAI Grok backend, so:

- remove the ollama role and its wiring in deploy_home.yml
- remove the searxng container task, template, and searxng_path default
- gregtime: swap OLLAMA_*/SEARXNG_URL/GEMINI_API_KEY env for XAI_API_KEY,
  bump image 3.6.5 -> 3.9.6
- vault: add xai_api_key, drop gemini_api_key

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 10:19:45 -04:00
Bastian de Byl 87cf953364 SCRUM-45: Revert Caddy /webhooks/easypost carve-out
The EasyPost tracker webhook moved to debyltech-api (publicly reachable Lambda);
the fulfillr host is LAN-restricted and no longer hosts it, so the carve-out is
no longer needed. Removes the handle blocks for prod and dev.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 21:13:56 -04:00
Bastian de Byl c896f69ff9 SCRUM-45: Caddy carve-out for the EasyPost return webhook
The Fulfillr host is IP-restricted, so EasyPost's servers can't reach it. Add a
narrow `handle /webhooks/easypost` before the IP restriction (handle blocks are
mutually exclusive, first match wins) for prod (:9054) and dev (:9055) so the
HMAC-verified tracker webhook is reachable while the rest of the host stays locked.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 20:29:44 -04:00
Bastian de Byl 2335b4980d fulfillr(prod): wire prod Turso store + live Stripe (fulfillr_prod_* vars) + image 20260606.1735 2026-06-06 17:28:00 -04:00
Bastian de Byl da98a2c5dc fulfillr(prod): add download_base_url=https://api.debyltech.com to production.json.j2 (cutover prep) 2026-06-06 16:55:55 -04:00
Bastian de Byl 935de1fcfe fulfillr-dev: download_base_url for resend-download links 2026-06-06 11:32:42 -04:00
Bastian de Byl 35213d81c3 fulfillr-dev: point aws.bucket at debyltech.digital.dev (digital file uploads) 2026-06-06 08:42:33 -04:00
Bastian de Byl e82ace6de3 fulfillr-dev: staging back-office container + Turso store prep
Add a second go-fulfillr container (fulfillr-dev) wired to the staging
Turso store + EasyPost/Stripe test keys via dev.json, served at
fulfillr-dev.debyltech.com (Caddy -> :9055), LAN-restricted like prod.

- fulfillr-dev.yml + dev.json.j2: the staging container, volumes, config
- defaults: fulfillr_dev_* vars; prod store URL stubbed off until cutover
- Caddyfile + caddy.yml: fulfillr-dev site block and static mount
- awsddns.yml: Route53 DDNS for the fulfillr-dev hostname
- production.json.j2: add store_database_url/store_auth, rename stripe key
  var to fulfillr_stripe_api_key
- vault.yml: dev + store/stripe secrets

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 00:23:07 -04:00
Bastian de Byl 2df5b7fc03 Deploy fulfillr 20260603.0222 and wire tickets_table
Bump fulfillr image to the build with the tickets feature, and add the
tickets_table to the fulfillr production.json config (new debyltech-tickets-prod
DynamoDB table) so the /api/v1/tickets routes register.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 22:32:52 -04:00
Bastian de Byl 5e189289e7 fulfillr: deploy Stripe payment requests (key + image 20260530.2348)
- add stripe_api_key to fulfillr production.json template
- add restricted Stripe key to ansible vault (encrypted)
- bump fulfillr image to the CI build containing the Stripe endpoints

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 20:58:40 -04:00
Bastian de Byl 829befeb1c chore: bump container versions and remove n8n
- gitea: 1.25.2 -> 1.26.1 (debyl + skudak)
- caddy: 2.10.2 -> 2.11.2
- uptime-kuma: 2.0.2 -> 2.3.2 (debyl + skudak)
- bookstack: 25.7 -> 26.3.4
- home-assistant: 2026.1 -> 2026.5.1
- immich (server + ML): v2.5.0 -> v2.7.5
- remove n8n service (unused)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-10 15:44:09 -04:00
Bastian de Byl 4cc65f2a99 feat: deploy go-fulfillr cases dashboard to home.debyl.io
- Bump fulfillr container image from 20260124.0411 to 20260509.1940
  (built from go-fulfillr commit 48b9f60 which adds /api/v1/cases
  endpoints for the contact-form CRM dashboard).
- Add fulfillr_cases_table default ("debyltech-cases-prod") so the
  HasCasesConfig() guard flips on at startup and the cases routes
  register.
- Add cases_table to production.json.j2 so it lands in /config inside
  the container.

Verified after deploy: GET /api/v1/cases returns the existing test
cases, PATCH succeeds, GSI1PK rewrite works.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-09 16:04:52 -04:00
Bastian de Byl 43fbcf59a5 add n8n workflow automation and fix cloud backup rsync
- Add n8n container (n8nio/n8n:2.11.3) with Caddy reverse proxy at n8n.debyl.io
- Add --exclude .ssh to cloud backup rsync to prevent overwriting
  authorized_keys on TrueNAS backup targets

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 12:12:19 -04:00
Bastian de Byl 8fd220a16e noticket - update zomboid b42revamp modpack to collection 3672556207
Replaces old 168-mod collection (3636931465) with new 385-mod collection.
Cleaned BBCode artifacts from mod IDs, updated map folders for 32 maps.
LogCabin retained for player connect/disconnect logging.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 13:59:33 -05:00
Bastian de Byl 3637b3ba23 noticket - remove karrio, update gregtime, fix caddy duplicate redirect
Remove Karrio shipping platform (containers, config, vault secrets,
Caddy site block). Bump gregtime 3.4.1 -> 3.4.3. Remove duplicate
home.debyl.io redirect in Caddyfile. Update zomboid b42revamp mod list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-24 17:40:00 -05:00
Bastian de Byl 495943b837 feat: add ollama and searxng, migrate to debyl.io hostname
- Add ollama role for local LLM inference (install, service, models)
- Add searxng container for private search
- Migrate hostname from home.bdebyl.net to home.debyl.io
  (inventory, awsddns, zomboid entrypoint, home_server_name)
- Update vault with new secrets

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-12 15:13:25 -05:00
Bastian de Byl 61692b36a2 refactor: reorganize fluent-bit and geoip out of containers
- Move fluent-bit to common role (systemd service, not a container)
- Move geoip to podman/tasks/data/ (data prep, not a container)
- Remove debyltech tag from geoip (not a debyltech service)
- Fix check_mode for fetch subuid task to enable dry-run mode

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-28 12:34:43 -05:00
Bastian de Byl 9d562c7188 feat: smart zomboid traffic filtering with packet-size detection
Replace per-IP hashlimit with smarter filtering that distinguishes
legitimate players from scanner bots based on packet behavior:
- Players send varied packet sizes (53, 37, 1472 bytes)
- Scanners only send 53-byte query packets

New firewall rule chain:
- Priority 2: Mark + ACCEPT non-query packets (verifies player)
- Priority 3: ACCEPT queries from verified IPs (1 hour TTL)
- Priority 4: LOG rate-limited queries from unverified IPs
- Priority 5: DROP rate-limited queries (2 burst, then 1/hour)

Also includes:
- Fail2ban zomboid jail with tighter thresholds (5 retries/4h, 1w ban)
- Graylog streams for zomboid-connections, zomboid-ratelimit, fail2ban
- GeoIP pipeline enrichment for zomboid traffic
- Fluent-bit inputs for ratelimit logs and fail2ban events
- Remove Legendary Katana mod (Workshop 3418366499) - removed from Steam
- Bump Immich to v2.5.0
- Fix fulfillr config (nil → null)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 15:09:26 -05:00
Bastian de Byl 33eceff1fe feat: add personal uptime kuma instance at uptime.debyl.io
- Add uptime-kuma-personal container on port 3002
- Add Caddy config for uptime.debyl.io with IP restriction
- Update both uptime-kuma instances to 2.0.2
- Rename debyltech tag from uptime-kuma to uptime-debyltech

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 08:04:33 -05:00
Bastian de Byl bc26fcd1f9 chore: fluent-bit zomboid, zomboid stats, home assistant, gregbot 2026-01-24 17:08:05 -05:00
Bastian de Byl 2b4844b211 feat: add fulfillr outreach email configuration
- Update street2 address to Unit 95
- Add outreach config with DynamoDB tables and SES settings

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 23:11:58 -05:00
Bastian de Byl 2c7704b6f9 feat: add zomboid world reset via systemd path unit
Deploy systemd path unit that watches for trigger file from Discord
bot and executes world reset script to delete saves and restart server.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 23:11:54 -05:00
Bastian de Byl c2d117bd95 feat: add systemd timer for zomboid container stats
Deploy systemd timer that writes zomboid container stats to
zomboid-stats.json every 30 seconds for gregtime to read.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-22 23:10:05 -05:00
Bastian de Byl c96aeafb3f feat: add git.skudak.com Gitea instance and skudak domain migrations
Gitea Skudak (git.skudak.com):
- New Gitea instance with PostgreSQL in podman pod under git user
- SSH access via Gitea's built-in SSH server on port 2222
- Registration restricted to @skudak.com emails with email confirmation
- SMTP configured for email delivery

Domain migrations:
- wiki.skudakrennsport.com → wiki.skudak.com (302 redirect)
- cloud.skudakrennsport.com + cloud.skudak.com (dual-domain serving)
- BookStack APP_URL updated to wiki.skudak.com
- Nextcloud trusted_domains updated for cloud.skudak.com

Infrastructure:
- SELinux context for git user container storage (container_file_t)
- Firewall rule for port 2222/tcp (Gitea Skudak SSH)
- Caddy reverse proxy for git.skudak.com

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-15 22:27:02 -05:00
Bastian de Byl 9e665a841d chore: non-cifs nextcloud, partsy, zomboid updates 2026-01-15 16:48:07 -05:00
Bastian de Byl 34b45853e2 graylog updates, test.debyl.io, scripts for reference 2026-01-13 16:08:38 -05:00
Bastian de Byl 364047558c fix: add backinstock_table for fulfillr config 2026-01-09 15:16:47 -05:00
Bastian de Byl 6af3c5dc69 feat: add comprehensive access logging to Graylog with GeoIP
- Add fluent-bit inputs for Caddy access logs (JSON) and SSH logs
- Create GeoIP task to download MaxMind GeoLite2-City database
- Mount GeoIP database in Graylog container
- Enable Gitea access logging via environment variables
- Add parsers.conf for Caddy JSON log parsing
- Remove unused nosql/redis container and configuration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-09 15:16:21 -05:00
Bastian de Byl cf200d82d6 chore: gitea-actions improvements, graylog/fluent-bit logging, zomboid mod
- Gitea actions: add handlers, improve deps and service template
- Graylog: simplify container config, add Caddy reverse proxy
- Add fluent-bit container for log forwarding
- Add ClimbDownRope mod (Workshop ID: 3000725405) to zomboid

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-03 17:20:18 -05:00
Bastian de Byl 38561cb968 gitea, zomboid updates, ssh key fixes 2025-12-19 10:39:56 -05:00
Bastian de Byl 8c21923358 zomboid added, caddyfile updates, debylio migration, ddns migration 2025-12-13 21:18:33 -05:00
Bastian de Byl 28fe5937fe updates for gregtime, caddyfile, added uptime-kuma 2025-11-02 14:18:45 -05:00
Bastian de Byl 37c7259cf7 replace partkeepr with partsy, make private 2025-10-21 16:40:56 -04:00
Bastian de Byl 812b5b30a1 Add CORS support to fulfillr API for local development
- Configure Caddy to send CORS headers for /api/* endpoints
- Allow all origins (*) since API is already IP-restricted to local network
- Handle preflight OPTIONS requests properly
- Update container versions (immich, fulfillr)

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-16 15:23:29 -04:00
Bastian de Byl 9c9da4f47c Complete infrastructure migration from nginx + ModSecurity to Caddy
This commit finalizes the comprehensive migration from nginx + ModSecurity + manual LetsEncrypt
to Caddy v2 with automatic HTTPS. The migration eliminates over 2000 lines of complex
configuration in favor of a single, simplified Caddyfile.

## Major Changes:

### Infrastructure Transformation
- **Web Server**: Replaced nginx with Caddy v2 for automatic HTTPS and simplified configuration
- **SSL/TLS**: Removed manual LetsEncrypt management, now fully automated by Caddy
- **Security**: Replaced ModSecurity WAF with Caddy's built-in security features
- **CI/CD**: Decommissioned Drone CI infrastructure completely

### Configuration Simplification
- **Before**: 20+ nginx site configs, ModSecurity rules, LetsEncrypt cron jobs
- **After**: Single Caddyfile with automatic HTTPS, security headers, and IP restrictions
- **Reduction**: 75% less configuration code while maintaining all functionality

### Files Added
- Caddy container deployment and configuration tasks
- Single Caddyfile template replacing all nginx configs
- Updated documentation (CLAUDE.md, TODO.md)

### Files Removed
- Complete nginx role and all site configurations (24 files)
- SSL role with LetsEncrypt management (6 files)
- Drone CI infrastructure (1 file)
- nginx static files and ModSecurity includes (2 files)

## Verified Functionality
All websites confirmed working with HTTPS certificates automatically provisioned:
- photos.bdebyl.net, parts.bdebyl.net, cloud.bdebyl.net
- wiki.skudakrennsport.com, cloud.skudakrennsport.com
- fulfillr.debyltech.com (with IP restrictions)
- Proper security headers and WebSocket support

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-11 20:38:45 -04:00
Bastian de Byl 6b813362ca noticket - cleanup of unused sites, containers 2025-03-01 20:47:53 -05:00
Bastian de Byl 761bb67b5c noticket - add self-hosted bitwarden for skudak 2025-02-07 19:39:32 -05:00
Bastian de Byl fced2a0038 noticket - add base site, update secrets 2025-02-03 12:34:41 -05:00
Bastian de Byl 65495ba863 noticket - updates to photos/immich 2025-01-08 15:09:31 -05:00