Commit graph

363 commits

Author SHA1 Message Date
221bd6b473 Merge branch 'main' of https://forgejo.basicstack.de/basicstack/stack.basicstack.de 2026-10-24 11:09:14 +02:00
b09fe3b23d imagePullPolicy: Always 2026-10-24 11:08:42 +02:00
CTO
ab60ef78f9 fix(argocd): bump application-controller memory 512Mi→1Gi (OOMKilled 1919× in 7d, DEV-596 preflight unblocker)
Container was repeatedly OOMKilled (exit 137) every ~5 s of live time since
2026-10-11 (pod age 6d23h, 1919 restarts). Live steady-state memory is now
~321 MiB with 1Gi/512Mi settings, so comfortable headroom. Patched the live
StatefulSet first to recover the control plane; this commit lets GitOps
retain the fix on the next reconcile.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-18 01:07:26 +00:00
CTO (Paperclip Agent)
5b627f9e73 feat(os-update): bounded retry on post-uncordon health gate (DEV-595)
The 180s post-uncordon health check in `update-node.sh` step 7 was single-shot.
Any transient yellow signal (Init:*, Running-but-not-Ready, deployment below
replicas during reschedule) at the exact moment of the check would FATAL the
whole cycle and require operator wake + manual `--start-from <next-node>`.
During DEV-593 this fired three times in one cycle (opencloud init, harbor
jobservice reinit) for conditions that cleared within seconds.

Wrap the health check in a bounded retry loop:
- First check at `POST_UNCORDON_WAIT_SECONDS` (default 180s, unchanged).
- Up to `HEALTH_MAX_RETRIES` (default 10) more attempts at
  `HEALTH_RETRY_INTERVAL_SECONDS` (default 30s) = +5 min max.
- Short-circuit halt immediately on persistent pod errors
  (CrashLoopBackOff, ImagePullBackOff, ErrImagePull,
  CreateContainerConfigError, InvalidImageName) — these don't recover on
  their own and shouldn't burn the retry budget.
- Each retry calls `cluster-health.sh` with `RETRY_ON_TRANSIENT=0` so the
  outer loop controls cadence (no double 30s sleeps per attempt).

No k3s or manifest changes. Preflight path is untouched.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-11 02:57:31 +00:00
CTO (Paperclip Agent)
4fea809116 chore: restore README.md to non-executable (accidental mode flip)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-11 02:57:31 +00:00
CTO (Paperclip Agent)
da9777358b feat(os-update): SSH-key drift preflight (DEV-587)
DEV-586 (2026-09-27) aborted mid-drain because cp-1's pubkey had drifted out
of four nodes' /root/.ssh/authorized_keys — probably Hetzner cloud-init
re-syncing from the HC SSH-keys API on reboot (the operator key is not in
that keyset). Both the apt-phase SSH and the CP peer-probe SSH died once
they tried to reach the drifted targets, and recovery cost real time.

os-update.sh now runs a preflight against the fully-computed plan:
- Reads cp-1's pubkey once (fails loudly if operator can't reach cp-1).
- For every planned worker + CP: proves operator SSH works, then grep-checks
  cp-1's pubkey in /root/.ssh/authorized_keys. Missing pubkeys are appended
  via the operator's own SSH (auto-heal). Verify via grep after the write.
- Nodes still unreachable at the end block the whole cycle with the exact
  fix command in the log — nothing gets drained until this passes.
- Preflight report: /tmp/os-update-<UTC-ts>/ssh-preflight.txt (ok/repaired/
  problem lists) as audit trail.
- Escape hatch: --skip-ssh-preflight for bootstrap scenarios.

Also factors the node->ssh-endpoint case into a shared node_ssh_endpoint()
helper so the preflight, finalize/apt-history loop, and any future call site
can't drift apart.

Verified end-to-end against the live cluster: all planned nodes reachable
and cp-1's pubkey currently present on cp-2/cp-3, worker-1..6.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-11 02:57:31 +00:00
CTO
a71c5d9a63 fix(opencloud): reduce CPU request 250m→50m to free worker-2 slot for backup CronJob (DEV-594)
Worker-2 CPU requests were pinned at 100% (2000m/2000m), leaving no room
for the daily opencloud-backup CronJob (needs 20m). Actual opencloud CPU
usage is ~3m — reserving 250m was 80x over actual. Reduce to 50m which is
still 15x headroom. Frees 200m on worker-2.

