Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ name: Unit Tests
on:
pull_request:
paths:
- 'src/**'
- '.mocharc.js'
- 'packages/*/src/**'
- 'packages/*/package.json'
- 'packages/*/tsconfig.json'
- 'packages/*/.mocharc.js'
- 'package.json'
- 'tsconfig.json'

jobs:
run_tests:
Expand All @@ -15,10 +16,10 @@ jobs:
steps:
- name: Checkout the repo
uses: actions/checkout@v4
- name: Install dependencies
id: install
- name: Build all dependencies
id: build
run: |
npm install
./bin/build.sh
- name: Run Tests
id: test
run: |
Expand Down
21 changes: 10 additions & 11 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,16 @@ WORKDIR /build

COPY . .

WORKDIR /build

RUN apt-get update && \
apt-get install -y \
--no-install-recommends \
python3 \
build-essential \
ca-certificates && \
python3 \
build-essential \
ca-certificates && \
useradd -u 10005 dwebapi && \
tail -n 1 /etc/passwd >/etc/passwd.scratch && \
npm install && \
npm run build
tail -n 1 /etc/passwd >/etc/passwd.scratch

RUN ./bin/build.sh

FROM gcr.io/distroless/nodejs20-debian12 as runtime

Expand All @@ -27,14 +25,15 @@ WORKDIR /app
# Copy node_modules until working fix with npm run build.
COPY --from=build --chown=10005:10005 /build/node_modules ./node_modules
COPY --from=build --chown=10005:10005 /build/package.json .
COPY --from=build --chown=10005:10005 /build/tsconfig.json .
COPY --from=build --chown=10005:10005 /build/dist ./dist
COPY --from=build --chown=10005:10005 /build/packages ./packages
COPY --from=build /etc/ssl /etc/ssl
COPY --from=build /etc/passwd.scratch /etc/passwd

USER dwebapi

EXPOSE 8080 9090 11000

# Node options to use openssl CA certificates
ENV NODE_OPTIONS="--import=extensionless/register --use-openssl-ca"

CMD ["dist/index.js"]
CMD ["packages/dweb-api-server/dist/index.js"]
60 changes: 43 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
# [eth.limo](https://eth.limo) dWeb Gateway API
# ENS dWeb Gateway API

