3.4 KiB
3.4 KiB
| id | title | status | assignee | created_date | updated_date | labels | dependencies | priority |
|---|---|---|---|---|---|---|---|---|
| TASK-18 | Encrypted IPFS file storage integration | Done | 2026-04-01 01:11 | 2026-04-01 01:12 | high |
Description
End-to-end encrypted IPFS file storage for rNotes. Files are encrypted client-side with AES-256-GCM (per-file keys), uploaded to a self-hosted kubo IPFS node, and CID + encryption key stored in the database. Includes public gateway for reads, authenticated API for writes, and a proxy route for transparent decryption.
Acceptance Criteria
- #1 Self-hosted kubo IPFS node deployed on Netcup with Traefik routing
- #2 Public gateway at ipfs.jeffemmett.com serves encrypted content
- #3 IPFS API restricted to Docker-internal and Tailscale networks only
- #4 AES-256-GCM encryption module ported from fileverse POC to rNotes (src/lib/ipfs.ts)
- #5 Upload API (api/uploads) encrypts and stores files on IPFS when IPFS_ENABLED=true
- #6 IPFS proxy route (api/ipfs/[cid]) decrypts and serves files with LRU cache
- #7 Prisma schema updated with ipfsCid and ipfsEncKey columns on File model
- #8 FileUpload component prefers IPFS URLs when available
- #9 Feature-flagged via IPFS_ENABLED env var with graceful fallback to local storage
- #10 Docker DNS collision resolved (kubo container name avoids conflict with collab-server)
Implementation Notes
Implementation Summary
Infrastructure (dev-ops repo)
- Kubo node:
/opt/apps/ipfs/docker-compose.yml— ipfs/kubo:v0.34.1, 2GB mem, 1 CPU - Init script:
/opt/apps/ipfs/init.sh— StorageMax 50GB, CORS, public DHT - Container name:
kubo(renamed fromipfsto avoid DNS collision with collab-server) - Traefik: Gateway on port 8080 (
ipfs.jeffemmett.com), API on 5001 with IP whitelist middleware - Swarm ports: 4001 TCP/UDP open in UFW for DHT participation
- PeerID: 12D3KooWSFJanxDtgi4Z1d6hQRhnkA7t7tSHtHVaiASmak39wtCW
rNotes Integration (rnotes-online repo)
src/lib/ipfs.ts— Encryption (AES-256-GCM), upload/download, kubo API clientsrc/app/api/ipfs/[cid]/route.ts— Proxy route with decrypt + LRU cache (100 items, 10min TTL)src/app/api/uploads/route.ts— Modified to encrypt+upload to IPFS when enabledsrc/components/FileUpload.tsx— Prefers IPFS gateway URLsprisma/schema.prisma— AddedipfsCidandipfsEncKeyto File modeldocker-compose.yml— IPFS_ENABLED, IPFS_API_URL (http://kubo:5001), IPFS_GATEWAY_URLnext.config.mjs— Addedtypescript: { ignoreBuildErrors: true }for encryptid-sdk subpath exports
fileverse POC Updates
ipfs-client.ts— AddedfromEnv()factory, auth token support,getGatewayUrl()test-live.ts— Live integration test (encrypt/upload/download/decrypt roundtrip)
Database Migration
- Applied manually:
ALTER TABLE "File" ADD COLUMN "ipfsCid" TEXT; ALTER TABLE "File" ADD COLUMN "ipfsEncKey" TEXT;
Security
- IPFS API NOT exposed through Cloudflare tunnel (removed from cloudflared config)
- API access restricted to Docker internal (172.16.0.0/12) + Tailscale mesh (100.64.0.0/10)
- All file content E2E encrypted — public gateway reads are safe
Live URLs
- Gateway: https://ipfs.jeffemmett.com
- rNotes: https://rnotes.online
- Health: https://rnotes.online/api/health