Commit Graph
98 Commits
Author SHA1 Message Date
valknarandClaude Sonnet 4.6 aaa4564a0b revert(gitea): remove custom images, use default Gitea logo
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 19:27:12 +02:00
valknarandClaude Sonnet 4.6 824c78535b fix(gitea): transparent background, flower maximized to fill square
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 19:01:14 +02:00
valknarandClaude Sonnet 4.6 b46f553457 feat(gitea): replace icons with square Catppuccin Frappé peony icon
Dark Frappé base (#303446) background with the pivoine.art peony
centered and padded. Replaces logo.svg/png, favicon.svg/png, and
apple-touch-icon.png.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 18:52:35 +02:00
valknarandClaude Sonnet 4.6 66536adde7 fix(gitea): set default theme to catppuccin-frappe-blue
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 18:04:21 +02:00
valknarandClaude Sonnet 4.6 9065421264 feat(gitea): replace custom themes with Catppuccin v1.0.2
70 CSS files covering all 4 flavors (Latte/Frappé/Macchiato/Mocha)
× 14 accents + 14 auto variants. Default theme set to Mocha Mauve.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-29 18:01:35 +02:00
valknarandClaude Sonnet 4.6 ec92d55e3c fix(mailpit): bind UI to port 8025 so healthcheck passes; revert email FROM changes
Port 0 disabled the web UI but broke the built-in /readyz healthcheck.
Binding to 8025 keeps the UI accessible within the Docker network only
(no Traefik labels) and lets the container report healthy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 19:34:40 +02:00
valknarandClaude Sonnet 4.6 1be281ec97 fix(mail): change FROM address to hi@pivoine.art for IONOS relay compatibility
IONOS rejects relay from VPS IPs when sender doesn't match authenticated
account. Switch gitea and passbolt FROM to the actual IONOS account address.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-27 19:09:00 +02:00
valknarandClaude Sonnet 4.6 7ae681cc83 docs(stacks): update README — stacks.sh moved to scripts repo, use stacks alias
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 15:53:16 +02:00
valknarandClaude Sonnet 4.6 056db5d6ea refactor(stacks): move stacks.sh to ~/scripts repo
stacks.sh now lives in git@dev.pivoine.art:valknar/scripts.git
and resolves STACKS_DIR from env or defaults to ~/stacks.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 15:47:30 +02:00
valknarandClaude Sonnet 4.6 a7a3fb8582 fix(stacks): show correct status when backup service has failed
Same fix as the update service: gate "not installed" on unit file
existence rather than systemctl exit code, which is non-zero for
failed services too.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 02:21:44 +02:00
valknarandClaude Sonnet 4.6 04a79ea198 fix(stacks): show correct status when update service has failed
systemctl status exits non-zero for failed services (exit 3), not
only missing ones (exit 4), causing the || warn branch to always fire
after a failed run. Gate the "not installed" warning on whether the
unit file exists instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 02:21:00 +02:00
valknarandClaude Sonnet 4.6 a08c5fca39 fix(stacks): prevent update run from aborting when images not in local cache
docker image inspect returns exit code 1 for images not yet pulled.
With set -euo pipefail, this silently killed the script mid-loop.
Add || true so an uncached image yields empty before/after (correct
first-run behaviour: pull fires, before != after, stack gets started).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-20 02:17:49 +02:00
valknarandClaude Sonnet 4.6 f65034a2e2 refactor(gitea): move VPS IPv4 address to .env as VPS_IPV4
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 20:42:26 +02:00
valknarandClaude Sonnet 4.6 fa0f5cba4b fix(gitea): update SSH_PORT to 22 to match new external port
Clone URLs now show the correct standard port 22.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 20:27:01 +02:00
valknarandClaude Sonnet 4.6 8797a42d61 fix(gitea): bind SSH explicitly to IPv4 to avoid conflict with sshd on IPv6
Docker binds to both [::]:22 and 0.0.0.0:22 without an explicit IP,
conflicting with sshd on [::]:22.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 20:25:59 +02:00
valknarandClaude Sonnet 4.6 1e5d5254a6 feat(gitea): expose SSH on port 22
sshd is now IPv6-only (ListenAddress ::, bindv6only=1), freeing
IPv4 port 22 for Gitea SSH.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 20:25:42 +02:00
valknarandClaude Sonnet 4.6 0f943b5a62 fix(coolify): switch PHP-FPM from ondemand to dynamic mode
ondemand kills all workers after 10s idle, causing cold Laravel
bootstrap (10-54s) on every request after any quiet period.
dynamic keeps min_spare_servers=1 alive at all times; the other
PM_* values (start_servers=2, min=1, max_spare=3) were already
set but ignored under ondemand.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 11:06:07 +02:00
valknarandClaude Sonnet 4.6 8d33836714 fix(gitea): block scraper IP and add rate limiting
74.7.243.196 was crawling valknar/home SVG icon tree at 1.8 req/s.
- Denylist the IP in fail2ban dynamic config
- Add rate-limit@file middleware to Gitea's Traefik router to throttle
  future scrapers on public repos

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 10:27:56 +02:00
valknarandClaude Sonnet 4.6 3265d69a6c fix(traefik): upgrade fail2ban plugin to v0.9.0 and fix config schema
v0.7.1 caused 403 on all routes. v0.9.0 renamed whitelist→allowlist,
changed ip to a comma-separated string, and added enabled/statuscode fields.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 10:09:37 +02:00
valknarandClaude Sonnet 4.6 586f137cf9 feat(traefik): add fail2ban plugin with global middleware
Registers tomMoulard/fail2ban v0.7.1 as an experimental plugin and
applies it globally on the web-secure entrypoint alongside security-headers.
Bans IPs after 5 failures within 10 minutes for 3 hours; whitelists
RFC1918 ranges to protect internal/Docker traffic.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-17 10:05:11 +02:00
valknarandClaude Sonnet 4.6 b69951f917 fix(help): align update subcommands and add backup descriptions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 23:49:57 +02:00
valknarandClaude Sonnet 4.6 d7829cb5ae docs: fix README backup/update/notifications sections
- Source .env before restic init so RESTIC_REPOSITORY is available
- Remove stale references to deleted _backup/.env and _update/.env
- Update Notifications section to point at root .env

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 21:13:00 +02:00
valknarandClaude Sonnet 4.6 8ae9c9e878 fix(update): compare local image store IDs, not running container IDs
docker compose images -q reports the image IDs of currently running
containers, which don't change after a pull — so before == after always
and containers were never recreated.

Fix: resolve each service's image tag to its local SHA256 ID via
docker image inspect, which reads the local image store and correctly
reflects the newly pulled image. Falls back from 'config --images'
(compose v2.19+) to parsing 'config' yaml for older versions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 21:09:56 +02:00
valknarandClaude Sonnet 4.6 fcff6f3298 refactor: absorb _backup and _update into stacks.sh
- Inline update logic (pull → compare digests → up -d → prune → notify)
- Inline backup logic with dynamic Postgres detection: any running
  <stack>_db container is dumped using the <stack>/<stack> convention
- Systemd unit files are now generated on `install` from embedded
  heredocs pointing at stacks.sh itself — no external scripts needed
- Root .env (WEBHOOK_URL, RESTIC_REPOSITORY, RESTIC_PASSWORD) replaces
  the per-service .env files in _backup/ and _update/
- Remove _backup/ and _update/ directories entirely
- Update README accordingly

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 20:56:04 +02:00
valknarandClaude Sonnet 4.6 e3cd2df372 docs: document stacks.sh in README
Replace manual docker compose / systemctl snippets with stacks.sh
equivalents and add a dedicated section covering all commands.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 19:55:05 +02:00
valknarandClaude Sonnet 4.6 067d017ea6 feat(stacks): add --static flag to completion command
Bakes the current stack list into the generated completion script instead
of using runtime directory discovery. Useful for remote hosts where the
stacks dir path differs from the local repo.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 19:52:55 +02:00
valknarandClaude Sonnet 4.6 3251f27edb feat: add stacks.sh stack manager CLI
Bash script for managing Docker Compose stacks with glob filtering,
parallel execution, service management, scaffolding, and shell completion.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 19:13:43 +02:00
valknarandClaude Sonnet 4.6 cf32e669cb fix(gitea): use .RunModeIsProd and hardcode Umami values in header template
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 10:34:21 +02:00
valknarandClaude Sonnet 4.6 e613d766ec fix(gitea): revert to UMAMI_ID/UMAMI_SRC and use .UmamiSrc/.UmamiId template vars
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 10:26:39 +02:00
valknarandClaude Sonnet 4.6 53608a7195 fix(gitea): use .UmamiSrc/.UmamiScript template vars for Umami analytics
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 10:24:57 +02:00
valknarandClaude Sonnet 4.6 735adfabea feat(gitea): add conditional Umami analytics via env vars in header
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-16 10:17:19 +02:00
valknarandClaude Sonnet 4.6 a9eb3de90d docs: add code stack to README
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 18:18:17 +02:00
valknarandClaude Sonnet 4.6 c27a012d54 fix(code): consolidate volumes into single /home/coder mount
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 18:05:43 +02:00
valknarandClaude Sonnet 4.6 d367540eac fix(code): mount workspace volume to /home/coder/workspace
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 18:01:19 +02:00
valknarandClaude Sonnet 4.6 c0aa3acbfc fix(code): mount workspace volume to /home/coder for full home persistence
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 17:59:53 +02:00
valknarandClaude Sonnet 4.6 d9bbfea9c4 fix(code): remove separate workspace volume
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 17:53:22 +02:00
valknarandClaude Sonnet 4.6 27c9e1c8a5 feat(code): add code-server stack with Traefik and Anthropic API support
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 17:31:58 +02:00
valknarandClaude Sonnet 4.6 d4327bd152 fix(update): detect real image changes and improve webhook messages
Use image ID diff instead of grepping "Pulled" (which appears even
when images are already up to date). Add timestamp, stack count, and
updated/total ratio to all notification messages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 07:58:55 +02:00
valknarandClaude Sonnet 4.6 2130069836 fix(update): set executable bit on update.sh in git index
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-12 07:41:24 +02:00
valknarandClaude Sonnet 4.6 6eb603205a fix(update): make update.sh executable and fix Pulled grep pattern
- Add +x permission to update.sh (was causing systemd 203/EXEC failure)
- Remove $ anchor from grep so trailing space in docker output is matched
- Add .data/ to .gitignore

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-11 16:19:36 +00:00
valknarandClaude Sonnet 4.6 f231dd5432 feat: externalize EMAIL_FROM and EMAIL_USER to .env for gitea and passbolt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 18:38:22 +02:00
valknarandClaude Sonnet 4.6 afec1c03f5 feat(gitea): set mailer FROM address
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 18:01:08 +02:00
valknarandClaude Sonnet 4.6 9c7a34e9c0 feat: remove Affine stack
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 13:43:21 +02:00
valknarandClaude Sonnet 4.6 40dfe5776a feat(gitea): add Umami analytics via direct custom header template
Mount templates/custom/ directly to Gitea's default GITEA_CUSTOM templates
path. No entrypoint override or envsubst needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 12:58:25 +02:00
valknarandClaude Sonnet 4.6 e5e6a2c302 feat(gitea): add Umami analytics via custom header template
Injects the Umami tracking script into every Gitea page using a custom
header.tmpl generated at startup via envsubst. Script URL and website ID
are externalized to UMAMI_SCRIPT_URL / UMAMI_WEBSITE_ID env vars.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-10 12:49:26 +02:00
valknarandClaude Sonnet 4.6 9c65cfc9e3 revert(passbolt): remove clock-skew patch — metadata key already created
The patched PublicKeyValidationService.php and its volume mount are
no longer needed now that the metadata key exists in the database.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 22:48:41 +02:00
valknarandClaude Sonnet 4.6 15ce2e3f4b chore: remove accidentally committed log file
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 22:47:34 +02:00
valknarandClaude Sonnet 4.6 300c685d50 feat: remove Vaultwarden — migrated to Passbolt
Credentials migrated to Passbolt CE at passbolt.pivoine.art.
Removed stack files and README entry. Data wiped on VPS.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 22:47:07 +02:00
valknarandClaude Sonnet 4.6 5c398ee77c fix(passbolt): add 300s clock-skew tolerance to key creation date check
The isNotCreatedInTheFutureRule has zero tolerance, causing the
browser extension to fail when generating a metadata key if the
browser clock is even 1 second ahead of the server. Patching
isDateInFuture to allow 300 seconds tolerance and mounting the
file as a read-only volume so the fix survives image updates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 22:16:56 +02:00
valknarandClaude Sonnet 4.6 2e31c1dcc9 fix(passbolt): persist GPG keyring as volume to survive restarts
Passbolt's entrypoint creates /var/lib/passbolt/.gnupg/pubring.kbx
as root while PHP-FPM runs as www-data. Without a volume this file
is recreated with wrong ownership on every container recreate, breaking
all GPG operations. Mounting the dir as a volume keeps the chown
33:33 fix permanent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 21:44:20 +02:00