this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Laptop migration commit

+449 -279
+15
.ripsed/undo.jsonl
··· 1 + {"timestamp":"1775379472","file_path":"/Users/noivanmondfrans/Projects/github.com/Trans-Utrecht-Beyond/infrastructure/k8s/ci/loom-deployment.yaml","entry":{"original_text":"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: loom\n namespace: ci\n labels:\n app: loom\n control-plane: controller-manager\nspec:\n replicas: 1\n selector:\n matchLabels:\n app: loom\n control-plane: controller-manager\n # RWO PVCs — only one pod can mount at a time\n strategy:\n type: Recreate\n template:\n metadata:\n annotations:\n kubectl.kubernetes.io/default-container: manager\n labels:\n app: loom\n control-plane: controller-manager\n spec:\n serviceAccountName: loom-controller\n terminationGracePeriodSeconds: 10\n securityContext:\n runAsNonRoot: true\n runAsUser: 65532\n fsGroup: 65532\n seccompProfile:\n type: RuntimeDefault\n containers:\n - name: manager\n image: zot.sans-self.org/infra/loom:latest\n imagePullPolicy: Always\n command:\n - /manager\n args:\n - --health-probe-bind-address=:8081\n env:\n - name: POD_NAMESPACE\n valueFrom:\n fieldRef:\n fieldPath: metadata.namespace\n - name: LOOM_IMAGE\n value: zot.sans-self.org/infra/loom:latest\n - name: SPINDLE_SERVER_HOSTNAME\n valueFrom:\n configMapKeyRef:\n name: loom-env\n key: hostname\n - name: SPINDLE_SERVER_OWNER\n valueFrom:\n configMapKeyRef:\n name: loom-env\n key: owner\n - name: SPINDLE_SERVER_DB_PATH\n value: /data/spindle.db\n - name: SPINDLE_SERVER_LOG_DIR\n value: /tmp/spindle-logs\n securityContext:\n allowPrivilegeEscalation: false\n capabilities:\n drop:\n - ALL\n livenessProbe:\n httpGet:\n path: /healthz\n port: 8081\n initialDelaySeconds: 15\n periodSeconds: 20\n readinessProbe:\n httpGet:\n path: /readyz\n port: 8081\n initialDelaySeconds: 5\n periodSeconds: 10\n resources:\n requests:\n cpu: 50m\n memory: 128Mi\n limits:\n cpu: 500m\n memory: 512Mi\n volumeMounts:\n - name: spindle-logs\n mountPath: /tmp/spindle-logs\n - name: spindle-db\n mountPath: /data\n - name: loom-config\n mountPath: /etc/loom\n readOnly: true\n volumes:\n - name: spindle-logs\n persistentVolumeClaim:\n claimName: loom-spindle-logs\n - name: spindle-db\n persistentVolumeClaim:\n claimName: loom-spindle-db\n - name: loom-config\n configMap:\n name: loom-config\n"}} 2 + {"timestamp":"1775379472","file_path":"/Users/noivanmondfrans/Projects/github.com/Trans-Utrecht-Beyond/infrastructure/k8s/knot/deployment.yaml","entry":{"original_text":"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: knot\n namespace: knot\nspec:\n replicas: 1\n strategy:\n type: Recreate\n selector:\n matchLabels:\n app: knot\n template:\n metadata:\n labels:\n app: knot\n spec:\n terminationGracePeriodSeconds: 30\n initContainers:\n - name: fix-ssh-perms\n image: busybox:1\n command: [\"sh\", \"-c\", \"chmod 600 /etc/ssh/keys/* 2>/dev/null || true\"]\n volumeMounts:\n - name: data\n mountPath: /etc/ssh/keys\n subPath: sshd-keys\n containers:\n - name: knot\n image: zot.sans-self.org/infra/knot:latest\n ports:\n - name: http\n containerPort: 5555\n - name: internal\n containerPort: 5444\n - name: ssh\n containerPort: 22\n envFrom:\n - configMapRef:\n name: knot-config\n volumeMounts:\n - name: data\n mountPath: /home/git\n - name: data\n mountPath: /etc/ssh/keys\n subPath: sshd-keys\n - name: sshd-hardening\n mountPath: /etc/ssh/sshd_config.d/hardening.conf\n subPath: hardening.conf\n readOnly: true\n livenessProbe:\n httpGet:\n path: /\n port: 5555\n initialDelaySeconds: 15\n periodSeconds: 60\n readinessProbe:\n httpGet:\n path: /\n port: 5555\n initialDelaySeconds: 5\n periodSeconds: 10\n lifecycle:\n postStart:\n exec:\n command: [\"mkdir\", \"-p\", \"/root/.config/git\"]\n # knot runs sshd via s6 — needs privilege escalation for privsep\n # and SETUID/SETGID for s6-applyuidgid to drop to git user\n securityContext:\n capabilities:\n drop:\n - ALL\n add:\n - NET_BIND_SERVICE\n - SETUID\n - SETGID\n - SYS_CHROOT\n resources:\n requests:\n cpu: 100m\n memory: 128Mi\n limits:\n cpu: 500m\n memory: 512Mi\n volumes:\n - name: data\n persistentVolumeClaim:\n claimName: knot-data\n - name: sshd-hardening\n configMap:\n name: sshd-hardening\n\n"}} 3 + {"timestamp":"1775379472","file_path":"/Users/noivanmondfrans/Projects/github.com/Trans-Utrecht-Beyond/infrastructure/k8s/pds/tranquil-frontend-deployment.yaml","entry":{"original_text":"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: tranquil-frontend\n namespace: pds\nspec:\n replicas: 1\n selector:\n matchLabels:\n app: tranquil-frontend\n template:\n metadata:\n labels:\n app: tranquil-frontend\n spec:\n securityContext:\n runAsNonRoot: true\n runAsUser: 101\n runAsGroup: 101\n containers:\n - name: frontend\n image: zot.sans-self.org/infra/tranquil-frontend:latest\n # nginx needs writable cache/pid dirs\n volumeMounts:\n - name: cache\n mountPath: /var/cache/nginx\n - name: run\n mountPath: /var/run\n ports:\n - containerPort: 80\n readinessProbe:\n httpGet:\n path: /\n port: 80\n periodSeconds: 10\n failureThreshold: 2\n securityContext:\n allowPrivilegeEscalation: false\n capabilities:\n drop:\n - ALL\n resources:\n requests:\n cpu: 10m\n memory: 32Mi\n limits:\n cpu: 100m\n memory: 64Mi\n volumes:\n - name: cache\n emptyDir: {}\n - name: run\n emptyDir: {}\n"}} 4 + {"timestamp":"1775379472","file_path":"/Users/noivanmondfrans/Projects/github.com/Trans-Utrecht-Beyond/infrastructure/k8s/pds/tranquil-deployment.yaml","entry":{"original_text":"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: tranquil-pds\n namespace: pds\nspec:\n replicas: 1\n strategy:\n type: RollingUpdate\n rollingUpdate:\n maxUnavailable: 1\n maxSurge: 1\n selector:\n matchLabels:\n app: tranquil-pds\n template:\n metadata:\n labels:\n app: tranquil-pds\n spec:\n affinity:\n podAntiAffinity:\n preferredDuringSchedulingIgnoredDuringExecution:\n - weight: 100\n podAffinityTerm:\n labelSelector:\n matchLabels:\n app: tranquil-pds\n topologyKey: kubernetes.io/hostname\n securityContext:\n runAsNonRoot: true\n runAsUser: 1000\n runAsGroup: 1000\n fsGroup: 1000\n containers:\n - name: tranquil-pds\n image: zot.sans-self.org/infra/tranquil-pds:latest\n ports:\n - name: http\n containerPort: 3000\n env:\n - name: PDS_HOSTNAME\n value: sans-self.org\n - name: DATABASE_URL\n valueFrom:\n secretKeyRef:\n name: tranquil-database-url\n key: url\n - name: BLOB_STORAGE_PATH\n value: /data/blobs\n - name: BACKUP_STORAGE_PATH\n value: /data/backups\n - name: CRAWLERS\n value: https://bsky.network\n - name: MAIL_FROM_ADDRESS\n value: noreply@sans-self.org\n - name: MAIL_FROM_NAME\n value: Sans Self PDS\n - name: SENDMAIL_PATH\n value: /usr/bin/msmtp\n - name: JWT_SECRET\n valueFrom:\n secretKeyRef:\n name: tranquil-secrets\n key: jwt_secret\n - name: DPOP_SECRET\n valueFrom:\n secretKeyRef:\n name: tranquil-secrets\n key: dpop_secret\n - name: MASTER_KEY\n valueFrom:\n secretKeyRef:\n name: tranquil-secrets\n key: master_key\n - name: CACHE_BACKEND\n value: valkey\n - name: VALKEY_URL\n valueFrom:\n secretKeyRef:\n name: tranquil-valkey-url\n key: url\n volumeMounts:\n - name: data\n mountPath: /data\n - name: msmtp-config\n mountPath: /etc/msmtprc\n subPath: msmtprc\n readOnly: true\n startupProbe:\n httpGet:\n path: /xrpc/_health\n port: 3000\n failureThreshold: 30\n periodSeconds: 2\n livenessProbe:\n httpGet:\n path: /xrpc/_health\n port: 3000\n periodSeconds: 30\n timeoutSeconds: 5\n failureThreshold: 3\n readinessProbe:\n httpGet:\n path: /xrpc/_health\n port: 3000\n periodSeconds: 10\n timeoutSeconds: 5\n failureThreshold: 2\n securityContext:\n allowPrivilegeEscalation: false\n capabilities:\n drop:\n - ALL\n resources:\n requests:\n cpu: 100m\n memory: 256Mi\n limits:\n cpu: 500m\n memory: 1Gi\n volumes:\n - name: data\n persistentVolumeClaim:\n claimName: tranquil-data\n - name: msmtp-config\n secret:\n secretName: msmtp-config\n defaultMode: 0400\n"}} 5 + {"timestamp":"1775379472","file_path":"/Users/noivanmondfrans/Projects/github.com/Trans-Utrecht-Beyond/infrastructure/k8s/opake-staging/web/deployment.yaml","entry":{"original_text":"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: opake-web\n namespace: opake-staging\nspec:\n replicas: 1\n selector:\n matchLabels:\n app: opake-web\n template:\n metadata:\n labels:\n app: opake-web\n spec:\n securityContext:\n runAsUser: 1000\n runAsGroup: 1000\n runAsNonRoot: true\n containers:\n - name: web\n image: zot.sans-self.org/opake/web:staging\n imagePullPolicy: Always\n ports:\n - containerPort: 3000\n env:\n - name: NODE_ENV\n value: production\n - name: PORT\n value: \"3000\"\n livenessProbe:\n httpGet:\n path: /\n port: 3000\n periodSeconds: 30\n failureThreshold: 3\n readinessProbe:\n httpGet:\n path: /\n port: 3000\n periodSeconds: 10\n failureThreshold: 2\n securityContext:\n allowPrivilegeEscalation: false\n capabilities:\n drop:\n - ALL\n readOnlyRootFilesystem: true\n resources:\n requests:\n cpu: 20m\n memory: 64Mi\n limits:\n cpu: 200m\n memory: 256Mi\n volumeMounts:\n - name: tmp\n mountPath: /tmp\n volumes:\n - name: tmp\n emptyDir: {}\n"}} 6 + {"timestamp":"1775379472","file_path":"/Users/noivanmondfrans/Projects/github.com/Trans-Utrecht-Beyond/infrastructure/k8s/opake-staging/appview/deployment.yaml","entry":{"original_text":"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: opake-appview\n namespace: opake-staging\nspec:\n replicas: 1\n strategy:\n type: Recreate\n selector:\n matchLabels:\n app: opake-appview\n template:\n metadata:\n labels:\n app: opake-appview\n spec:\n terminationGracePeriodSeconds: 30\n securityContext:\n runAsUser: 1000\n runAsGroup: 1000\n runAsNonRoot: true\n containers:\n - name: appview\n image: zot.sans-self.org/opake/appview:staging\n imagePullPolicy: Always\n ports:\n - containerPort: 6100\n env:\n - name: DATABASE_URL\n valueFrom:\n secretKeyRef:\n name: opake-staging-database-url\n key: url\n - name: SECRET_KEY_BASE\n valueFrom:\n secretKeyRef:\n name: opake-staging-secrets\n key: secret-key-base\n - name: PHX_HOST\n value: appview.staging.opake.app\n - name: PHX_SERVER\n value: \"true\"\n - name: CORS_ORIGIN\n value: https://staging.opake.app\n - name: OPAKE_FRONTEND_URL\n value: https://staging.opake.app\n - name: JETSTREAM_URL\n value: wss://jetstream2.us-east.bsky.network/subscribe\n startupProbe:\n httpGet:\n path: /api/health\n port: 6100\n failureThreshold: 30\n periodSeconds: 2\n livenessProbe:\n httpGet:\n path: /api/health\n port: 6100\n periodSeconds: 30\n timeoutSeconds: 5\n failureThreshold: 3\n readinessProbe:\n httpGet:\n path: /api/health\n port: 6100\n periodSeconds: 10\n timeoutSeconds: 5\n failureThreshold: 2\n securityContext:\n allowPrivilegeEscalation: false\n capabilities:\n drop:\n - ALL\n resources:\n requests:\n cpu: 50m\n memory: 64Mi\n limits:\n cpu: 500m\n memory: 256Mi\n"}} 7 + {"timestamp":"1775379472","file_path":"/Users/noivanmondfrans/Projects/github.com/Trans-Utrecht-Beyond/infrastructure/k8s/registry/ingress.yaml","entry":{"original_text":"apiVersion: traefik.io/v1alpha1\nkind: Middleware\nmetadata:\n name: strip-server-headers\n namespace: registry\nspec:\n headers:\n customResponseHeaders:\n X-Powered-By: \"\"\n---\napiVersion: traefik.io/v1alpha1\nkind: IngressRoute\nmetadata:\n name: zot\n namespace: registry\nspec:\n entryPoints:\n - websecure\n tls:\n secretName: zot-sans-self-org-tls\n routes:\n - match: Host(`zot.sans-self.org`)\n kind: Rule\n middlewares:\n - name: strip-server-headers\n services:\n - name: zot\n port: 5000\n"}} 8 + {"timestamp":"1775379472","file_path":"/Users/noivanmondfrans/Projects/github.com/Trans-Utrecht-Beyond/infrastructure/k8s/registry/cert.yaml","entry":{"original_text":"apiVersion: cert-manager.io/v1\nkind: Certificate\nmetadata:\n name: zot-sans-self-org\n namespace: registry\nspec:\n secretName: zot-sans-self-org-tls\n issuerRef:\n name: letsencrypt-prod\n kind: ClusterIssuer\n dnsNames:\n - zot.sans-self.org\n"}} 9 + {"timestamp":"1775379472","file_path":"/Users/noivanmondfrans/Projects/github.com/Trans-Utrecht-Beyond/infrastructure/k8s/opake/jetstream/deployment.yaml","entry":{"original_text":"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: jetstream\n namespace: opake\nspec:\n replicas: 1\n strategy:\n type: Recreate\n selector:\n matchLabels:\n app: jetstream\n template:\n metadata:\n labels:\n app: jetstream\n spec:\n terminationGracePeriodSeconds: 30\n securityContext:\n fsGroup: 1000\n runAsUser: 1000\n runAsGroup: 1000\n runAsNonRoot: true\n containers:\n - name: jetstream\n image: zot.sans-self.org/infra/jetstream:latest\n ports:\n - name: ws\n containerPort: 6008\n - name: metrics\n containerPort: 6009\n env:\n - name: JETSTREAM_DATA_DIR\n value: /data\n - name: JETSTREAM_EVENT_TTL\n value: \"1h\"\n - name: JETSTREAM_LIVENESS_TTL\n value: \"30s\"\n volumeMounts:\n - name: data\n mountPath: /data\n livenessProbe:\n tcpSocket:\n port: 6008\n periodSeconds: 30\n timeoutSeconds: 5\n failureThreshold: 3\n readinessProbe:\n tcpSocket:\n port: 6008\n periodSeconds: 10\n timeoutSeconds: 5\n failureThreshold: 2\n securityContext:\n allowPrivilegeEscalation: false\n capabilities:\n drop:\n - ALL\n resources:\n requests:\n cpu: 50m\n memory: 128Mi\n limits:\n cpu: \"1\"\n memory: 512Mi\n volumes:\n - name: data\n persistentVolumeClaim:\n claimName: jetstream-data\n"}} 10 + {"timestamp":"1775379472","file_path":"/Users/noivanmondfrans/Projects/github.com/Trans-Utrecht-Beyond/infrastructure/k8s/opake/web/deployment.yaml","entry":{"original_text":"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: opake-web\n namespace: opake\nspec:\n replicas: 2\n selector:\n matchLabels:\n app: opake-web\n template:\n metadata:\n labels:\n app: opake-web\n spec:\n securityContext:\n runAsUser: 1000\n runAsGroup: 1000\n runAsNonRoot: true\n containers:\n - name: web\n image: zot.sans-self.org/opake/web:REPLACE_ME\n ports:\n - containerPort: 3000\n env:\n - name: NODE_ENV\n value: production\n - name: PORT\n value: \"3000\"\n livenessProbe:\n httpGet:\n path: /\n port: 3000\n periodSeconds: 30\n failureThreshold: 3\n readinessProbe:\n httpGet:\n path: /\n port: 3000\n periodSeconds: 10\n failureThreshold: 2\n securityContext:\n allowPrivilegeEscalation: false\n capabilities:\n drop:\n - ALL\n readOnlyRootFilesystem: true\n resources:\n requests:\n cpu: 20m\n memory: 64Mi\n limits:\n cpu: 200m\n memory: 256Mi\n volumeMounts:\n - name: tmp\n mountPath: /tmp\n volumes:\n - name: tmp\n emptyDir: {}\n"}} 11 + {"timestamp":"1775379472","file_path":"/Users/noivanmondfrans/Projects/github.com/Trans-Utrecht-Beyond/infrastructure/k8s/opake/web/kustomization.yaml","entry":{"original_text":"apiVersion: kustomize.config.k8s.io/v1beta1\nkind: Kustomization\n\nresources:\n - deployment.yaml\n - service.yaml\n - ingress.yaml\n - pds-handle-proxy.yaml\n - cert.yaml\n\nimages:\n - name: zot.sans-self.org/opake/web\n newTag: latest\n"}} 12 + {"timestamp":"1775379472","file_path":"/Users/noivanmondfrans/Projects/github.com/Trans-Utrecht-Beyond/infrastructure/k8s/opake/appview/deployment.yaml","entry":{"original_text":"apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: opake-appview\n namespace: opake\nspec:\n replicas: 1\n strategy:\n type: Recreate # SQLite WAL — one writer\n selector:\n matchLabels:\n app: opake-appview\n template:\n metadata:\n labels:\n app: opake-appview\n spec:\n terminationGracePeriodSeconds: 30\n securityContext:\n fsGroup: 1000\n runAsUser: 1000\n runAsGroup: 1000\n runAsNonRoot: true\n containers:\n - name: appview\n image: zot.sans-self.org/opake/appview:REPLACE_ME\n ports:\n - containerPort: 6100\n env:\n - name: OPAKE_DATA_DIR\n value: /data\n - name: RUST_LOG\n value: info\n volumeMounts:\n - name: data\n mountPath: /data\n - name: config\n mountPath: /data/appview.toml\n subPath: appview.toml\n readOnly: true\n startupProbe:\n httpGet:\n path: /api/health\n port: 6100\n failureThreshold: 30\n periodSeconds: 2\n livenessProbe:\n httpGet:\n path: /api/health\n port: 6100\n periodSeconds: 30\n timeoutSeconds: 5\n failureThreshold: 3\n readinessProbe:\n httpGet:\n path: /api/health\n port: 6100\n periodSeconds: 10\n timeoutSeconds: 5\n failureThreshold: 2\n securityContext:\n allowPrivilegeEscalation: false\n capabilities:\n drop:\n - ALL\n resources:\n requests:\n cpu: 50m\n memory: 64Mi\n limits:\n cpu: 500m\n memory: 256Mi\n volumes:\n - name: data\n persistentVolumeClaim:\n claimName: opake-appview-data\n - name: config\n configMap:\n name: opake-appview-config\n"}} 13 + {"timestamp":"1775379472","file_path":"/Users/noivanmondfrans/Projects/github.com/Trans-Utrecht-Beyond/infrastructure/k8s/opake/appview/kustomization.yaml","entry":{"original_text":"apiVersion: kustomize.config.k8s.io/v1beta1\nkind: Kustomization\n\nresources:\n - configmap.yaml\n - pvc.yaml\n - deployment.yaml\n - service.yaml\n - ingress.yaml\n - cert.yaml\n\nimages:\n - name: zot.sans-self.org/opake/appview\n newTag: latest\n"}} 14 + {"timestamp":"1775379473","file_path":"/Users/noivanmondfrans/Projects/github.com/Trans-Utrecht-Beyond/infrastructure/Makefile","entry":{"original_text":"# Derived secrets — generated from source secrets before kustomize build\nJUICEFS_METAURL := k8s/juicefs/metaurl.secret\nTRANQUIL_DB_URL := k8s/pds/tranquil-database-url.secret\nTRANQUIL_VALKEY_URL := k8s/pds/tranquil-valkey-url.secret\nOPAKE_STAGING_DB_URL := k8s/opake-staging/database-url.secret\n\n.PHONY: secrets clean-secrets build\n\nsecrets: $(JUICEFS_METAURL) $(TRANQUIL_DB_URL) $(TRANQUIL_VALKEY_URL) $(OPAKE_STAGING_DB_URL)\n\n$(JUICEFS_METAURL): k8s/juicefs/redis-password.secret\n\t@pw=$$(cat $< | tr -d '\\n') && \\\n\t\tprintf 'redis://:%s@redis.juicefs.svc.cluster.local:6379/0' \"$$pw\" > $@\n\n$(TRANQUIL_DB_URL): k8s/postgres/postgres-password.secret\n\t@pw=$$(cat $< | tr -d '\\n' | python3 -c 'import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read(),safe=\"\"),end=\"\")') && \\\n\t\tprintf 'postgres://tranquil:%s@postgres.postgres.svc.cluster.local:5432/pds' \"$$pw\" > $@\n\nbuild: secrets\n\tkustomize build k8s/\n\n$(TRANQUIL_VALKEY_URL): k8s/juicefs/redis-password.secret\n\t@pw=$$(cat $< | tr -d '\\n') && \\\n\t\tprintf 'redis://:%s@redis.juicefs.svc.cluster.local:6379/1' \"$$pw\" > $@\n\n$(OPAKE_STAGING_DB_URL): k8s/postgres/opake-staging-password.secret\n\t@pw=$$(cat $< | tr -d '\\n' | python3 -c 'import sys,urllib.parse; print(urllib.parse.quote(sys.stdin.read(),safe=\"\"),end=\"\")') && \\\n\t\tprintf 'ecto://opake_staging:%s@postgres.postgres.svc.cluster.local:5432/opake_staging' \"$$pw\" > $@\n\nclean-secrets:\n\trm -f $(JUICEFS_METAURL) $(TRANQUIL_DB_URL) $(TRANQUIL_VALKEY_URL) $(OPAKE_STAGING_DB_URL)\n\n# Tranquil PDS\nTRANQUIL_REPO ?= /tmp/tranquil-pds\n\n.PHONY: build-tranquil push-tranquil build-tranquil-frontend push-tranquil-frontend\n\nbuild-tranquil:\n\t@test -d \"$(TRANQUIL_REPO)\" || { echo \"error: tranquil-pds repo not found at $(TRANQUIL_REPO)\"; exit 1; }\n\tdocker buildx build --platform linux/arm64 -t zot.sans-self.org/infra/tranquil-pds:latest \"$(TRANQUIL_REPO)\"\n\npush-tranquil:\n\tdocker push zot.sans-self.org/infra/tranquil-pds:latest\n\nbuild-tranquil-frontend:\n\t@test -d \"$(TRANQUIL_REPO)\" || { echo \"error: tranquil-pds repo not found at $(TRANQUIL_REPO)\"; exit 1; }\n\tdocker buildx build --platform linux/arm64 -t zot.sans-self.org/infra/tranquil-frontend:latest \"$(TRANQUIL_REPO)/frontend\"\n\npush-tranquil-frontend:\n\tdocker push zot.sans-self.org/infra/tranquil-frontend:latest\n\n# Jetstream (self-hosted, ARM64)\nJETSTREAM_REPO ?= /tmp/jetstream\n\n.PHONY: build-jetstream push-jetstream\n\nbuild-jetstream:\n\t@test -d \"$(JETSTREAM_REPO)\" || { echo \"error: jetstream repo not found at $(JETSTREAM_REPO)\"; exit 1; }\n\tdocker buildx build --platform linux/arm64 -t zot.sans-self.org/infra/jetstream:latest -f dockerfiles/jetstream.Dockerfile \"$(JETSTREAM_REPO)\"\n\npush-jetstream:\n\tdocker push zot.sans-self.org/infra/jetstream:latest\n\n# CI validate image\n.PHONY: build-ci-validate push-ci-validate\n\nbuild-ci-validate:\n\tdocker buildx build --platform linux/arm64 -t zot.sans-self.org/infra/ci-validate:latest -f dockerfiles/ci-validate.Dockerfile .\n\npush-ci-validate:\n\tdocker push zot.sans-self.org/infra/ci-validate:latest\n\n# CI opake image (buildah + bash for Loom)\n.PHONY: build-ci-opake push-ci-opake\n\nbuild-ci-opake:\n\tdocker buildx build --platform linux/arm64 -t zot.sans-self.org/infra/ci-opake:latest -f dockerfiles/ci-opake.Dockerfile .\n\npush-ci-opake:\n\tdocker push zot.sans-self.org/infra/ci-opake:latest\n\n# JuiceFS mount image (v1.3.1 + btrfs inode cache fix from juicedata/juicefs#6675)\n.PHONY: build-juicefs-mount push-juicefs-mount\n\nbuild-juicefs-mount:\n\tdocker buildx build --platform linux/arm64 -t sansself/juicefs:v1.3.1-btrfs-fix -f dockerfiles/juicefs-mount.Dockerfile .\n\npush-juicefs-mount:\n\tdocker push sansself/juicefs:v1.3.1-btrfs-fix\n\n# Rust + wasm-pack base image (used in Containerfile.web wasm-builder stage)\n.PHONY: build-rust-wasm push-rust-wasm\n\nbuild-rust-wasm:\n\tdocker buildx build --platform linux/arm64 -t zot.sans-self.org/infra/rust-wasm:latest -f dockerfiles/rust-wasm.Dockerfile .\n\npush-rust-wasm:\n\tdocker push zot.sans-self.org/infra/rust-wasm:latest\n\n"}} 15 + {"timestamp":"1775379473","file_path":"/Users/noivanmondfrans/Projects/github.com/Trans-Utrecht-Beyond/infrastructure/deploy-tranquil.sh","entry":{"original_text":"#!/usr/bin/env bash\nset -euo pipefail\n\nTRANQUIL_REPO=\"${TRANQUIL_REPO:-$HOME/Projects/tranquil-pds}\"\nIMAGE=\"zot.sans-self.org/infra/tranquil-pds:latest\"\nNAMESPACE=\"pds\"\nDEPLOYMENT=\"tranquil-pds\"\n\nif [[ ! -d \"$TRANQUIL_REPO\" ]]; then\n echo \"error: tranquil-pds repo not found at $TRANQUIL_REPO\" >&2\n exit 1\nfi\n\necho \"==> Building $IMAGE from $TRANQUIL_REPO\"\ndocker buildx build --platform linux/arm64 -t \"$IMAGE\" \"$TRANQUIL_REPO\"\n\necho \"==> Pushing $IMAGE\"\ndocker push \"$IMAGE\"\n\necho \"==> Restarting $DEPLOYMENT\"\nkubectl rollout restart \"deployment/$DEPLOYMENT\" -n \"$NAMESPACE\"\nkubectl rollout status \"deployment/$DEPLOYMENT\" -n \"$NAMESPACE\" --timeout=120s\n\necho \"==> Done\"\nkubectl get pods -n \"$NAMESPACE\" -l \"app=$DEPLOYMENT\"\n"}}
+28 -12
Makefile
··· 37 37 38 38 build-tranquil: 39 39 @test -d "$(TRANQUIL_REPO)" || { echo "error: tranquil-pds repo not found at $(TRANQUIL_REPO)"; exit 1; } 40 - docker buildx build --platform linux/arm64 -t zot.sans-self.org/infra/tranquil-pds:latest "$(TRANQUIL_REPO)" 40 + docker buildx build --platform linux/arm64 -t rg.fr-par.scw.cloud/sans-self/infra/tranquil-pds:latest "$(TRANQUIL_REPO)" 41 41 42 42 push-tranquil: 43 - docker push zot.sans-self.org/infra/tranquil-pds:latest 43 + docker push rg.fr-par.scw.cloud/sans-self/infra/tranquil-pds:latest 44 44 45 45 build-tranquil-frontend: 46 46 @test -d "$(TRANQUIL_REPO)" || { echo "error: tranquil-pds repo not found at $(TRANQUIL_REPO)"; exit 1; } 47 - docker buildx build --platform linux/arm64 -t zot.sans-self.org/infra/tranquil-frontend:latest "$(TRANQUIL_REPO)/frontend" 47 + docker buildx build --platform linux/arm64 -t rg.fr-par.scw.cloud/sans-self/infra/tranquil-frontend:latest "$(TRANQUIL_REPO)/frontend" 48 48 49 49 push-tranquil-frontend: 50 - docker push zot.sans-self.org/infra/tranquil-frontend:latest 50 + docker push rg.fr-par.scw.cloud/sans-self/infra/tranquil-frontend:latest 51 51 52 52 # Jetstream (self-hosted, ARM64) 53 53 JETSTREAM_REPO ?= /tmp/jetstream ··· 56 56 57 57 build-jetstream: 58 58 @test -d "$(JETSTREAM_REPO)" || { echo "error: jetstream repo not found at $(JETSTREAM_REPO)"; exit 1; } 59 - docker buildx build --platform linux/arm64 -t zot.sans-self.org/infra/jetstream:latest -f dockerfiles/jetstream.Dockerfile "$(JETSTREAM_REPO)" 59 + docker buildx build --platform linux/arm64 -t rg.fr-par.scw.cloud/sans-self/infra/jetstream:latest -f dockerfiles/jetstream.Dockerfile "$(JETSTREAM_REPO)" 60 60 61 61 push-jetstream: 62 - docker push zot.sans-self.org/infra/jetstream:latest 62 + docker push rg.fr-par.scw.cloud/sans-self/infra/jetstream:latest 63 63 64 64 # CI validate image 65 65 .PHONY: build-ci-validate push-ci-validate 66 66 67 67 build-ci-validate: 68 - docker buildx build --platform linux/arm64 -t zot.sans-self.org/infra/ci-validate:latest -f dockerfiles/ci-validate.Dockerfile . 68 + docker buildx build --platform linux/arm64 -t rg.fr-par.scw.cloud/sans-self/infra/ci-validate:latest -f dockerfiles/ci-validate.Dockerfile . 69 69 70 70 push-ci-validate: 71 - docker push zot.sans-self.org/infra/ci-validate:latest 71 + docker push rg.fr-par.scw.cloud/sans-self/infra/ci-validate:latest 72 72 73 73 # CI opake image (buildah + bash for Loom) 74 74 .PHONY: build-ci-opake push-ci-opake 75 75 76 76 build-ci-opake: 77 - docker buildx build --platform linux/arm64 -t zot.sans-self.org/infra/ci-opake:latest -f dockerfiles/ci-opake.Dockerfile . 77 + docker buildx build --platform linux/arm64 -t rg.fr-par.scw.cloud/sans-self/infra/ci-opake:latest -f dockerfiles/ci-opake.Dockerfile . 78 78 79 79 push-ci-opake: 80 - docker push zot.sans-self.org/infra/ci-opake:latest 80 + docker push rg.fr-par.scw.cloud/sans-self/infra/ci-opake:latest 81 81 82 82 # JuiceFS mount image (v1.3.1 + btrfs inode cache fix from juicedata/juicefs#6675) 83 83 .PHONY: build-juicefs-mount push-juicefs-mount ··· 88 88 push-juicefs-mount: 89 89 docker push sansself/juicefs:v1.3.1-btrfs-fix 90 90 91 + # Knot — built from upstream tangled.org/tangled.org/knot-docker recipe. 92 + # KNOT_TAG selects the knot release; KNOT_DOCKER_REF pins the build recipe. 93 + KNOT_TAG ?= v1.13.0-alpha 94 + KNOT_DOCKER_REF ?= 380990bbb9154e22310bf9945723f7cbeabf5307 95 + 96 + .PHONY: build-knot push-knot 97 + 98 + build-knot: 99 + docker buildx build --platform linux/arm64 \ 100 + --build-arg TAG=$(KNOT_TAG) \ 101 + -t rg.fr-par.scw.cloud/sans-self/infra/knot:$(KNOT_TAG) \ 102 + https://tangled.org/tangled.org/knot-docker.git#$(KNOT_DOCKER_REF) 103 + 104 + push-knot: 105 + docker push rg.fr-par.scw.cloud/sans-self/infra/knot:$(KNOT_TAG) 106 + 91 107 # Rust + wasm-pack base image (used in Containerfile.web wasm-builder stage) 92 108 .PHONY: build-rust-wasm push-rust-wasm 93 109 94 110 build-rust-wasm: 95 - docker buildx build --platform linux/arm64 -t zot.sans-self.org/infra/rust-wasm:latest -f dockerfiles/rust-wasm.Dockerfile . 111 + docker buildx build --platform linux/arm64 -t rg.fr-par.scw.cloud/sans-self/infra/rust-wasm:latest -f dockerfiles/rust-wasm.Dockerfile . 96 112 97 113 push-rust-wasm: 98 - docker push zot.sans-self.org/infra/rust-wasm:latest 114 + docker push rg.fr-par.scw.cloud/sans-self/infra/rust-wasm:latest 99 115
+1 -1
deploy-tranquil.sh
··· 2 2 set -euo pipefail 3 3 4 4 TRANQUIL_REPO="${TRANQUIL_REPO:-$HOME/Projects/tranquil-pds}" 5 - IMAGE="zot.sans-self.org/infra/tranquil-pds:latest" 5 + IMAGE="rg.fr-par.scw.cloud/sans-self/infra/tranquil-pds:latest" 6 6 NAMESPACE="pds" 7 7 DEPLOYMENT="tranquil-pds" 8 8
+2
k8s/ci/deployment.yaml
··· 28 28 env: 29 29 - name: PYTHONPATH 30 30 value: /deps 31 + - name: KNOT_WS_URL 32 + value: ws://knot.knot-personal.svc.cluster.local:5555/events 31 33 volumeMounts: 32 34 - name: script 33 35 mountPath: /app
+2 -2
k8s/ci/loom-deployment.yaml
··· 33 33 type: RuntimeDefault 34 34 containers: 35 35 - name: manager 36 - image: zot.sans-self.org/infra/loom:latest 36 + image: rg.fr-par.scw.cloud/sans-self/infra/loom:latest 37 37 imagePullPolicy: Always 38 38 command: 39 39 - /manager ··· 45 45 fieldRef: 46 46 fieldPath: metadata.namespace 47 47 - name: LOOM_IMAGE 48 - value: zot.sans-self.org/infra/loom:latest 48 + value: rg.fr-par.scw.cloud/sans-self/infra/loom:latest 49 49 - name: SPINDLE_SERVER_HOSTNAME 50 50 valueFrom: 51 51 configMapKeyRef:
+3 -3
k8s/ci/loom-pvc.yaml
··· 6 6 spec: 7 7 accessModes: 8 8 - ReadWriteOnce 9 - storageClassName: juicefs-sc 9 + storageClassName: longhorn 10 10 resources: 11 11 requests: 12 12 storage: 1Gi ··· 19 19 spec: 20 20 accessModes: 21 21 - ReadWriteOnce 22 - storageClassName: juicefs-sc 22 + storageClassName: longhorn 23 23 resources: 24 24 requests: 25 - storage: 5Gi 25 + storage: 2Gi
+12
k8s/knot-personal/cert.yaml
··· 1 + apiVersion: cert-manager.io/v1 2 + kind: Certificate 3 + metadata: 4 + name: knot-personal-sans-self-org 5 + namespace: knot-personal 6 + spec: 7 + secretName: knot-personal-sans-self-org-tls 8 + issuerRef: 9 + name: letsencrypt-prod 10 + kind: ClusterIssuer 11 + dnsNames: 12 + - knot-personal.sans-self.org
+36
k8s/knot-personal/ingress.yaml
··· 1 + apiVersion: traefik.io/v1alpha1 2 + kind: Middleware 3 + metadata: 4 + name: strip-server-headers 5 + namespace: knot-personal 6 + spec: 7 + headers: 8 + customResponseHeaders: 9 + X-Powered-By: "" 10 + --- 11 + apiVersion: traefik.io/v1alpha1 12 + kind: IngressRoute 13 + metadata: 14 + name: knot 15 + namespace: knot-personal 16 + spec: 17 + entryPoints: 18 + - websecure 19 + tls: 20 + secretName: knot-personal-sans-self-org-tls 21 + routes: 22 + # Tarpit for vulnerability scanners 23 + - match: Host(`knot-personal.sans-self.org`) && (PathPrefix(`/wp-admin`) || PathPrefix(`/wp-login`) || PathPrefix(`/wp-content`) || PathPrefix(`/wp-includes`) || PathPrefix(`/wordpress`) || PathPrefix(`/.env`) || PathPrefix(`/.git`) || PathPrefix(`/phpmyadmin`) || PathPrefix(`/pma`) || PathPrefix(`/myadmin`) || PathPrefix(`/admin`) || PathPrefix(`/administrator`) || PathPrefix(`/cgi-bin`) || PathPrefix(`/actuator`) || PathPrefix(`/solr`) || PathPrefix(`/console`) || PathPrefix(`/vendor`) || PathPrefix(`/config`) || PathPrefix(`/backup`) || PathPrefix(`/debug`) || PathPrefix(`/server-status`) || PathPrefix(`/xmlrpc.php`)) 24 + kind: Rule 25 + priority: 200 26 + services: 27 + - name: tarpit 28 + port: 8080 29 + # Everything else 30 + - match: Host(`knot-personal.sans-self.org`) 31 + kind: Rule 32 + middlewares: 33 + - name: strip-server-headers 34 + services: 35 + - name: knot 36 + port: 5555
+3 -3
k8s/knot/backup-cronjob.yaml k8s/knot-personal/backup-cronjob.yaml
··· 2 2 kind: CronJob 3 3 metadata: 4 4 name: knot-backup 5 - namespace: knot 5 + namespace: knot-personal 6 6 spec: 7 7 schedule: "30 2 * * *" 8 8 concurrencyPolicy: Forbid ··· 54 54 value: | 55 55 /data/repositories:repositories 56 56 - name: BACKUP_BUCKET 57 - value: sans-self-net 57 + value: sans-self-backups 58 58 - name: BACKUP_PREFIX 59 59 value: knot 60 60 - name: S3_ACCESS_KEY ··· 96 96 volumes: 97 97 - name: service-data 98 98 persistentVolumeClaim: 99 - claimName: knot-data 99 + claimName: knot-data-longhorn 100 100 - name: tmp 101 101 emptyDir: 102 102 sizeLimit: 1Gi
-12
k8s/knot/cert.yaml
··· 1 - apiVersion: cert-manager.io/v1 2 - kind: Certificate 3 - metadata: 4 - name: knot-sans-self-org 5 - namespace: knot 6 - spec: 7 - secretName: knot-sans-self-org-tls 8 - issuerRef: 9 - name: letsencrypt-prod 10 - kind: ClusterIssuer 11 - dnsNames: 12 - - knot.sans-self.org
+3 -3
k8s/knot/configmap.yaml k8s/knot-personal/configmap.yaml
··· 2 2 kind: ConfigMap 3 3 metadata: 4 4 name: knot-config 5 - namespace: knot 5 + namespace: knot-personal 6 6 data: 7 - KNOT_SERVER_HOSTNAME: knot.sans-self.org 7 + KNOT_SERVER_HOSTNAME: knot-personal.sans-self.org 8 8 KNOT_SERVER_OWNER: did:plc:wydyrngmxbcsqdvhmd7whmye 9 9 KNOT_SERVER_PORT: "443" 10 10 KNOT_SERVER_LISTEN_ADDR: 0.0.0.0:5555 ··· 17 17 kind: ConfigMap 18 18 metadata: 19 19 name: sshd-hardening 20 - namespace: knot 20 + namespace: knot-personal 21 21 data: 22 22 hardening.conf: | 23 23 MaxAuthTries 3
+22 -8
k8s/knot/deployment.yaml k8s/knot-personal/deployment.yaml
··· 2 2 kind: Deployment 3 3 metadata: 4 4 name: knot 5 - namespace: knot 5 + namespace: knot-personal 6 6 spec: 7 7 replicas: 1 8 8 strategy: ··· 17 17 spec: 18 18 terminationGracePeriodSeconds: 30 19 19 initContainers: 20 - - name: fix-ssh-perms 21 - image: busybox:1 22 - command: ["sh", "-c", "chmod 600 /etc/ssh/keys/* 2>/dev/null || true"] 20 + # Seed directories knot owns as git (uid 1000), and generate sshd 21 + # host keys as root. Matches the upstream s6 oneshot but runs 22 + # reliably at init time regardless of s6-rc ordering, and keeps 23 + # keys root-owned so sshd's StrictModes accepts them. 24 + - name: init-data-dirs 25 + image: rg.fr-par.scw.cloud/sans-self/infra/knot:v1.13.0-alpha 26 + command: 27 + - sh 28 + - -c 29 + - | 30 + set -eu 31 + mkdir -p /home/git/repositories /home/git/data /home/git/sshd-keys 32 + chown 1000:1000 /home/git/repositories /home/git/data 33 + chown 0:0 /home/git/sshd-keys 34 + for t in rsa ecdsa ed25519; do 35 + f=/home/git/sshd-keys/ssh_host_${t}_key 36 + [ -f "$f" ] || ssh-keygen -t "$t" -f "$f" -q -N "" 37 + done 23 38 volumeMounts: 24 39 - name: data 25 - mountPath: /etc/ssh/keys 26 - subPath: sshd-keys 40 + mountPath: /home/git 27 41 containers: 28 42 - name: knot 29 - image: zot.sans-self.org/infra/knot:latest 43 + image: rg.fr-par.scw.cloud/sans-self/infra/knot:v1.13.0-alpha 30 44 ports: 31 45 - name: http 32 46 containerPort: 5555 ··· 84 98 volumes: 85 99 - name: data 86 100 persistentVolumeClaim: 87 - claimName: knot-data 101 + claimName: knot-data-longhorn 88 102 - name: sshd-hardening 89 103 configMap: 90 104 name: sshd-hardening
-36
k8s/knot/ingress.yaml
··· 1 - apiVersion: traefik.io/v1alpha1 2 - kind: Middleware 3 - metadata: 4 - name: strip-server-headers 5 - namespace: knot 6 - spec: 7 - headers: 8 - customResponseHeaders: 9 - X-Powered-By: "" 10 - --- 11 - apiVersion: traefik.io/v1alpha1 12 - kind: IngressRoute 13 - metadata: 14 - name: knot 15 - namespace: knot 16 - spec: 17 - entryPoints: 18 - - websecure 19 - tls: 20 - secretName: knot-sans-self-org-tls 21 - routes: 22 - # Tarpit for vulnerability scanners 23 - - match: Host(`knot.sans-self.org`) && (PathPrefix(`/wp-admin`) || PathPrefix(`/wp-login`) || PathPrefix(`/wp-content`) || PathPrefix(`/wp-includes`) || PathPrefix(`/wordpress`) || PathPrefix(`/.env`) || PathPrefix(`/.git`) || PathPrefix(`/phpmyadmin`) || PathPrefix(`/pma`) || PathPrefix(`/myadmin`) || PathPrefix(`/admin`) || PathPrefix(`/administrator`) || PathPrefix(`/cgi-bin`) || PathPrefix(`/actuator`) || PathPrefix(`/solr`) || PathPrefix(`/console`) || PathPrefix(`/vendor`) || PathPrefix(`/config`) || PathPrefix(`/backup`) || PathPrefix(`/debug`) || PathPrefix(`/server-status`) || PathPrefix(`/xmlrpc.php`)) 24 - kind: Rule 25 - priority: 200 26 - services: 27 - - name: tarpit 28 - port: 8080 29 - # Everything else 30 - - match: Host(`knot.sans-self.org`) 31 - kind: Rule 32 - middlewares: 33 - - name: strip-server-headers 34 - services: 35 - - name: knot 36 - port: 5555
k8s/knot/kustomization.yaml k8s/knot-personal/kustomization.yaml
+1 -1
k8s/knot/namespace.yaml k8s/knot-personal/namespace.yaml
··· 1 1 apiVersion: v1 2 2 kind: Namespace 3 3 metadata: 4 - name: knot 4 + name: knot-personal
+2 -2
k8s/knot/network-policy.yaml k8s/knot-personal/network-policy.yaml
··· 2 2 kind: NetworkPolicy 3 3 metadata: 4 4 name: knot-ingress 5 - namespace: knot 5 + namespace: knot-personal 6 6 spec: 7 7 podSelector: 8 8 matchLabels: ··· 44 44 kind: NetworkPolicy 45 45 metadata: 46 46 name: tarpit-ingress 47 - namespace: knot 47 + namespace: knot-personal 48 48 spec: 49 49 podSelector: 50 50 matchLabels:
+3 -3
k8s/knot/pvc.yaml k8s/knot-personal/pvc.yaml
··· 1 1 apiVersion: v1 2 2 kind: PersistentVolumeClaim 3 3 metadata: 4 - name: knot-data 5 - namespace: knot 4 + name: knot-data-longhorn 5 + namespace: knot-personal 6 6 spec: 7 + storageClassName: longhorn 7 8 accessModes: 8 9 - ReadWriteOnce 9 - storageClassName: juicefs-sc 10 10 resources: 11 11 requests: 12 12 storage: 10Gi
+1 -1
k8s/knot/service.yaml k8s/knot-personal/service.yaml
··· 2 2 kind: Service 3 3 metadata: 4 4 name: knot 5 - namespace: knot 5 + namespace: knot-personal 6 6 spec: 7 7 selector: 8 8 app: knot
+1 -1
k8s/knot/ssh-ingress.yaml k8s/knot-personal/ssh-ingress.yaml
··· 2 2 kind: IngressRouteTCP 3 3 metadata: 4 4 name: knot-ssh 5 - namespace: knot 5 + namespace: knot-personal 6 6 spec: 7 7 entryPoints: 8 8 - knot-ssh
+2 -2
k8s/knot/tarpit-deployment.yaml k8s/knot-personal/tarpit-deployment.yaml
··· 2 2 kind: Deployment 3 3 metadata: 4 4 name: tarpit 5 - namespace: knot 5 + namespace: knot-personal 6 6 spec: 7 7 replicas: 1 8 8 selector: ··· 53 53 kind: Service 54 54 metadata: 55 55 name: tarpit 56 - namespace: knot 56 + namespace: knot-personal 57 57 spec: 58 58 selector: 59 59 app: tarpit
+14 -8
k8s/kustomization.yaml
··· 6 6 - juicefs 7 7 - postgres 8 8 - pds 9 - - knot 9 + - knot-personal 10 10 - registry 11 11 - alerting 12 12 - ci ··· 22 22 files: 23 23 - backup.sh=shared/backup.sh 24 24 - name: backup-script 25 - namespace: knot 25 + namespace: knot-personal 26 26 files: 27 27 - backup.sh=shared/backup.sh 28 28 - name: tarpit-script ··· 30 30 files: 31 31 - tarpit.py=shared/tarpit.py 32 32 - name: tarpit-script 33 - namespace: knot 33 + namespace: knot-personal 34 34 files: 35 35 - tarpit.py=shared/tarpit.py 36 36 ··· 39 39 namespace: pds 40 40 type: Opaque 41 41 files: 42 - - access-key=shared/s3-access-key.secret 43 - - secret-key=shared/s3-secret-key.secret 42 + - access-key=shared/scw-s3-access-key.secret 43 + - secret-key=shared/scw-s3-secret-key.secret 44 44 - name: knot-s3-credentials 45 - namespace: knot 45 + namespace: knot-personal 46 46 type: Opaque 47 47 files: 48 - - access-key=shared/s3-access-key.secret 49 - - secret-key=shared/s3-secret-key.secret 48 + - access-key=shared/scw-s3-access-key.secret 49 + - secret-key=shared/scw-s3-secret-key.secret 50 + - name: postgres-s3-credentials 51 + namespace: postgres 52 + type: Opaque 53 + files: 54 + - access-key=shared/scw-s3-access-key.secret 55 + - secret-key=shared/scw-s3-secret-key.secret 50 56 - name: tranquil-secrets 51 57 namespace: pds 52 58 type: Opaque
+1 -1
k8s/opake-staging/appview/deployment.yaml
··· 22 22 runAsNonRoot: true 23 23 containers: 24 24 - name: appview 25 - image: zot.sans-self.org/opake/appview:staging 25 + image: rg.fr-par.scw.cloud/sans-self/opake/appview:staging 26 26 imagePullPolicy: Always 27 27 ports: 28 28 - containerPort: 6100
+1 -1
k8s/opake-staging/web/deployment.yaml
··· 19 19 runAsNonRoot: true 20 20 containers: 21 21 - name: web 22 - image: zot.sans-self.org/opake/web:staging 22 + image: rg.fr-par.scw.cloud/sans-self/opake/web:staging 23 23 imagePullPolicy: Always 24 24 ports: 25 25 - containerPort: 3000
+1 -1
k8s/opake/appview/deployment.yaml
··· 23 23 runAsNonRoot: true 24 24 containers: 25 25 - name: appview 26 - image: zot.sans-self.org/opake/appview:REPLACE_ME 26 + image: rg.fr-par.scw.cloud/sans-self/opake/appview:REPLACE_ME 27 27 ports: 28 28 - containerPort: 6100 29 29 env:
+1 -1
k8s/opake/appview/kustomization.yaml
··· 10 10 - cert.yaml 11 11 12 12 images: 13 - - name: zot.sans-self.org/opake/appview 13 + - name: rg.fr-par.scw.cloud/sans-self/opake/appview 14 14 newTag: latest
+1
k8s/opake/appview/pvc.yaml
··· 4 4 name: opake-appview-data 5 5 namespace: opake 6 6 spec: 7 + storageClassName: longhorn 7 8 accessModes: 8 9 - ReadWriteOnce 9 10 resources:
+1 -1
k8s/opake/jetstream/deployment.yaml
··· 23 23 runAsNonRoot: true 24 24 containers: 25 25 - name: jetstream 26 - image: zot.sans-self.org/infra/jetstream:latest 26 + image: rg.fr-par.scw.cloud/sans-self/infra/jetstream:latest 27 27 ports: 28 28 - name: ws 29 29 containerPort: 6008
+1
k8s/opake/jetstream/pvc.yaml
··· 4 4 name: jetstream-data 5 5 namespace: opake 6 6 spec: 7 + storageClassName: longhorn 7 8 accessModes: 8 9 - ReadWriteOnce 9 10 resources:
+1 -1
k8s/opake/web/deployment.yaml
··· 19 19 runAsNonRoot: true 20 20 containers: 21 21 - name: web 22 - image: zot.sans-self.org/opake/web:REPLACE_ME 22 + image: rg.fr-par.scw.cloud/sans-self/opake/web:REPLACE_ME 23 23 ports: 24 24 - containerPort: 3000 25 25 env:
+1 -1
k8s/opake/web/kustomization.yaml
··· 9 9 - cert.yaml 10 10 11 11 images: 12 - - name: zot.sans-self.org/opake/web 12 + - name: rg.fr-par.scw.cloud/sans-self/opake/web 13 13 newTag: latest
+27 -62
k8s/pds/backup-cronjob.yaml
··· 13 13 template: 14 14 spec: 15 15 restartPolicy: OnFailure 16 - securityContext: 17 - fsGroup: 1000 18 - initContainers: 19 - - name: install-sqlite 16 + serviceAccountName: pds-backup 17 + containers: 18 + - name: backup 20 19 image: rclone/rclone:1.69 21 20 command: 22 21 - sh 23 22 - -c 24 23 - | 25 - apk add --no-cache sqlite > /dev/null 26 - cp /usr/bin/sqlite3 /tools/ 27 - for lib in $(ldd /usr/bin/sqlite3 | awk '/=>/ {print $3}'); do 28 - cp "$lib" /tools/ 29 - done 30 - volumeMounts: 31 - - name: tools 32 - mountPath: /tools 33 - securityContext: 34 - runAsUser: 0 35 - allowPrivilegeEscalation: false 36 - capabilities: 37 - drop: 38 - - ALL 39 - resources: 40 - requests: 41 - cpu: 50m 42 - memory: 64Mi 43 - limits: 44 - cpu: 100m 45 - memory: 128Mi 46 - containers: 47 - - name: backup 48 - image: rclone/rclone:1.69 49 - command: ["sh", "/scripts/backup.sh"] 24 + set -eu 25 + apk add --no-cache postgresql16-client > /dev/null 2>&1 26 + TIMESTAMP=$(date +%Y%m%d-%H%M%S) 27 + 28 + PGPASSWORD="$POSTGRES_PASSWORD" pg_dumpall \ 29 + -h postgres.postgres.svc.cluster.local -U postgres \ 30 + > "/tmp/postgres-${TIMESTAMP}.sql" 31 + echo "dumped: $(wc -c < /tmp/postgres-${TIMESTAMP}.sql) bytes" 32 + 33 + rclone copyto "/tmp/postgres-${TIMESTAMP}.sql" \ 34 + ":s3:sans-self-backups/pds/db/postgres-${TIMESTAMP}.sql" \ 35 + --s3-provider Other \ 36 + --s3-access-key-id "${S3_ACCESS_KEY}" \ 37 + --s3-secret-access-key "${S3_SECRET_KEY}" \ 38 + --s3-endpoint s3.fr-par.scw.cloud \ 39 + --s3-region fr-par \ 40 + --s3-no-check-bucket \ 41 + --s3-acl private 42 + echo "uploaded: postgres-${TIMESTAMP}.sql" 50 43 env: 51 - - name: BACKUP_DB_GLOB 52 - value: "/data/*.sqlite" 53 - - name: BACKUP_SYNC_DIRS 54 - value: "" 55 - - name: BACKUP_BUCKET 56 - value: sans-self-net 57 - - name: BACKUP_PREFIX 58 - value: pds 44 + - name: POSTGRES_PASSWORD 45 + valueFrom: 46 + secretKeyRef: 47 + name: postgres-credentials 48 + key: password 59 49 - name: S3_ACCESS_KEY 60 50 valueFrom: 61 51 secretKeyRef: ··· 67 57 name: pds-s3-credentials 68 58 key: secret-key 69 59 volumeMounts: 70 - - name: service-data 71 - mountPath: /data 72 - readOnly: true 73 60 - name: tmp 74 61 mountPath: /tmp 75 - - name: tools 76 - mountPath: /tools 77 - readOnly: true 78 - - name: scripts 79 - mountPath: /scripts 80 - readOnly: true 81 - securityContext: 82 - runAsUser: 1000 83 - runAsNonRoot: true 84 - allowPrivilegeEscalation: false 85 - capabilities: 86 - drop: 87 - - ALL 88 62 resources: 89 63 requests: 90 64 cpu: 50m ··· 93 67 cpu: 250m 94 68 memory: 512Mi 95 69 volumes: 96 - - name: service-data 97 - persistentVolumeClaim: 98 - claimName: pds-data 99 70 - name: tmp 100 71 emptyDir: 101 72 sizeLimit: 1Gi 102 - - name: tools 103 - emptyDir: 104 - sizeLimit: 50Mi 105 - - name: scripts 106 - configMap: 107 - name: backup-script
+1 -1
k8s/pds/kustomization.yaml
··· 9 9 - service.yaml 10 10 - ingress.yaml 11 11 - cert.yaml 12 - - backup-cronjob.yaml 13 12 - network-policy.yaml 14 13 - tarpit-deployment.yaml 15 14 - tranquil-pvc.yaml ··· 17 16 - tranquil-frontend-deployment.yaml 18 17 - tranquil-service.yaml 19 18 - tranquil-frontend-service.yaml 19 + - valkey-deployment.yaml 20 20 21 21 generatorOptions: 22 22 disableNameSuffixHash: true
+3 -3
k8s/pds/network-policy.yaml
··· 90 90 protocol: UDP 91 91 - port: 53 92 92 protocol: TCP 93 - # Redis (shared cache, juicefs namespace DB 1) 93 + # Valkey cache (same namespace) 94 94 - to: 95 - - namespaceSelector: 95 + - podSelector: 96 96 matchLabels: 97 - kubernetes.io/metadata.name: juicefs 97 + app: valkey 98 98 ports: 99 99 - port: 6379 100 100 # External (S3, bsky.network, plc.directory, etc)
+2 -2
k8s/pds/tranquil-deployment.yaml
··· 34 34 fsGroup: 1000 35 35 containers: 36 36 - name: tranquil-pds 37 - image: zot.sans-self.org/infra/tranquil-pds:latest 37 + image: rg.fr-par.scw.cloud/sans-self/infra/tranquil-pds:latest 38 38 ports: 39 39 - name: http 40 40 containerPort: 3000 ··· 122 122 volumes: 123 123 - name: data 124 124 persistentVolumeClaim: 125 - claimName: tranquil-data 125 + claimName: tranquil-data-longhorn 126 126 - name: msmtp-config 127 127 secret: 128 128 secretName: msmtp-config
+1 -1
k8s/pds/tranquil-frontend-deployment.yaml
··· 19 19 runAsGroup: 101 20 20 containers: 21 21 - name: frontend 22 - image: zot.sans-self.org/infra/tranquil-frontend:latest 22 + image: rg.fr-par.scw.cloud/sans-self/infra/tranquil-frontend:latest 23 23 # nginx needs writable cache/pid dirs 24 24 volumeMounts: 25 25 - name: cache
+4 -4
k8s/pds/tranquil-pvc.yaml
··· 1 1 apiVersion: v1 2 2 kind: PersistentVolumeClaim 3 3 metadata: 4 - name: tranquil-data 4 + name: tranquil-data-longhorn 5 5 namespace: pds 6 6 spec: 7 7 accessModes: 8 - - ReadWriteMany 9 - storageClassName: juicefs-sc 8 + - ReadWriteOnce 9 + storageClassName: longhorn 10 10 resources: 11 11 requests: 12 - storage: 20Gi 12 + storage: 5Gi
k8s/pds/tranquil-valkey-url.secret