Backup CronJob is pinned to the opencloud node by RWO PVC + hard
podAffinity. Reducing opencloud's own CPU request is the cleanest
unblock — contained to the opencloud namespace, no cross-app blast
radius.

Related: DEV-593, DEV-464, DEV-481, DEV-514, DEV-582/584 (recurring
pattern of backup CronJob squeezed off worker-2).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-11 01:11:29 +00:00
CTO
0164c8cb4e fix(velero-drill): switch webhook from bearer to github_hmac (DEV-590)
The 2026-10-01 monthly drill passed but the post-run webhook got
HTTP 401 from the home Paperclip. Root cause: the home server's
global actorMiddleware (/app/server/src/middleware/auth.ts) rejects
every Authorization: Bearer <...> that is not a board API key or an
agent JWT before per-route handlers run, so the trigger's own
`bearer` signingMode handler is unreachable with a 48-hex random
secret. Only publicMcpGatewayProtocolPath is exempted.

Fix:
- Trigger 6983798e-018c-459d-95ea-4b5959441e2d switched to
  signingMode: github_hmac via PATCH (secret unchanged).
- post_webhook now computes HMAC-SHA256(body, secret) with openssl
  (already in alpine/k8s) and sends X-Hub-Signature-256 instead of
  Authorization: Bearer. HMAC headers bypass the Bearer branch of
  the global middleware entirely.

Verified end-to-end: synthetic HMAC-signed body posted to
.../fire returned 202 issue_created and spawned DEV-591 under
parent DEV-578. SealedSecret is unchanged (same secret value
reused as the HMAC key).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-01 04:45:51 +00:00
5188784e86 apps/grist/grist-deployment.yaml aktualisiert 2026-09-27 09:55:23 +00:00
CTO
9305eb7415 fix(monitoring): Prometheus deployment source-of-truth + Recreate strategy (DEV-583)
Adds apps/monitoring/prometheus-deployment.yaml capturing the live PVC/Service/
Deployment for monitoring/prometheus. Two behaviour changes vs the previous
live state:

- strategy: RollingUpdate -> Recreate. prometheus-data-encrypted is RWO on
  hcloud-volumes-encrypted; a rolling update deadlocks because the surge pod
  cannot attach the PVC while the old pod holds it. This has been jamming the
  weekly OS-update preflight (see DEV-582). Same fix pattern as loki
  (loki-deployment.yaml) and opencloud (DEV-538).
- pod-template annotation backup.velero.io/backup-volumes-excludes:
  prometheus-storage. Restores the annotation that rev 6 tried to add before
  the RollingUpdate deadlock forced a rollback in DEV-582.

Applied and verified: rollout succeeded, /-/ready and /-/healthy both green,
pod running on k3s-worker-6 after PVC re-attach.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-13 01:11:07 +00:00
CTO (Paperclip Agent)
7368283d76 retire(monitoring): remove backup-k8s-resources CronJob + restic alerts (DEV-576)
Velero cluster-all now covers cluster-manifest backup with two successive
Completed runs verified and one restore drill (openwebui) green (DEV-575).

- Delete apps/monitoring/backup-k8s-resources-cronjob.yaml
- Drop BackupK8sResourcesStale + BackupK8sResourcesCheckFailed from
  backup-restic-alerts.yaml and matching series/cases from the promtool
  test file (all 9 remaining rules pass `promtool test rules`)
- Update apps/monitoring/README.md + prometheus-backup-cronjob comments
- Runbook lives in BookStack: EN
  https://books.basicstack.de/books/it-operations-manual/page/velero-backup-restore-runbook
  DE
  https://books.basicstack.de/books/it-betriebshandbuch/page/velero-backup-und-restore-runbook

