Skip to content

Commit 6833eb7

Browse files
feat: IPFS/IPNS integration for Real-Currents
- Add custom server.js with IPFS gateway, IPNS gateway, deploy API - Add ipfs-http-client and mime-types dependencies - Update dev/start scripts to use custom server - Add GitHub Actions deploy.yml (Gandi + IPFS + IPNS publish) - Update next.config: trailingSlash, images.unoptimized - Add IpfsLinks component and /api/ipfs/current endpoint - Add .env.example and docs/IPFS-INTEGRATION.md Co-authored-by: John Hall <dancingfrog@users.noreply.github.com>
1 parent e9a6cd1 commit 6833eb7

9 files changed

Lines changed: 2012 additions & 19 deletions

File tree

.env.example

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# IPFS Worker Node (Phase 1 - VPS with Kubo + Caddy)
2+
# Required for /ipfs/*, /ipns/*, /api/deploy, /api/ipns/publish to work
3+
IPFS_NODE_HOST=ipfs-api.real-currents.com
4+
IPFS_USER=gandi
5+
IPFS_PASS=your-password-here
6+
7+
# Deploy API authentication (must match GitHub Actions secret)
8+
DEPLOY_SECRET=generate-a-strong-random-secret
9+
10+
# Server
11+
PORT=3000
12+
NODE_ENV=production

.github/workflows/deploy.yml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: Deploy to Gandi + IPFS
2+
3+
on:
4+
push:
5+
branches: [main]
6+
workflow_dispatch:
7+
8+
jobs:
9+
deploy:
10+
runs-on: ubuntu-latest
11+
12+
steps:
13+
- name: Checkout
14+
uses: actions/checkout@v4
15+
with:
16+
submodules: 'true'
17+
18+
- name: Setup Node.js
19+
uses: ./.github/workflows/node.js
20+
21+
- name: Setup Quarto
22+
uses: ./.github/workflows/quarto
23+
24+
- name: Build Site (Quarto + Next.js)
25+
run: npm run build
26+
27+
- name: Deploy to Gandi via Git
28+
env:
29+
GANDI_GIT_URL: ${{ secrets.GANDI_GIT_URL }}
30+
run: |
31+
git config user.name "GitHub Actions"
32+
git config user.email "actions@github.com"
33+
git remote add gandi $GANDI_GIT_URL 2>/dev/null || git remote set-url gandi $GANDI_GIT_URL
34+
git push gandi main --force
35+
36+
- name: Install ipfs-car CLI
37+
run: npm install -g ipfs-car
38+
39+
- name: Package Build as CAR
40+
run: ipfs-car pack out --output deploy.car
41+
42+
- name: Upload CAR to IPFS (via Gandi Gateway)
43+
id: ipfs_upload
44+
env:
45+
DEPLOY_SECRET: ${{ secrets.DEPLOY_SECRET }}
46+
run: |
47+
RESPONSE=$(curl -s -X POST https://www.real-currents.com/api/deploy \
48+
-H "x-api-key: $DEPLOY_SECRET" \
49+
-H "Content-Type: application/vnd.ipld.car" \
50+
--data-binary @deploy.car)
51+
52+
echo "IPFS Response: $RESPONSE"
53+
CID=$(echo $RESPONSE | jq -r '.cid')
54+
echo "cid=$CID" >> $GITHUB_OUTPUT
55+
echo "Deployed to IPFS: $CID"
56+
57+
- name: Update IPNS Pointer (xr-baseline-0)
58+
env:
59+
DEPLOY_SECRET: ${{ secrets.DEPLOY_SECRET }}
60+
CID: ${{ steps.ipfs_upload.outputs.cid }}
61+
run: |
62+
curl -X POST https://www.real-currents.com/api/ipns/publish \
63+
-H "x-api-key: $DEPLOY_SECRET" \
64+
-H "Content-Type: application/json" \
65+
-d "{\"key\": \"xr-baseline-0\", \"cid\": \"$CID\"}"
66+
67+
echo "IPNS updated: xr-baseline-0 -> $CID"
68+
69+
- name: Log Deployment
70+
run: |
71+
echo "✅ Live site updated at https://www.real-currents.com"
72+
echo "✅ IPFS snapshot: https://www.real-currents.com/ipfs/${{ steps.ipfs_upload.outputs.cid }}"
73+
echo "✅ IPNS stable link: https://www.real-currents.com/ipns/xr-baseline-0"