This is a binary file and will not be displayed.

+57
k8s/pds/valkey-deployment.yaml
··· 1 + apiVersion: apps/v1 2 + kind: Deployment 3 + metadata: 4 + name: valkey 5 + namespace: pds 6 + spec: 7 + replicas: 1 8 + strategy: 9 + type: Recreate 10 + selector: 11 + matchLabels: 12 + app: valkey 13 + template: 14 + metadata: 15 + labels: 16 + app: valkey 17 + spec: 18 + containers: 19 + - name: valkey 20 + image: redis:7-alpine 21 + command: ["redis-server"] 22 + args: 23 + - --maxmemory 24 + - 64mb 25 + - --maxmemory-policy 26 + - allkeys-lru 27 + - --save 28 + - "" 29 + ports: 30 + - containerPort: 6379 31 + resources: 32 + requests: 33 + cpu: 25m 34 + memory: 32Mi 35 + limits: 36 + cpu: 100m 37 + memory: 96Mi 38 + livenessProbe: 39 + exec: 40 + command: ["redis-cli", "ping"] 41 + periodSeconds: 30 42 + readinessProbe: 43 + exec: 44 + command: ["redis-cli", "ping"] 45 + periodSeconds: 10 46 + --- 47 + apiVersion: v1 48 + kind: Service 49 + metadata: 50 + name: valkey 51 + namespace: pds 52 + spec: 53 + selector: 54 + app: valkey 55 + ports: 56 + - port: 6379 57 + targetPort: 6379
+74
k8s/postgres/backup-cronjob.yaml
··· 1 + apiVersion: batch/v1 2 + kind: CronJob 3 + metadata: 4 + name: postgres-backup 5 + namespace: postgres 6 + spec: 7 + schedule: "0 2 * * *" 8 + concurrencyPolicy: Forbid 9 + successfulJobsHistoryLimit: 3 10 + failedJobsHistoryLimit: 3 11 + jobTemplate: 12 + spec: 13 + template: 14 + metadata: 15 + labels: 16 + app: postgres 17 + spec: 18 + restartPolicy: OnFailure 19 + containers: 20 + - name: backup 21 + image: postgres:18-alpine 22 + command: 23 + - sh 24 + - -c 25 + - | 26 + set -eu 27 + apk add --no-cache rclone > /dev/null 2>&1 28 + TIMESTAMP=$(date +%Y%m%d-%H%M%S) 29 + 30 + PGPASSWORD="$POSTGRES_PASSWORD" pg_dumpall \ 31 + -h postgres -U postgres \ 32 + > "/tmp/postgres-${TIMESTAMP}.sql" 33 + echo "dumped: $(wc -c < /tmp/postgres-${TIMESTAMP}.sql) bytes" 34 + 35 + rclone copyto "/tmp/postgres-${TIMESTAMP}.sql" \ 36 + ":s3:sans-self-backups/postgres/postgres-${TIMESTAMP}.sql" \ 37 + --s3-provider Other \ 38 + --s3-access-key-id "${S3_ACCESS_KEY}" \ 39 + --s3-secret-access-key "${S3_SECRET_KEY}" \ 40 + --s3-endpoint s3.fr-par.scw.cloud \ 41 + --s3-region fr-par \ 42 + --s3-no-check-bucket \ 43 + --s3-acl private 44 + echo "uploaded: postgres-${TIMESTAMP}.sql" 45 + env: 46 + - name: POSTGRES_PASSWORD 47 + valueFrom: 48 + secretKeyRef: 49 + name: postgres-credentials 50 + key: superuser-password 51 + - name: S3_ACCESS_KEY 52 + valueFrom: 53 + secretKeyRef: 54 + name: postgres-s3-credentials 55 + key: access-key 56 + - name: S3_SECRET_KEY 57 + valueFrom: 58 + secretKeyRef: 59 + name: postgres-s3-credentials 60 + key: secret-key 61 + volumeMounts: 62 + - name: tmp 63 + mountPath: /tmp 64 + resources: 65 + requests: 66 + cpu: 50m 67 + memory: 128Mi 68 + limits: 69 + cpu: 250m 70 + memory: 512Mi 71 + volumes: 72 + - name: tmp 73 + emptyDir: 74 + sizeLimit: 1Gi
+88
k8s/postgres/deployment.yaml
··· 1 + apiVersion: apps/v1 2 + kind: Deployment 3 + metadata: 4 + name: postgres 5 + namespace: postgres 6 + spec: 7 + replicas: 1 8 + strategy: 9 + type: Recreate 10 + selector: 11 + matchLabels: 12 + app: postgres 13 + template: 14 + metadata: 15 + labels: 16 + app: postgres 17 + spec: 18 + securityContext: 19 + fsGroup: 70 20 + runAsUser: 70 21 + runAsGroup: 70 22 + runAsNonRoot: true 23 + containers: 24 + - name: postgres 25 + image: postgres:18-alpine 26 + ports: 27 + - containerPort: 5432 28 + env: 29 + - name: POSTGRES_PASSWORD 30 + valueFrom: 31 + secretKeyRef: 32 + name: postgres-credentials 33 + key: superuser-password 34 + - name: PGDATA 35 + value: /var/lib/postgresql/data/pgdata 36 + - name: TRANQUIL_PASSWORD 37 + valueFrom: 38 + secretKeyRef: 39 + name: postgres-credentials 40 + key: tranquil-password 41 + - name: OPAKE_STAGING_PASSWORD 42 + valueFrom: 43 + secretKeyRef: 44 + name: postgres-credentials 45 + key: opake-staging-password 46 + volumeMounts: 47 + - name: data 48 + mountPath: /var/lib/postgresql/data 49 + - name: initdb 50 + mountPath: /docker-entrypoint-initdb.d 51 + readOnly: true 52 + startupProbe: 53 + exec: 54 + command: ["pg_isready", "-U", "postgres"] 55 + periodSeconds: 5 56 + failureThreshold: 60 57 + livenessProbe: 58 + exec: 59 + command: ["sh", "-c", "psql -U postgres -c 'SELECT 1'"] 60 + periodSeconds: 30 61 + timeoutSeconds: 5 62 + failureThreshold: 3 63 + readinessProbe: 64 + exec: 65 + command: ["sh", "-c", "psql -U postgres -c 'SELECT 1'"] 66 + periodSeconds: 10 67 + timeoutSeconds: 5 68 + failureThreshold: 2 69 + securityContext: 70 + allowPrivilegeEscalation: false 71 + capabilities: 72 + drop: 73 + - ALL 74 + resources: 75 + requests: 76 + cpu: 100m 77 + memory: 256Mi 78 + limits: 79 + cpu: 500m 80 + memory: 1Gi 81 + volumes: 82 + - name: data 83 + persistentVolumeClaim: 84 + claimName: postgres-data 85 + - name: initdb 86 + configMap: 87 + name: postgres-initdb 88 + defaultMode: 0555
+2 -10
k8s/postgres/initdb-configmap.yaml
··· 9 9 #!/bin/sh 10 10 set -e 11 11 echo "host all all all scram-sha-256" >> "$PGDATA/pg_hba.conf" 12 - echo "host replication replicator all scram-sha-256" >> "$PGDATA/pg_hba.conf" 13 12 14 - 02-replication.sh: | 15 - #!/bin/sh 16 - set -e 17 - psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname postgres <<EOSQL 18 - CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD '$REPLICATION_PASSWORD'; 19 - EOSQL 20 - 21 - 03-databases.sh: | 13 + 02-databases.sh: | 22 14 #!/bin/sh 23 15 set -e 24 16 psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname postgres <<EOSQL ··· 26 18 CREATE DATABASE pds OWNER tranquil; 27 19 EOSQL 28 20 29 - 04-opake.sh: | 21 + 03-opake.sh: | 30 22 #!/bin/sh 31 23 set -e 32 24 psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname postgres <<EOSQL
+3 -4
k8s/postgres/kustomization.yaml
··· 3 3 4 4 resources: 5 5 - namespace.yaml 6 - - statefulset.yaml 7 - - scripts-configmap.yaml 6 + - deployment.yaml 7 + - pvc.yaml 8 8 - initdb-configmap.yaml 9 9 - service.yaml 10 - - network-policy.yaml 10 + - backup-cronjob.yaml 11 11 12 12 generatorOptions: 13 13 disableNameSuffixHash: true ··· 19 19 files: 20 20 - superuser-password=superuser-password.secret 21 21 - tranquil-password=postgres-password.secret 22 - - replication-password=replication-password.secret 23 22 - opake-staging-password=opake-staging-password.secret
-33
k8s/postgres/network-policy.yaml
··· 1 - apiVersion: networking.k8s.io/v1 2 - kind: NetworkPolicy 3 - metadata: 4 - name: postgres-ingress 5 - namespace: postgres 6 - spec: 7 - podSelector: 8 - matchLabels: 9 - app: postgres 10 - policyTypes: 11 - - Ingress 12 - ingress: 13 - # PDS namespace → postgres 14 - - from: 15 - - namespaceSelector: 16 - matchLabels: 17 - kubernetes.io/metadata.name: pds 18 - ports: 19 - - port: 5432 20 - # Opake staging → postgres 21 - - from: 22 - - namespaceSelector: 23 - matchLabels: 24 - kubernetes.io/metadata.name: opake-staging 25 - ports: 26 - - port: 5432 27 - # Replication between primary and standby 28 - - from: 29 - - podSelector: 30 - matchLabels: 31 - app: postgres 32 - ports: 33 - - port: 5432
+12
k8s/postgres/pvc.yaml
··· 1 + apiVersion: v1 2 + kind: PersistentVolumeClaim 3 + metadata: 4 + name: postgres-data 5 + namespace: postgres 6 + spec: 7 + storageClassName: longhorn 8 + accessModes: 9 + - ReadWriteOnce 10 + resources: 11 + requests: 12 + storage: 5Gi
-30
k8s/postgres/service.yaml
··· 1 - # Primary service — tranquil connects here for reads+writes 2 1 apiVersion: v1 3 2 kind: Service 4 3 metadata: ··· 7 6 spec: 8 7 selector: 9 8 app: postgres 10 - statefulset.kubernetes.io/pod-name: postgres-0 11 - ports: 12 - - port: 5432 13 - targetPort: 5432 14 - --- 15 - # Headless service for StatefulSet pod DNS (postgres-0.postgres-headless, etc) 16 - apiVersion: v1 17 - kind: Service 18 - metadata: 19 - name: postgres-headless 20 - namespace: postgres 21 - spec: 22 - clusterIP: None 23 - selector: 24 - app: postgres 25 - ports: 26 - - port: 5432 27 - targetPort: 5432 28 - --- 29 - # Read-only service — hits the standby for read scaling if needed 30 - apiVersion: v1 31 - kind: Service 32 - metadata: 33 - name: postgres-ro 34 - namespace: postgres 35 - spec: 36 - selector: 37 - app: postgres 38 - statefulset.kubernetes.io/pod-name: postgres-1 39 9 ports: 40 10 - port: 5432 41 11 targetPort: 5432
+2 -2
k8s/postgres/statefulset.yaml
··· 100 100 resources: 101 101 requests: 102 102 cpu: 100m 103 - memory: 128Mi 103 + memory: 256Mi 104 104 limits: 105 105 cpu: 500m 106 - memory: 512Mi 106 + memory: 1Gi 107 107 volumes: 108 108 - name: scripts 109 109 configMap:
+1 -1
k8s/registry/cert.yaml
··· 9 9 name: letsencrypt-prod 10 10 kind: ClusterIssuer 11 11 dnsNames: 12 - - zot.sans-self.org 12 + - rg.fr-par.scw.cloud/sans-self
+2 -12
k8s/registry/configmap.yaml
··· 9 9 "distSpecVersion": "1.1.0", 10 10 "storage": { 11 11 "rootDirectory": "/data", 12 - "dedupe": true 12 + "dedupe": false 13 13 }, 14 14 "http": { 15 15 "address": "0.0.0.0", ··· 28 28 } 29 29 } 30 30 }, 31 - "extensions": { 32 - "search": { 33 - "enable": true, 34 - "cve": { 35 - "updateInterval": "2h" 36 - } 37 - }, 38 - "ui": { 39 - "enable": true 40 - } 41 - } 31 + "extensions": {} 42 32 }
+1 -1
k8s/registry/ingress.yaml
··· 19 19 tls: 20 20 secretName: zot-sans-self-org-tls 21 21 routes: 22 - - match: Host(`zot.sans-self.org`) 22 + - match: Host(`rg.fr-par.scw.cloud/sans-self`) 23 23 kind: Rule 24 24 middlewares: 25 25 - name: strip-server-headers
+2 -2
k8s/shared/backup.sh
··· 11 11 --s3-provider Other \ 12 12 --s3-access-key-id "${S3_ACCESS_KEY}" \ 13 13 --s3-secret-access-key "${S3_SECRET_KEY}" \ 14 - --s3-endpoint nbg1.your-objectstorage.com \ 15 - --s3-region nbg1 \ 14 + --s3-endpoint s3.fr-par.scw.cloud \ 15 + --s3-region fr-par \ 16 16 --s3-no-check-bucket \ 17 17 --s3-acl private 18 18 }
k8s/shared/scw-s3-access-key.secret

This is a binary file and will not be displayed.

k8s/shared/scw-s3-secret-key.secret

This is a binary file and will not be displayed.

+7 -6
kube.tf
··· 48 48 # Tangled knot Git SSH passthrough (git clone git@knot.sans-self.org:handle/repo) 49 49 traefik_additional_ports = [{ name = "knot-ssh", port = 22, exposedPort = 22 }] 50 50 51 - # Storage — JuiceFS CSI handles persistent storage; Hetzner CSI disabled to 52 - # avoid the upstream blkid/mkfs bug (kubernetes/kubernetes#95183). 53 - # local-path enabled for JuiceFS Redis metadata (can't use JuiceFS for its own metadata) 54 - enable_longhorn = false 55 - disable_hetzner_csi = true 56 - enable_local_storage = true 51 + # Storage — Longhorn for replicated persistent storage on local NVMe. 52 + # Hetzner CSI disabled (upstream blkid/mkfs bug). 53 + # local-path for ephemeral/cache volumes (valkey, redis metadata). 54 + enable_longhorn = true 55 + longhorn_replica_count = 2 56 + disable_hetzner_csi = true 57 + enable_local_storage = true 57 58 hetzner_ccm_use_helm = true 58 59 59 60 # Kubernetes version & upgrades