Rollback: revert this commit — Argo restores the CronJob + alerts and
restic k8s-resources runs resume at the next 02:00 UTC schedule.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-12 18:24:07 +00:00
CTO
765962c936 fix(velero-drill): hostAliases to reach paperclip.home webhook from cluster (DEV-579)
Public DNS for paperclip.home.imicros.de returns the LAN IP 10.0.0.2, so the
drill's webhook POST was landing with HTTP 000 from Hetzner. The home reverse
proxy at 46.225.155.185 already serves that vhost publicly, so pinning the
hostname to the home public IP in the CronJob's hostAliases lets the webhook
fire the Paperclip routine without any home-network or SealedSecret changes.

Verified end-to-end: bearer POST via --resolve returns HTTP 202 with
status=issue_created (run issue b07fc035 created under DEV-578).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-12 18:15:51 +00:00
CTO (Paperclip Agent)
ac9f36b1e7 feat(velero): monthly restore-drill CronJob for openwebui (DEV-575)
- CronJob at 0 4 1 * * restores the latest openwebui-manifests backup into
  a transient restore-test-<ts> namespace, diffs core resource-kind counts
  against the live openwebui namespace, and deletes the target namespace.
- ServiceAccount + ClusterRole scoped to velero.io + namespace + configmap
  ops. Script enforces the 'restore-test-' name prefix before deleting a
  namespace. Cluster-wide 'view' bound for the diff.
- Failure surfaces via kube_job_status_failed (DEV-574 alert stack) and via
  ConfigMap velero-restore-drill-last-result written on every run.
- Optional Paperclip webhook Secret is sealed but unreachable from Hetzner
  right now (home Paperclip is LAN-only); wired as optional secretKeyRef so
  the drill runs regardless. Connectivity is a follow-up.
- Argo Application velero-restore-drill points at velero/restore-drill/.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-12 18:03:37 +00:00
CTO (Paperclip Agent)
6a66924595 feat(velero): ServiceMonitor + PrometheusRule for backup alerts (DEV-574)
Adds Prometheus scrape of Velero's :8085 metrics and four alerts
(VeleroBackupFailed, VeleroBackupPartial, VeleroBackupMissed,
VeleroStorageLocationUnavailable) labeled team=infra severity=warning.

VeleroStorageLocationUnavailable substitutes for the spec's
VeleroRepoUnhealthy since Velero v1.18.1 does not expose
velero_backup_repository_available (see comment in the rule).

Wired via a new ArgoCD Application (velero-monitoring) that syncs
only servicemonitor.yaml + prometheusrule.yaml from basicstack-repo/velero/.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-12 17:58:45 +00:00
CTO (Paperclip Agent)
ec3e9624b3 feat(velero): schedules + PVC exclude annotations (DEV-573)
- velero/schedules/: cluster-all @ 03:00 (30d TTL, manifests only),
  per-namespace <ns>-manifests @ 03:15+n min (30 in-scope namespaces,
  30d TTL, no fs backup), and per-namespace <ns>-data @ 03:45+n min
  for the whitelist (pocket-id, vaultwarden, vikunja, passbolt;
  14d TTL, defaultVolumesToFsBackup=true).
- All schedules exclude velero, kube-system, kube-node-lease,
  kube-public so the Kopia password Secret never lands in S3.
- apps/app-velero-schedules.yaml: sibling Argo app syncing that
  directory into namespace velero with prune+selfHeal.
- opencloud, harbor(registry+trivy): pod-template annotation
  backup.velero.io/backup-volumes-excludes as a safety net so blob-
  heavy PVCs are never fs-backed-up even under a manual defaultVolumes
  run. nextcloud/monitoring workloads are not Argo-tracked yet — they
  were patched live in the same commit but need a follow-up to source-
  control them.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-12 17:35:49 +00:00
CTO (Paperclip Agent)
9b8cfebbd5 velero: reseal S3 credential with new Hetzner project key
The prior sealed secret's access key lived in the personal `imicros.de`
Hetzner Object Storage project and could not list objects in
`basicstack-velero` (bucket is in the BasicStack project) — BSL was
`Unavailable` with an S3 `AccessDenied` on ListObjectsV2.

CEO created a new S3 credential in the BasicStack project; re-sealed
against the cluster controller and applied. BSL `default` now
`Available`.

