diff --git a/k8s/apps/pkg-repo/ci-rbac.yaml b/k8s/apps/pkg-repo/ci-rbac.yaml
new file mode 100644
index 0000000..3ab2e63
--- /dev/null
+++ b/k8s/apps/pkg-repo/ci-rbac.yaml
@@ -0,0 +1,41 @@
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+ name: pkg-repo-ci
+ namespace: pkg-repo
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ name: pkg-repo-publisher
+ namespace: pkg-repo
+rules:
+ - apiGroups: [""]
+ resources: ["pods"]
+ verbs: ["get", "list"]
+ - apiGroups: [""]
+ resources: ["pods/exec"]
+ verbs: ["create"]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: pkg-repo-ci-publisher
+ namespace: pkg-repo
+subjects:
+ - kind: ServiceAccount
+ name: pkg-repo-ci
+ namespace: pkg-repo
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: pkg-repo-publisher
+---
+apiVersion: v1
+kind: Secret
+metadata:
+ name: pkg-repo-ci-token
+ namespace: pkg-repo
+ annotations:
+ kubernetes.io/service-account.name: pkg-repo-ci
+type: kubernetes.io/service-account-token
diff --git a/k8s/apps/pkg-repo/deployment.yaml b/k8s/apps/pkg-repo/deployment.yaml
index 6ab1483..b1848df 100644
--- a/k8s/apps/pkg-repo/deployment.yaml
+++ b/k8s/apps/pkg-repo/deployment.yaml
@@ -1,4 +1,76 @@
---
+apiVersion: v1
+kind: PersistentVolumeClaim
+metadata:
+ name: pkg-repo-pvc
+ namespace: pkg-repo
+spec:
+ storageClassName: longhorn
+ accessModes: [ReadWriteOnce]
+ resources:
+ requests:
+ storage: 10Gi
+---
+apiVersion: v1
+kind: ConfigMap
+metadata:
+ name: reprepro-config
+ namespace: pkg-repo
+data:
+ distributions: |
+ Codename: trixie
+ Suite: stable
+ Components: main
+ Architectures: amd64 arm64
+ SignWith: yes
+ options: |
+ basedir /repo/debian
+ init.sh: |
+ #!/bin/bash
+ set -e
+
+ # Install reprepro (one-time, cached in PVC)
+ if ! command -v reprepro &>/dev/null; then
+ apt-get update && apt-get install -y --no-install-recommends reprepro gpg
+ rm -rf /var/lib/apt/lists/*
+ fi
+
+ # Import GPG key
+ gpg --batch --import /gpg/private.key 2>/dev/null || true
+
+ # Export public key for clients
+ gpg --armor --export > /repo/pubkey.gpg
+
+ # Init reprepro if not done
+ if [ ! -f /repo/debian/db/version ]; then
+ echo "Initializing reprepro..."
+ mkdir -p /repo/debian/conf
+ cp /config/distributions /repo/debian/conf/
+ cp /config/options /repo/debian/conf/
+ cd /repo/debian && reprepro export
+ fi
+
+ # Always ensure conf is up-to-date
+ mkdir -p /repo/debian/conf
+ cp /config/distributions /repo/debian/conf/
+ cp /config/options /repo/debian/conf/
+
+ # Index page
+ cat > /repo/index.html <<'HTML'
+
+
n0ball repo
+
+ n0ball Package Repository
+
+
+ HTML
+
+ echo "Repo initialized. Waiting for commands..."
+ sleep infinity
+---
apiVersion: apps/v1
kind: Deployment
metadata:
@@ -23,11 +95,33 @@ spec:
- name: repo-data
mountPath: /usr/share/nginx/html
readOnly: true
+ - name: reprepro
+ image: debian:trixie
+ command: ["bash", "/config/init.sh"]
+ volumeMounts:
+ - name: repo-data
+ mountPath: /repo
+ - name: gpg-key
+ mountPath: /gpg
+ readOnly: true
+ - name: config
+ mountPath: /config
+ readOnly: true
+ - name: incoming
+ mountPath: /incoming
volumes:
- name: repo-data
- nfs:
- server: 192.168.50.50
- path: /mnt/NFS/pve-nfs/pkg-repo
+ persistentVolumeClaim:
+ claimName: pkg-repo-pvc
+ - name: gpg-key
+ secret:
+ secretName: repo-gpg-key
+ - name: config
+ configMap:
+ name: reprepro-config
+ defaultMode: 0755
+ - name: incoming
+ emptyDir: {}
---
apiVersion: v1
kind: Service
diff --git a/k8s/apps/pkg-repo/kustomization.yaml b/k8s/apps/pkg-repo/kustomization.yaml
index 34a36c7..cb387ef 100644
--- a/k8s/apps/pkg-repo/kustomization.yaml
+++ b/k8s/apps/pkg-repo/kustomization.yaml
@@ -4,3 +4,4 @@ resources:
- namespace.yaml
- deployment.yaml
- ingress.yaml
+ - ci-rbac.yaml
diff --git a/k8s/infrastructure/sops/kustomization.yaml b/k8s/infrastructure/sops/kustomization.yaml
index 9e3b4b7..f3922cd 100644
--- a/k8s/infrastructure/sops/kustomization.yaml
+++ b/k8s/infrastructure/sops/kustomization.yaml
@@ -11,3 +11,4 @@ resources:
- smtp-secret.yaml
- grafana-smtp-secret.yaml
- vaultwarden-smtp-secret.yaml
+ - repo-gpg-key-secret.yaml
diff --git a/k8s/infrastructure/sops/repo-gpg-key-secret.yaml b/k8s/infrastructure/sops/repo-gpg-key-secret.yaml
new file mode 100644
index 0000000..912a6c3
--- /dev/null
+++ b/k8s/infrastructure/sops/repo-gpg-key-secret.yaml
@@ -0,0 +1,27 @@
+apiVersion: v1
+kind: Secret
+metadata:
+ name: repo-gpg-key
+ namespace: pkg-repo
+stringData:
+ private.key: ENC[AES256_GCM,data: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,iv:cMtSMutwl2xFah4N9ETjQM/mksUzy28xQDDs0pvvihQ=,tag:babUz1bdcTxYFblzxb7eMg==,type:str]
+sops:
+ kms: []
+ gcp_kms: []
+ azure_kv: []
+ hc_vault: []
+ age:
+ - recipient: age1y5rw08wm2s2hemapzf43c0l4xass7fhc55qh3n4cxtuxzrj8q3cqtydy7m
+ enc: |
+ -----BEGIN AGE ENCRYPTED FILE-----
+ YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSAvV0JQQjFkQW01L1AvSWI4
+ c3FYVkRWLzc1T1FVVE1zUEMrVE9sOENwQlZvCjViL2pYRnRrQWlla1dHOFNOUHpt
+ R24xbGdGWlc0NXhpSGRlb3V5b2x0NGsKLS0tIGd2VG9adG8vV1ROQ3FPM3MrV3Y2
+ dUpjVWU2d0NuRkVVeXVmeGhWcHBvck0Kqp0OCx6NJOYobX1VQaedca89n8HbEYHl
+ GexaSWuuc8qPS9r9Wo9q4gcOGYd2UAIOxZCpY0nlBSvrA/HzMbN6lw==
+ -----END AGE ENCRYPTED FILE-----
+ lastmodified: "2026-03-10T14:42:17Z"
+ mac: ENC[AES256_GCM,data:wlxIC7SureKW2yVWFonx7BhN9Ff2j+WC1xH6QjpCd4ZR7qgzrI9SxiTVzNwsyssAkmTM8ntaEYl4Uh70GmkIUWnRzk870M+TspYeGcbP23Kg7evWpGDenf+WFDh8SI1DdI0nPObefW6WbNhWhqh+8ZeBp/KWwSIaz8NuV8fL9Wg=,iv:mecMWQHjBP6Gw31umjrzlVCgF8h15kMAv6gC5zrqhCE=,tag:Wa5/7L9gHWMDnL2O/dylUA==,type:str]
+ pgp: []
+ encrypted_regex: ^(data|stringData)$
+ version: 3.9.4