docs/IPFS-INTEGRATION.md

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# IPFS Integration - Implementation Summary
2+
3+
This document summarizes the IPFS/IPNS integration implemented per the Real-Currents IPFS Integration plan.
4+
5+
## What Was Implemented
6+
7+
### Phase 2: Gandi Node.js Server (Security Guard + Gateway)
8+
9+
- **`server.js`** – Custom HTTP server that:
10+
- Serves `/ipfs/<cid>/...` – Immutable IPFS gateway
11+
- Serves `/ipns/<name>/...` – Mutable IPNS gateway (with 5-min cache)
12+
- `POST /api/deploy` – CAR upload (authenticated via `x-api-key`)
13+
- `POST /api/ipns/publish` – IPNS publish (authenticated)
14+
- `GET /api/ipfs/current` – Returns current CID for IPNS key (for footer links)
15+
- Falls through to Next.js for all other routes
16+
17+
- **Dependencies added**: `ipfs-http-client`, `mime-types`
18+
19+
- **Scripts updated**:
20+
- `dev` – Uses custom server (with Quarto render)
21+
- `start` – Production server; `start:static` – Original static server
22+
23+
### Phase 3: GitHub Actions
24+
25+
- **`.github/workflows/deploy.yml`** – Runs on push to `main`:
26+
1. Build (Quarto + Next.js)
27+
2. Deploy to Gandi via Git
28+
3. Package `out/` as CAR
29+
4. Upload CAR to `/api/deploy`
30+
5. Publish IPNS `xr-baseline-0` → new CID
31+
32+
### Phase 4: Next.js Configuration
33+
34+
- **`next.config.mjs`** – Added `trailingSlash: true`, `images.unoptimized: true` (static export already configured)
35+
36+
### Phase 6: Content Integration
37+
38+
- **`src/components/IpfsLinks.tsx`** – Client component that fetches current CID and shows:
39+
- Latest Version (IPNS) – `/ipns/xr-baseline-0`
40+
- Immutable Snapshot (IPFS) – `/ipfs/<cid>` when available
41+
42+
- **`src/app/layout.tsx`** – Footer includes `IpfsLinks`, `metadataBase` for canonical URLs
43+
44+
## Next Steps (Manual)
45+
46+
### Phase 1: Worker Node Setup (VPS)
47+
48+
1. Install Kubo (IPFS) on your VPS
49+
2. Create IPNS key: `ipfs key gen xr-baseline-0 --type=ed25519`
50+
3. Install and configure Caddy with Basic Auth
51+
4. Point `ipfs-api.real-currents.com` to the VPS
52+
53+
### Configure Secrets
54+
55+
**GitHub** (Settings → Secrets and variables → Actions):
56+
57+
- `GANDI_GIT_URL` – Gandi Git remote URL
58+
- `DEPLOY_SECRET` – Strong random secret (same as Gandi env)
59+
60+
**Gandi** (or `.env` for local):
61+
62+
- `IPFS_NODE_HOST`, `IPFS_USER`, `IPFS_PASS` – Worker node credentials
63+
- `DEPLOY_SECRET` – Same as GitHub secret
64+
65+
See `.env.example` for the full list.
66+
67+
## URL Structure
68+
69+
| Pattern | Handler | Purpose |
70+
|---------|---------|---------|
71+
| `/xr/baseline-0`, `/`, etc. | Next.js | Live site |
72+
| `/ipfs/<cid>/...` | IPFS Gateway | Immutable snapshot |
73+
| `/ipns/xr-baseline-0/...` | IPNS Gateway | Stable pointer to latest |
74+
75+
## Testing Locally
76+
77+
Without the Worker node, IPFS/IPNS routes return 503. To test:
78+
79+
```bash
80+
# With env vars set (copy .env.example to .env)
81+
npm run dev
82+
# Then: curl http://localhost:3000/ipns/xr-baseline-0
83+
```
84+
85+
With the Worker configured, the IPFS and IPNS gateways will work as expected.

next.config.mjs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
/** @type {import('next').NextConfig} */
22
const nextConfig = {
33
"distDir": "out",
4+
"output": "export",
5+
"trailingSlash": true,
6+
"images": {
7+
"unoptimized": true,
8+
},
49
"eslint": {
510
"ignoreDuringBuilds": true,
611
},
@@ -10,7 +15,7 @@ const nextConfig = {
1015
// "src/components/SvelteMainComponent.tsx",
1116
//
1217
// ],
13-
"output": "export", // <=== enables static exports
18+
// output: 'export' enables static exports (see above)
1419
// "output": "standalone",
1520
"reactStrictMode": true,
1621
"typescript": {

0 commit comments

Comments
 (0)