Refs DEV-572.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-12 17:25:15 +00:00
CTO Agent
60cc931d6b fix(velero): pin AWS plugin to v1.14.2 (v1.14.3 unreleased); shrink node-agent memory req to fit workers (DEV-572)
- velero-plugin-for-aws v1.14.3 is not published on docker.io (only rc.1/rc.2). Downgrade to the latest GA tag v1.14.2 — still compatible with Velero 1.18.x.
- Drop node-agent memory request 256Mi -> 64Mi so the daemonset schedules on worker-1/2/3 (they sit at ~95% memory-request already).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-12 17:08:09 +00:00
CTO Agent
b3d98bf902 feat(velero): Argo app + Helm values for Kopia/Hetzner S3 backups (DEV-572)
- apps/app-velero.yaml: Argo Application, path apps/velero, ns velero,
  CreateNamespace + ServerSideApply (chart uses long CRDs).
- apps/velero/Chart.yaml: pinned vmware-tanzu velero chart 12.1.0
  (appVersion 1.18.1).
- apps/velero/values.yaml: Kopia uploader, no CSI, node-agent enabled,
  AWS S3 provider -> https://hel1.your-objectstorage.com, dedicated
  bucket basicstack-velero (per DEV-570), BSL credential
  velero-s3-credentials/cloud, Kopia repo password auto-picked from
  velero-repo-credentials/repository-password.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-12 17:05:42 +00:00
CTO Agent
f318719c59 velero(secrets): seal dedicated Hetzner OS S3 credential (DEV-570)
Adds SealedSecret velero-s3-credentials in namespace velero, holding
the dedicated velero-prod Hetzner Object Storage access/secret key
(scoped only to the basicstack-velero bucket).

Sealed on cp-1 via /root/seal-velero-s3-credentials.sh with the
sealed-secrets-controller public cert; plaintext never left the host.
Only the in-cluster controller can decrypt.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-12 17:00:43 +00:00
CTO Agent
0c82ded5f7 velero(secrets): seal Kopia repo password for velero namespace
Adds the SealedSecret for the Kopia repository password that the Velero
BackupRepository will consume once the Argo app lands. Raw password is
held on cp-1 (root:root, 0600) pending the CEO handoff to Vaultwarden
and the offline safe copy (DEV-571).

Refs: DEV-569, DEV-571

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-12 16:22:22 +00:00
CTO
b2ee3ffa67 feat(openwebui): deploy Open WebUI v0.11.3 with Pocket ID OIDC (DEV-567)
Adds the Open WebUI stack at openwebui.basicstack.de:

- Namespace `openwebui`, ArgoCD Application with auto-sync + selfHeal
- Own Postgres 16 + pgvector (`pgvector/pgvector:pg16`, 20Gi encrypted PVC,
  fsn1-pinned); VECTOR_DB=pgvector, extension auto-created
- Own Redis 7 (2Gi encrypted PVC, fsn1-pinned) used both as cache and as
  websocket manager (db=1)
- Hetzner Object Storage bucket `openwebui-basicstack` (hel1, path-style)
  for file uploads; shared project S3 credentials
- Traefik ingress + Let's Encrypt TLS
- Pocket ID client `Open WebUI` (PKCE on) restricted to groups
  `openwebui_admins` and `openwebui_users`; `andreas.leinen@basicstack.de`
  is member of both and gets admin via OAUTH_ADMIN_ROLES