Backend service API for use with reverse proxies to deploy an HTTP [ENS](https://ens.domains) or [GNS](https://genomedomains.com/) gateway capable of resolving [IPFS](https://docs.ipfs.tech/), [IPNS](https://docs.ipfs.tech/how-to/publish-ipns/), [Arweave](https://www.arweave.org/), [Arweave Naming Service (ARNS)](https://docs.ar.io/arns/#overview), and [Swarm](https://www.ethswarm.org/) content.
Backend service API for use with reverse proxies to deploy an HTTP [ENS](https://ens.domains)/[GNS](https://genomedomains.com/) gateway capable of resolving [IPFS](https://docs.ipfs.tech/), [IPNS](https://docs.ipfs.tech/how-to/publish-ipns/), [Arweave](https://www.arweave.org/), [Arweave Name System (ArNS)](https://docs.ar.io/arns/#overview), and [Swarm](https://www.ethswarm.org/) content.

Upstream proxies can forward ENS and GNS hostnames for resolution and properly route them to the appropriate storage gateway path and destination via the following response headers:
Upstream proxies can forward ENS and GNS hostnames for resolution and properly route them to the appropriate storage gateway path and destination via the following response headers (IPFS example below):

IPFS example:
```
X-Content-Location: ${cid}.ipfs.dweb.link
X-Content-Path: /
Expand All @@ -15,17 +14,17 @@ __Gateway request flow__

![alt text](./images/flow.jpg "Example resolution and request data flow")

### Configuration
## Configuration

| Environment Variable | Default | Purpose |
| Environment Variable | Default | Description |
| ------------- |:-------------:| -----:|
| `LISTEN_PORT` | `8888` | Proxy API listener port. |
| `IPFS_SUBDOMAIN_SUPPORT` | `"false"` | Return IPFS gateway destination in subdomain format, i.e. `${cid\|peerId}.${ipfs\|ipns}.dweb.link`. Otherwise results are returned as `dweb.link/ipfs/${cid}`. |
| `IPFS_SUBDOMAIN_SUPPORT` | `"false"` | Return IPFS gateway destination in subdomain format, i.e. `${cid\|peerId}.${ipfs\|ipns}.dweb.link`. Otherwise results are returned as `dweb.link/ipfs/${cid}`. Note that dweb.link is just an example and not a default value in this context. Please see `IPFS_TARGET` for more information.|
| `IPFS_AUTH_KEY` | `null` | Basic authentication for `IPFS_KUBO_API_URL`. |
| `IPFS_KUBO_API_URL` | `undefined` | URL to Kubo `/api/v0/name/resolve` service. This setting performs IPNS name resolution and PeerId conversion to CIDv1 identifiers during the contentHash lookup process. Note, this does not enable or disable IPNS support (as this is performed by the IPFS backend) but rather attempts to use resolved CID values as cache keys as opposed to peerIds. Please read the official IPFS [documentation](https://docs.ipfs.tech/reference/kubo/rpc/#api-v0-name-resolve) for more information. |
| `ARWEAVE_TARGET` | `"https://arweave.net"` | Arweave gateway FQDN. |
| `SWARM_TARGET` | `"https://api.gateway.ethswarm.org"` | Swarm gateway FQDN. |
| `IPFS_TARGET` | `http://127.0.0.1:8080` | FQDN of IPFS gateway backend to use for requests. |
| `IPFS_TARGET` | `http://localhost:8080` | FQDN of IPFS gateway backend to use for requests. |
| `REDIS_URL` | `"redis://127.0.0.1:6379"` | Redis server endpoint. |
| `CACHE_TTL` | `"300"` | TTL to persist resolved records |
| `ASK_ENABLED` | `"false"` | Whether to spawn a special listener for responding to
Expand All @@ -42,22 +41,31 @@ __Gateway request flow__
| `PURGE_CACHE_ON_START` | `"false"` | Indicates whether to purge the entire Redis cache upon server startup. |
| `PURGE_CACHE_COUNT` | `"20000"` | Number of keys to purge if `PURGE_CACHE_ON_START` is enabled. |
| `PURGE_CACHE_PATTERN` | `"*.${DOMAIN_TLD_HOSTNAME}"` | Key pattern to purge if `PURGE_CACHE_ON_START` is enabled. |
| `SW_BUNDLE_PUBLIC_URL` | `""` | Optional value if using service workers instead of the API. Set this to the parent wildcard domain you will be serving traffic from, i.e. setting this value to `eth.example.com` would support `ens.eth.example.com`, etc. |
| `SERVICE_WORKER_TRUSTLESS` | `"false"` | Optional value if using service workers instead of the API. Set this to `"true"` to enable [trustless IPFS gateway mode](https://specs.ipfs.tech/http-gateways/trustless-gateway/). You must also set `IPFS_TARGET` to the hostname of a gateway running in trustless mode. |

### Local Example
## Quickstart

1. Start Redis
1. Start Redis (using any method)

```
podman run -p 127.0.0.1:6379:6379 docker.io/library/redis
podman pull docker.io/library/redis
podman run --net=host docker.io/library/redis
```

2. Configure the necessary environment listed above
(Note you can also use `docker` instead of `podman`)

3. Start dWeb Proxy API
2. Configure the necessary environment variables listed above

3. Start the ENS dWeb Proxy API

```
npm install
npm run dev
./bin/build.sh

# (optional) run test suites
npm run test

./bin/runDev.sh
```

4. Make a request
Expand All @@ -76,7 +84,21 @@ Keep-Alive: timeout=5
Transfer-Encoding: chunked
```

__Use with Caddy server as a local gateway__
### Container example

```
podman pull docker.io/library/redis
podman run --net=host docker.io/library/redis

buildah bud -t dweb-api-proxy .

# Make sure to pass the necessary environment variables with "-e" flags
podman run --rm -it --net=host -e "ETH_RPC_ENDPOINT=${ETH_RPC_ENDPOINT}" dweb-api-proxy
```

(Note you can also use `docker` instead of `buildah`)

### Running a local gateway with Caddy server

Start `dweb-proxy-api` with the correct environment variables and install [Caddy server](https://github.com/caddyserver/caddy).

Expand Down Expand Up @@ -150,4 +172,8 @@ For example, using `/etc/hosts`:
::1 localhost ens.eth
```

Save the file, launch Caddy (`caddy run`) and then open a browser and navigate to `https://ens.eth:8443`.
Save the file, launch Caddy (`caddy run`) and then open a browser and navigate to `https://ens.eth:8443`.

## Service Workers

All static assets for supporting service worker resolution are located in `packages/dweb-api-serviceworker/dist`. We recommend using an HTTP server such as Caddy or Nginx to serve this content (any CDN will work as well). The `SW_BUNDLE_PUBLIC_URL` environment variable should be set to the domain you will be serving traffic from. For example, if you are serving traffic from `*.eth.example.com`, set `SW_BUNDLE_PUBLIC_URL` to `eth.example.com` in order to resolve `ens.eth.example.com`.
11 changes: 11 additions & 0 deletions bin/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#!/usr/bin/env sh
cd bin || exit 1
packages=$(node ./list_packages.js)
cd ..
npm i
echo "${PWD}"
for package in ${packages}; do
echo "Building ${package}"
npm i -w "${package}"
npm run build -w "${package}"
done
6 changes: 6 additions & 0 deletions bin/list_packages.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import pkg from "../package.json" with {type: "json"}


pkg.workspaces.forEach((workspace) => {
console.log(workspace)
});
10 changes: 10 additions & 0 deletions bin/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "bin",
"version": "1.0.0",
"description": "this only exists for json imports",
"type": "module",
"author": "Your Name",
"license": "MIT",
"dependencies": {
}
}
9 changes: 9 additions & 0 deletions bin/runDev.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/env bash

IPFS_SUBDOMAIN_SUPPORT=true
ASK_ENABLED=true
DNSQUERY_ENABLED=true

npm i
npm i --workspaces
npm run dev -w packages/dweb-api-server
123 changes: 49 additions & 74 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,76 +1,51 @@
{
"name": "dweb-api",
"version": "1.0.0",
"description": "Proxy middleware for ENS and other on chain naming services",
"main": "index.js",
"scripts": {
"dev": "nodemon -e js,ts --watch src --exec \"npx tsx src/index.ts\"",
"fmt": "prettier src -w",
"test": "mocha --exit",
"test:coverage": "nyc mocha --exit",
"build": "tsc",
"run-build": "node --import=extensionless/register dist/index.js"
},
"author": "eth.limo team",
"license": "MIT",
"dependencies": {
"@ensdomains/content-hash": "^3.0.0-beta.5",
"@libp2p/peer-id": "^4.0.9",
"@types/dns-packet": "^5.2.4",
"@types/superagent": "^8.1.1",
"@web3-name-sdk/core": "^0.1.18",
"axios": "^1.4.0",
"cors": "^2.8.5",
"dns-packet": "^5.6.0",
"ethers": "^6.11.1",
"express": "^4.18.2",
"extensionless": "^1.9.6",
"inversify": "^6.0.1",
"ioredis": "^5.3.2",
"multiformats": "^13.0.0",
"node-cache": "^5.1.2",
"nyc": "^15.1.0",
"pkg": "^5.8.1",
"punycode": "^2.3.0",
"re2": "^1.19.0",
"redis-lock": "^1.0.0",
"redlock": "^5.0.0-beta.2",
"reflect-metadata": "^0.2.1",
"rfc4648": "^1.5.3",
"superagent": "^9.0.2",
"typescript": "^5.1.3",
"typeserializer": "^0.2.5",
"url-regex-safe": "^4.0.0",
"viem": "^1.21.4",
"warp-contracts": "^1.4.25",
"winston": "^3.9.0",
"zod": "^3.22.4"
},
"devDependencies": {
"@types/chai": "^4.3.5",
"@types/cors": "^2.8.13",
"@types/express": "^4.17.17",
"@types/http-proxy": "^1.17.11",
"@types/mocha": "^10.0.1",
"@types/node": "^20.3.1",
"@types/punycode": "^2.1.3",
"@types/sinon": "^17.0.2",
"@types/sinon-chai": "^3.2.12",
"@types/url-regex-safe": "^1.0.0",
"chai": "^4.3.7",
"ioredis-mock": "^8.9.0",
"mocha": "^10.2.0",
"node-mocks-http": "^1.14.1",
"nodemon": "^3.0.1",
"prettier": "^3.1.0",
"sinon": "^17.0.1",
"sinon-chai": "^3.7.0",
"ts-mocha": "^10.0.0",
"ts-node": "^10.9.1",
"tsx": "^4.6.1"
},
"overrides": {
"uint8arrays": "5.0.2"
},
"type": "module"
"name": "dweb-api",
"version": "1.0.0",
"description": "Proxy middleware for ENS and other on chain naming services",
"author": "eth.limo team",
"license": "MIT",
"workspaces": [
"packages/dweb-api-types",
"packages/dweb-api-logger",
"packages/dweb-api-cache",
"packages/dweb-api-resolver",
"packages/dweb-api-server",
"packages/dweb-api-serviceworker"
],
"scripts": {
"test": "npm run test --workspace packages/dweb-api-server",
"clean": "rm -rf dist; rm -rf packages/*/dist; rm -rf node_modules; rm -rf packages/*/node_modules",
"build-all": "sh bin/build.sh"
},
"dependencies": {
"node-polyfill-webpack-plugin": "^4.0.0",
"pkg": "^5.8.1",
"re2": "^1.19.0",
"rfc4648": "^1.5.3",
"winston": "^3.14.0",
"zod": "^3.22.4"
},
"devDependencies": {
"@types/chai": "^4.3.5",
"@types/mocha": "^10.0.1",
"@types/node": "^20.3.1",
"@types/sinon": "^17.0.2",
"@types/sinon-chai": "^3.2.12",
"chai": "^4.3.7",
"dweb-api-types": "^1.0.0",
"mocha": "^10.2.0",
"nodemon": "^3.0.1",
"nyc": "^17.0.0",
"prettier": "^3.1.0",
"sinon": "^18.0.1",
"sinon-chai": "^3.7.0",
"ts-mocha": "^10.0.0",
"ts-node": "^10.9.1",
"tsx": "^4.6.1",
"typescript": "^5.1.3"
},
"overrides": {
"uint8arrays": "5.1.0",
"axios": "1.8.4"
}
}
18 changes: 18 additions & 0 deletions packages/dweb-api-cache/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"name": "dweb-api-cache",
"version": "1.0.0",
"description": "Caching strategies for dweb-api-cache",
"scripts": {
"fmt": "prettier src -w",
"test": "mocha --exit",
"test:coverage": "nyc mocha --exit",
"build": "tsc"
},
"author": "eth.limo team",
"license": "MIT",
"dependencies": {
"dweb-api-serviceworker": "file:../dweb-api-serviceworker",
"dweb-api-types": "file:../dweb-api-types"
},
"type": "module"
}
Loading