anydrop/k8s/minio.yml
ordinarthur 0b639dfc3c feat: encrypted cloud relay (Phase 2)
Adds a "Via AnyDrop" flow for senders who need to reach someone not
present on the mesh. The file is sealed client-side (XChaCha20-Poly1305),
uploaded directly to an in-cluster MinIO bucket via a presigned PUT, and
handed off to the recipient as a URL whose fragment carries the key.
The server only ever sees ciphertext, opaque metadata blobs, and sizes.

- server: transfers table (drizzle migration), /api/transfers CRUD +
  consume endpoint, presigned PUT/GET via @aws-sdk/client-s3, cleanup
  loop that purges expired + exhausted blobs.
- web: @noble/ciphers sealFile/openFile, high-level sendCloud/receive
  helpers, CloudSharePanel on Home, /r/:id receive page, /inbox page
  for signed-in users (sent + received tabs).
- k8s: MinIO StatefulSet with bucket-init initContainer, S3 env vars
  on the server Deployment (credentials pulled from minio-credentials
  Secret).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-20 11:09:58 +02:00

139 lines
3.9 KiB
YAML

# MinIO — S3-compatible object storage for the encrypted relay.
#
# Phase 2 context: the server never sees plaintext. Clients upload an
# XChaCha20-Poly1305 ciphertext directly to MinIO via a presigned PUT URL,
# and recipients download via a presigned GET URL. The symmetric key stays
# in the browser URL fragment (#k=...); the server only knows the storage
# key and blob size.
#
# Single-node deployment in-cluster. Bucket "transfers" is created on boot
# via a one-shot initContainer (mc mb --ignore-existing).
#
# DEPLOY-TIME REQUIREMENT: the API port (9000) must be publicly reachable at
# the host declared by S3_ENDPOINT in server.yml (s3.anydrop.arthurbarre.fr).
# Presigned URLs are signed against that host, and the browser must be able
# to resolve it. Configure an external route (traefik ingress, nginx, etc.)
# from that hostname to this Service on port 9000.
apiVersion: v1
kind: Service
metadata:
name: minio
namespace: anydrop
labels:
app: minio
spec:
clusterIP: None
selector:
app: minio
ports:
- name: api
port: 9000
targetPort: 9000
- name: console
port: 9001
targetPort: 9001
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: minio
namespace: anydrop
spec:
serviceName: minio
replicas: 1
selector:
matchLabels:
app: minio
template:
metadata:
labels:
app: minio
spec:
initContainers:
- name: ensure-bucket
image: minio/mc:latest
command:
- /bin/sh
- -c
- |
set -e
until mc alias set local http://minio:9000 "$MINIO_ROOT_USER" "$MINIO_ROOT_PASSWORD" 2>/dev/null; do
echo "waiting for minio..."
sleep 2
done
mc mb --ignore-existing local/transfers
# Keep the bucket private — every object is served via presigned URL.
mc anonymous set none local/transfers
echo "bucket ready"
env:
- name: MINIO_ROOT_USER
valueFrom:
secretKeyRef:
name: minio-credentials
key: access_key
- name: MINIO_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: minio-credentials
key: secret_key
containers:
- name: minio
image: minio/minio:latest
args:
- server
- /data
- --console-address
- ":9001"
ports:
- containerPort: 9000
name: api
- containerPort: 9001
name: console
env:
- name: MINIO_ROOT_USER
valueFrom:
secretKeyRef:
name: minio-credentials
key: access_key
- name: MINIO_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: minio-credentials
key: secret_key
- name: MINIO_BROWSER_REDIRECT_URL
value: "https://anydrop.arthurbarre.fr/minio-console"
volumeMounts:
- name: data
mountPath: /data
livenessProbe:
httpGet:
path: /minio/health/live
port: 9000
initialDelaySeconds: 30
periodSeconds: 20
timeoutSeconds: 5
readinessProbe:
httpGet:
path: /minio/health/ready
port: 9000
initialDelaySeconds: 5
periodSeconds: 10
timeoutSeconds: 3
resources:
requests:
memory: "256Mi"
cpu: "100m"
limits:
memory: "1Gi"
cpu: "1000m"
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 20Gi