- Password login disabled (`ENABLE_LOGIN_FORM=false`, `ENABLE_SIGNUP=false`)

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-12 14:06:56 +00:00
CTO
0e796dff14 fix(os-update): accept 401/403 as /livez alive in CP reboot poll (DEV-566)
k3s api-server /livez on this cluster is auth-gated and returns HTTP 401
without a bearer token. The Phase E reboot poll hardcoded HTTP 200, so
every CP reboot would time out at 10 min ("api-server did not return
within 600s") even though cp-2 was actually Ready and etcd-rejoined
within ~90 s.

Any HTTP response from the api-server (200/401/403) means it is up and
serving; only network-level failures (000, connection refused) count as
"not yet back". Added --max-time 5 to keep polling snappy immediately
after the reboot when TCP may accept-then-drop.

Verified during DEV-566 cycle:
  curl -sk -o/dev/null -w '%{http_code}' https://<cp>:6443/livez -> 401
on cp-1, cp-2, cp-3 (all Ready).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-06 01:47:47 +00:00
CTO
b7e34236db fix(os-update): add k3s-worker-6 to SSH target maps (DEV-566)
k3s-worker-6 was joined on 2026-08-30 but never added to the
node_ssh_target case in update-node.sh, so the weekly cycle would die
with 'unknown node k3s-worker-6' as soon as it reached that node. Map
it to its public IP (49.12.223.133) — same pattern as worker-3/4/5.

Also add the corresponding case in os-update.sh's apt-history digest.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-06 01:04:35 +00:00
CTO
b2b0d27803 fix(vikunja): set pod FQDN so SMTP EHLO passes Stalwart validation (DEV-563)
Stalwart's port-25 relay rejects bare EHLO labels — needs at least one
dot. Vikunja doesn't call go-mail's WithHELO, so it sends
os.Hostname() as EHLO. Setting hostname/subdomain plus
setHostnameAsFQDN=true makes the pod's kernel hostname a valid FQDN
(vikunja.basicstack.vikunja.svc.cluster.local), which Stalwart
accepts.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-05 10:05:26 +00:00
CTO
2ec560e090 feat(vikunja): enable mailer via port-25 in-cluster relay (DEV-563)
Vikunja now sends reminder/notification emails through
`stalwart-smtp.stalwart.svc.cluster.local:25` using unauthenticated
relay from the pod-network CIDR that Stalwart already trusts. The
`vikunja@basicstack.de` principal exists in Stalwart but its password
is unknown and admin API access to reset it is currently broken
(admin/andreas/paperclip password auth returns `{"type":"failure"}` per
`stalwart-oidc-only-admin` note). Verified end-to-end: message from
`vikunja@basicstack.de` sent via port-25 relay arrived in the passbolt
mailbox.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-05 10:03:07 +00:00
CTO
bf7e78a573 feat(kaneo): wire SMTP submission via kaneo@basicstack.de (DEV-562)
Adds the kaneo-smtp SealedSecret and points the Kaneo container at
mail.basicstack.de:587 (STARTTLS) for outbound workspace invites and
notifications. DISABLE_EMAIL_OTP_SIGN_IN stays true so sign-in remains
OIDC-only via Pocket ID.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-05 10:01:52 +00:00
CTO
2c9e90ac2b docs(outline): note PKCE must be disabled on the Pocket ID client
Outline's OIDC plugin does not send `code_challenge`, so the Pocket ID
client must be created with `pkceEnabled: false` (default is `true`).
Login fails with `?notice=auth-error` and Pocket ID rejects
`/authorize` when PKCE is enforced.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-05 09:57:39 +00:00
CTO
244ff86714 docs(vikunja): note PKCE must be off on Pocket ID client (DEV-560)
Vikunja 2.x's frontend redirect helper and Go backend do not send
code_challenge. Pocket ID rejects the /authorize call with
'code_challenge parameter is missing' when the client has pkceEnabled=true.
The Vikunja client is confidential (isPublic=false) so client_secret still
authenticates the token exchange — turning PKCE off is safe and required.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-05 09:56:07 +00:00
CTO
18eba97fcb feat(vikunja): request vikunja_scope for OIDC group→team mapping (DEV-564)
Add `vikunja_scope` to the OIDC scope list the Vikunja app requests
from Pocket ID, so the `vikunja_groups` custom claim is available for
Vikunja's automatic team provisioning.

The claim itself is set per Pocket ID user group (`vikunja_admins`,
`vikunja_members`) via `PUT /api/custom-claims/user-group/:id`.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-05 09:47:37 +00:00
cto-agent
38c7b8c624 feat(outline): deploy Outline knowledge base at outline.basicstack.de (DEV-559)
- Namespace `outline` with Postgres 16, Redis 7, and Outline 1.10.0
- Stateful pods pinned to Hetzner location `fsn1` (encrypted CSI volumes)
- Pocket ID OIDC integration (group-restricted to outline_admins/outline_members)
- Hetzner Object Storage bucket `outline-basicstack` (region hel1) with CORS
- Traefik Ingress with cert-manager `letsencrypt-prod` (DNS-01)
- Sealed secrets for postgres, app (SECRET_KEY/UTILS_SECRET/URLs), OIDC, S3
- Argo CD Application `outline` in `apps/app-outline.yaml`

SMTP notifications deferred (see apps/outline/README.md).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-05 09:42:21 +00:00
CTO
c184eeaff3 chore(vikunja): rename JWT env to non-deprecated service.secret (DEV-560)
Vikunja 2.6.0 deprecates `service.jwtsecret` in favor of `service.secret`;
switch the env var name (secret payload unchanged) and fix the admin-promotion
example in the README to the current CLI (`user set-admin --admin`).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-05 09:41:05 +00:00
CTO
1743815703 feat(vikunja): deploy Vikunja 2.6.0 with Pocket ID OIDC (DEV-560)
Namespace-scoped Postgres 16 + unified Vikunja container behind Traefik
ingress at vikunja.basicstack.de. Both pods pinned to Hetzner fsn1 due to
their encrypted PVCs (10 Gi DB, 10 Gi attachments). Local auth disabled;
sign-in only via Pocket ID (client `Vikunja`, groups `vikunja_admins` /
`vikunja_members`). SMTP mailer left disabled pending mailbox provisioning.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-05 09:38:25 +00:00
CTO
28a28bf7c0 feat(kaneo): deploy Kaneo project management (DEV-561)
Hand-rolled manifests under apps/kaneo/ + Argo Application:

- Dedicated PostgreSQL 16-alpine on hcloud-volumes-encrypted (20Gi),
  pinned to fsn1 via csi.hetzner.cloud/location node affinity.
- Kaneo v2.22.0 (single ghcr.io/usekaneo/kaneo image; API + web on
  5173). Pinned to fsn1 for consistency with the volume.
- Traefik ingress at kaneo.basicstack.de with letsencrypt-prod cert.
- Better Auth JWT secret and Postgres credentials as SealedSecrets.
- OIDC via Pocket ID using CUSTOM_OAUTH_*, restricted to the two
  new Pocket ID groups kaneo-admins / kaneo-members (client id
  d58ff9bb-dc98-49b4-9535-4af2aba66de2).
- Registration + password + email-OTP sign-in disabled; SSO only.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-05 09:38:04 +00:00
dda91a7471 apps/grist/grist-deployment.yaml aktualisiert 2026-08-30 12:03:03 +00:00
CTO
f29e2a5cb5 chore(grist): enable ArgoCD auto-sync + selfHeal (DEV-556)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-30 11:07:29 +00:00
CTO
8cdfd0e2f9 feat(grist): deploy Grist community edition (DEV-556)
- New namespace grist with in-namespace PostgreSQL 16
- Ingress grist.basicstack.de + letsencrypt-prod (DNS-01 via Hetzner)
- Pocket ID OIDC login (client "Grist", group "grist")
- All PVCs on hcloud-volumes-encrypted
- ArgoCD application via apps/app-grist.yaml

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-30 11:05:20 +00:00
CTO Agent
a61f0cd630 fix(opencloud): shrink backup pod requests to fit worker-3 (DEV-555)
The scheduled backup pod could not start because worker-3 (where the
opencloud data PVC is attached and where podAffinity pins the backup)
had ~40Mi of memory left by requests after opencloud/tika were
co-scheduled. rclone actually uses well under 100Mi during the sync.

Drop cpu request 100m -> 50m and memory request 256Mi -> 64Mi. Limits
unchanged (500m / 512Mi) so it can still burst.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-30 10:26:13 +00:00
CTO Agent
d5e88d884e docs(infra): refresh k3s ops doc and add --node-ip to provision script (DEV-410)
- provision-k3s-worker.sh now sets --node-ip=<private-ip> on the agent
  install so k3s does not default to the public IP (DEV-224 root cause)
- K3S_OPERATIONS.md node table reflects the current cluster (cp-2/cp-3,
  worker-4/5/6), documents the Hetzner Cloud Volume datacenter constraint,
  and calls out the --node-ip requirement in the manual join steps

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-30 10:07:25 +00:00
CTO Agent
a30274d1eb feat(os-update): integrate control planes into weekly rolling cycle (DEV-553)
os-update.sh now walks all three CPs after workers, one at a time, etcd
followers first and leader last. Each CP is delegated to update-cp-node.sh
--run with ASSUME_YES=1; a health gate (cluster-health.sh +
etcdctl endpoint status) runs between CPs. Adds --skip-cps and --only-cps
flags; --only <node> and --start-from <node> now route to the correct list
automatically.

OS_UPDATE_PROCEDURE.md updated: CPs are no longer excluded, §8 renamed to
"Control plane handling" and describes the leader-last cycle + delegation.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-30 09:41:22 +00:00
CTO
c97a2d218e chore(stalwart): bump v0.16.11 -> v0.16.18 (DEV-542)
Transport-only patch bump. No auth/OIDC/config changes.
Snapshots (config.json, DB dump, live YAML) attached to DEV-542.
Rollback: git revert; ArgoCD resyncs; PVCs retained.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-23 16:46:31 +00:00
CTO
e69e3d8eb4 chore(restic): bump image 0.17.3 -> 0.19.1 (DEV-541)
Mirrored docker.io/restic/restic:0.19.1 to
harbor.basicstack.de/library/restic:0.19.1 (crane in-cluster Job).
Updated all CronJob pins and the restore-drill/tag-bump docs.

Digest: sha256:136600b6ff6843d61d355f7f71f460a166429f35de6fd11b568fece3c9a4d510

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-23 15:52:15 +00:00
CTO Agent
96c3846202 chore(stalwart): bump alpine 3.19 -> 3.24 in backup cronjob (DEV-539)
Only occurrence of plain alpine:3.19 across apps/**. Touches:
- apps/stalwart/stalwart-fresh-deployment.yaml (stalwart-backup CronJob container)

Scope excludes alpine/k8s (see DEV-520 majors phase).

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-23 15:42:36 +00:00
CTO Agent
76e250c334 fix(opencloud): use Recreate rollout strategy for RWO PVC (DEV-538)
The 7.2.0 -> 7.4.0 rollout stalled because the default RollingUpdate
tried to start the new pod before terminating the old one, but they
both mount opencloud-data (RWO), causing Multi-Attach errors.

Switch to strategy: Recreate so the old pod is terminated first and
the volume detaches cleanly before the new pod starts. Same fix
pattern as bookstack (DEV-528) and other RWO-backed apps.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-23 15:39:17 +00:00
CTO Agent
0887f87c82 chore(opencloud): bump 7.2.0 -> 7.4.0 (DEV-538)
Skimmed 7.3.0 (2026-07-14) and 7.4.0 (2026-08-03) release notes: no
breaking changes, no config or index migrations required.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-23 15:35:03 +00:00
CTO
a7e81db4e1 chore(directus): bump 12.1.1 -> 12.3.0 (DEV-537)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-23 15:27:14 +00:00
CTO
777a4d0404 chore(pocket-id): bump v2.11.0 -> v2.14.0 (DEV-536)
Minor version bump of the cluster OIDC provider. No breaking changes;
multi-client-secret support in 2.14 is additive; SameSite=Lax cookie
default in 2.13 is standard. DB snapshot captured pre-rollout.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-23 15:18:23 +00:00
CTO
3317401eba chore(headlamp): bump v0.43.0 -> v0.45.0 (DEV-535)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-23 15:14:38 +00:00
CTO Agent
71496fc5ac chore(dozzle): bump v10.6.10 -> v10.7.3 (DEV-534)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-23 15:11:47 +00:00
CTO Agent
6313b85999 chore(pangolin): bump postgres 17.5 -> 17.11 (DEV-533)
Patch bump per DEV-520 plan. Only pangolin uses postgres:17.5*;
other apps (paperclip, directus, forgejo, stalwart) are on
postgres:16-alpine and are out of scope for this ticket.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-23 15:07:38 +00:00
CTO Agent
7cbb603ec1 chore(pangolin): bump busybox 1.37 -> 1.38.0 (DEV-532)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-08-23 15:02:26 +00:00