From a1ee82b946d5880ad198745b7840635281da6424 Mon Sep 17 00:00:00 2001 From: Marketen Date: Wed, 2 Sep 2026 12:35:06 +0200 Subject: [PATCH 1/5] docs: present package as Nexus Privacy Layer --- .github/workflows/main.yml | 6 -- Dockerfile | 9 +-- README.md | 120 +++++++++++++------------------------ dappnode_package.json | 12 ++-- docker-compose.yml | 8 +-- 5 files changed, 48 insertions(+), 107 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 4ec6e06..e89906f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,11 +15,6 @@ jobs: steps: - uses: actions/checkout@v4 - run: npx @dappnode/dappnodesdk build --skip_save - env: - # Read access to the private dappnode-nexus-sdk repository. The - # default GITHUB_TOKEN is scoped to this repository only and cannot - # fetch the proxy source. - NEXUS_SDK_TOKEN: ${{ secrets.NEXUS_SDK_TOKEN }} release: name: Release @@ -31,5 +26,4 @@ jobs: run: npx @dappnode/dappnodesdk publish patch --dappnode_team_preset --timeout 2h env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - NEXUS_SDK_TOKEN: ${{ secrets.NEXUS_SDK_TOKEN }} DEVELOPER_ADDRESS: "0xf35960302a07022aba880dffaec2fdd64d5bf1c1" diff --git a/Dockerfile b/Dockerfile index 7e39ad3..b40757d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -15,13 +15,7 @@ RUN apk add --no-cache ca-certificates git WORKDIR /src RUN git init \ && git remote add origin https://github.com/dappnode/dappnode-nexus-sdk.git -RUN --mount=type=secret,id=github_token \ - if test -s /run/secrets/github_token; then \ - auth_header="$(printf 'x-access-token:%s' "$(cat /run/secrets/github_token)" | base64 | tr -d '\n')"; \ - git -c http.extraHeader="Authorization: Basic ${auth_header}" fetch --depth=1 origin "${UPSTREAM_VERSION}"; \ - else \ - git fetch --depth=1 origin "${UPSTREAM_VERSION}"; \ - fi \ +RUN git fetch --depth=1 origin "${UPSTREAM_VERSION}" \ && git checkout --detach FETCH_HEAD \ && test "$(git rev-parse HEAD)" = "${UPSTREAM_VERSION}" @@ -42,6 +36,7 @@ FROM ${RUNTIME_IMAGE} COPY --from=build /out/nexus-proxy /usr/local/bin/nexus-proxy COPY --from=build /out/nexus-proxy-healthcheck /usr/local/bin/nexus-proxy-healthcheck COPY --from=build --chown=nonroot:nonroot /out/state /var/lib/nexus-proxy +COPY --from=build /src/LICENSE /usr/share/doc/nexus-privacy-layer/LICENSE COPY nexus-gateway-policy.json /etc/nexus/nexus-gateway-policy.json COPY THIRD_PARTY_NOTICES.md /usr/share/doc/nexus-local-proxy/THIRD_PARTY_NOTICES.md diff --git a/README.md b/README.md index 62ec801..e6c054b 100644 --- a/README.md +++ b/README.md @@ -1,114 +1,74 @@ -# Nexus Local Proxy for DAppNode +# Nexus Privacy Layer for DAppNode -This DAppNode package runs one shared Nexus proxy for OpenAI-compatible -applications installed on the same DAppNode. The proxy verifies fresh AWS -Nitro attestation against an independently pinned policy and encrypts chat -request and response bodies with EHBP before they cross Cloudflare. +Private, OpenAI-compatible access to Nexus for applications on your DAppNode. -## Client configuration +This package runs a shared local privacy layer for applications installed on +the same DAppNode. It verifies the Nexus confidential service before accepting +traffic and protects prompt and response bodies on their way to and from +Nexus. -Configure DAppNode applications with: +## Connect an application + +After installing **Nexus Local Proxy**, configure applications on the same +DAppNode with: ```text Base URL: http://nexus-local-proxy.dappnode.private:3301/v1 -API key: the application's normal Nexus API key +API key: your Nexus API key API: OpenAI Chat Completions ``` -The API key remains in the normal `Authorization: Bearer ...` header. The -proxy does not store it and does not automatically retry inference requests. +Create and manage API keys at +[nexus.dappnode.com](https://nexus.dappnode.com). Applications can use +`POST /v1/chat/completions` for regular or streaming responses and +`GET /v1/models` to list the available models. -Applications may declare the package dependency once and then receive proxy -and trust-policy updates independently: +An application can declare the package as a dependency: ```json { "dependencies": { - "nexus-local-proxy.dnp.dappnode.eth": "^0.1.0" + "nexus-local-proxy.dnp.dappnode.eth": "^0.2.0" } } ``` -`POST /v1/chat/completions` and `GET /v1/models` are OpenAI-compatible, so an -application that lists models against its configured base URL works without a -second endpoint. `GET /healthz` reports whether the local proxy is ready. - -The model catalog is the one route here that is **not** confidential. It is -public, unauthenticated, cacheable data with no prompt, completion or -credential in it, so the proxy passes it through over ordinary TLS rather than -over EHBP, and does not forward the caller's `Authorization` header. These -requests are not counted on the verification page, because nothing about them -crossed the attested channel. Add `--model-catalog=false` to the service -command to remove the route. - -## Privacy verification page +## Check your privacy connection -The package serves a page showing whether the Gateway is currently verified and -what was checked: +Open the local verification page: ```text http://nexus-local-proxy.dappnode.private:3301/verification ``` -It reports the verdict in plain language, lists the checks the proxy performed -before it would encrypt anything, shows the enclave measurements and attested -key, and lists recent requests with the attested key each one was encrypted to. -From there the raw COSE_Sign1 attestation document and its signed manifest can -be downloaded and re-checked with an independent AWS Nitro verifier. - -The page shows no prompt or completion content. The ledger behind it records -only verification evidence and per request identifiers, timing and sizes, and -holds them in memory: nothing is written to disk and history starts empty after -a restart. Because it is served on the proxy port, anything on the DAppNode -internal network can read this metadata. Add `--verification-ui=false` to the -service command to remove the page and its API. - -## Security boundary +It shows whether the Nexus service passed verification and which protected +connection handled each recent request. Verification evidence and request +metadata can be kept across package restarts; prompts and responses are never +part of that history. -Callers trust the DAppNode host, this package, and the DAppNode internal -network. The caller-to-proxy hop is ordinary HTTP and is outside EHBP. From -this local proxy to the measured Nexus Gateway process inside AWS Nitro -Enclaves, prompt and completion bodies are encrypted and integrity-protected. +## What is protected -EHBP does not hide the HTTP method, path, headers, body length, frame sizes, -timing, or bearer API key. This package does not extend the claim to a -downstream inference provider. Do not publish container port 3301 to the host -or Internet. +- Prompt and response bodies are encrypted between this package and the + verified Nexus confidential service. +- The package refuses to accept traffic when it cannot verify that service. +- Prompt and response content is not written to verification history or logs. -## Current pin +The DAppNode host and internal network remain trusted. Request metadata, +including headers, sizes, and timing, is outside the body-encryption boundary. +The protection does not extend beyond Nexus to a downstream model provider. +Port `3301` must remain private to the DAppNode network. -This package pins: +## Development -- SDK commit `7ecb47b27122d41f010d33811236328e7ce3af17` (`main`). -- Gateway release `v0.1.57`, source revision `bda15a3549b7a9fbb37004281852079e9013f73b`. -- The PCR values in `nexus-gateway-policy.json`. -- Gateway origin `https://nexus-api-tee.dappnode.com`. - -The trust policy must always describe a Gateway release actually deployed at -that origin. It is fail-closed: if no pinned release matches the running -enclave, the proxy refuses to start. Take measurements from the signed release -record rather than from the live attestation endpoint. - -`releases` accepts several entries, so a Gateway can be rolled out without -installed proxies failing closed in between. Publish a policy listing both the -outgoing and incoming release, let it reach nodes, deploy the Gateway, then -publish a policy listing only the new release. - -## Build +The package builds the privacy-layer source from the exact public commit set in +`UPSTREAM_VERSION`: ```sh -NEXUS_SDK_TOKEN="$(gh auth token)" docker compose build +docker compose build ``` -The image builds the SDK from a full Git commit, verifies the checked-out -revision matches, and runs as an unprivileged user in a minimal runtime image. - -`NEXUS_SDK_TOKEN` must hold a token with read access to the private -`dappnode-nexus-sdk` repository. `docker-compose.yml` passes it to the build as -a BuildKit secret, so it never reaches an image layer. In CI it comes from the -`NEXUS_SDK_TOKEN` repository secret; the default `GITHUB_TOKEN` cannot be used -because it is scoped to this repository only. +## License -Remove the secret, the `secrets:` blocks in `docker-compose.yml`, and this -section once the SDK repository is public: the Dockerfile already falls back to -an unauthenticated fetch when no secret is supplied. +This package is licensed under the [MIT License](LICENSE). The bundled Nexus +Privacy Layer is licensed under Apache-2.0; dependency notices are in +[THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md). diff --git a/dappnode_package.json b/dappnode_package.json index c9bd7e6..ff8ac85 100644 --- a/dappnode_package.json +++ b/dappnode_package.json @@ -1,11 +1,11 @@ { "name": "nexus-local-proxy.dnp.dappnode.eth", "version": "0.2.0", - "upstreamVersion": "1fcfe23b2c2b141b2f7937ebb808646b37ed7439", + "upstreamVersion": "d02247176da7f848cc9a3ff4350a5307e007f58b", "upstreamRepo": "dappnode/dappnode-nexus-sdk", "upstreamArg": "UPSTREAM_VERSION", - "shortDescription": "Attested private AI proxy for DAppNode applications", - "description": "A shared OpenAI-compatible Nexus proxy for applications on the same DAppNode. It verifies the measured Nexus Gateway inside AWS Nitro Enclaves and encrypts prompt and completion bodies with EHBP before they cross Cloudflare. A local verification page at /verification shows the attestation evidence behind that protection and lets you export it for independent checking. GET /v1/models passes the Gateway public model catalog through so ordinary OpenAI clients can list models against the proxy.", + "shortDescription": "Privacy layer for Nexus AI on DAppNode", + "description": "Gives DAppNode applications an OpenAI-compatible local endpoint that verifies the Nexus confidential service and protects prompt and response bodies. Includes a local privacy verification page and model listing.", "type": "dncore", "backup": [ { @@ -28,12 +28,10 @@ "keywords": [ "ai", "confidential", - "ehbp", - "nitro-enclaves", - "attestation", "openai", + "privacy", "proxy", - "tee" + "verification" ], "author": "DAppNode Association (https://github.com/dappnode)", "repository": { diff --git a/docker-compose.yml b/docker-compose.yml index 97041dd..7c2e2fb 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,9 +6,7 @@ services: context: . dockerfile: Dockerfile args: - UPSTREAM_VERSION: 1fcfe23b2c2b141b2f7937ebb808646b37ed7439 - secrets: - - github_token + UPSTREAM_VERSION: d02247176da7f848cc9a3ff4350a5307e007f58b image: nexus-local-proxy.dnp.dappnode.eth:0.2.0 container_name: DAppNodePackage-nexus-local-proxy.dnp.dappnode.eth restart: unless-stopped @@ -48,7 +46,3 @@ services: volumes: verification_state: {} - -secrets: - github_token: - environment: NEXUS_SDK_TOKEN From b6509789aee0e28155933cf503e75bd3e9bffe8c Mon Sep 17 00:00:00 2001 From: Marketen Date: Wed, 2 Sep 2026 12:40:07 +0200 Subject: [PATCH 2/5] chore: update SDK and Go pins --- Dockerfile | 2 +- dappnode_package.json | 2 +- docker-compose.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Dockerfile b/Dockerfile index b40757d..e223047 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ # syntax=docker/dockerfile:1.7 -ARG GO_IMAGE=golang:1.26.4-alpine@sha256:3ad57304ad93bbec8548a0437ad9e06a455660655d9af011d58b993f6f615648 +ARG GO_IMAGE=golang:1.26.8-alpine@sha256:34efdd6036c92e155c8b0162a5da7626586b612ea636590035602c970eece564 ARG RUNTIME_IMAGE=gcr.io/distroless/static-debian12:nonroot@sha256:1b7b9f0f0e0a1d2155f531db587cc48ec26aaf97ab64364225f5bf18a054e66a FROM ${GO_IMAGE} AS build diff --git a/dappnode_package.json b/dappnode_package.json index ff8ac85..7229557 100644 --- a/dappnode_package.json +++ b/dappnode_package.json @@ -1,7 +1,7 @@ { "name": "nexus-local-proxy.dnp.dappnode.eth", "version": "0.2.0", - "upstreamVersion": "d02247176da7f848cc9a3ff4350a5307e007f58b", + "upstreamVersion": "1525f9699c03ba8eb987182f9dbe4f2af9f48cd9", "upstreamRepo": "dappnode/dappnode-nexus-sdk", "upstreamArg": "UPSTREAM_VERSION", "shortDescription": "Privacy layer for Nexus AI on DAppNode", diff --git a/docker-compose.yml b/docker-compose.yml index 7c2e2fb..59f6c17 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: context: . dockerfile: Dockerfile args: - UPSTREAM_VERSION: d02247176da7f848cc9a3ff4350a5307e007f58b + UPSTREAM_VERSION: 1525f9699c03ba8eb987182f9dbe4f2af9f48cd9 image: nexus-local-proxy.dnp.dappnode.eth:0.2.0 container_name: DAppNodePackage-nexus-local-proxy.dnp.dappnode.eth restart: unless-stopped From 42f6eeefdd497767a4dfe54870faa13d9958e539 Mon Sep 17 00:00:00 2001 From: Marketen Date: Wed, 2 Sep 2026 12:42:49 +0200 Subject: [PATCH 3/5] ci: update checkout action --- .github/workflows/main.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index e89906f..f0fd39f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest name: Build test steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - run: npx @dappnode/dappnodesdk build --skip_save release: @@ -21,7 +21,7 @@ jobs: runs-on: ubuntu-latest if: github.event_name == 'push' || github.event_name == 'repository_dispatch' steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Publish run: npx @dappnode/dappnodesdk publish patch --dappnode_team_preset --timeout 2h env: From 1b7a5d6ec60753f241de54ef306cb20e4f2962dc Mon Sep 17 00:00:00 2001 From: Marketen Date: Wed, 2 Sep 2026 12:59:23 +0200 Subject: [PATCH 4/5] docs: keep Nexus SDK identity --- Dockerfile | 2 +- README.md | 16 ++++++++-------- dappnode_package.json | 6 +++--- docker-compose.yml | 2 +- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Dockerfile b/Dockerfile index e223047..cd63364 100644 --- a/Dockerfile +++ b/Dockerfile @@ -36,7 +36,7 @@ FROM ${RUNTIME_IMAGE} COPY --from=build /out/nexus-proxy /usr/local/bin/nexus-proxy COPY --from=build /out/nexus-proxy-healthcheck /usr/local/bin/nexus-proxy-healthcheck COPY --from=build --chown=nonroot:nonroot /out/state /var/lib/nexus-proxy -COPY --from=build /src/LICENSE /usr/share/doc/nexus-privacy-layer/LICENSE +COPY --from=build /src/LICENSE /usr/share/doc/nexus-sdk/LICENSE COPY nexus-gateway-policy.json /etc/nexus/nexus-gateway-policy.json COPY THIRD_PARTY_NOTICES.md /usr/share/doc/nexus-local-proxy/THIRD_PARTY_NOTICES.md diff --git a/README.md b/README.md index e6c054b..5972493 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,11 @@ -# Nexus Privacy Layer for DAppNode +# Nexus Local Proxy for DAppNode Private, OpenAI-compatible access to Nexus for applications on your DAppNode. -This package runs a shared local privacy layer for applications installed on -the same DAppNode. It verifies the Nexus confidential service before accepting -traffic and protects prompt and response bodies on their way to and from -Nexus. +This package runs a shared local instance of the DAppNode Nexus SDK for +applications installed on the same DAppNode. It verifies the Nexus Gateway +before accepting traffic and protects prompt and response bodies on their way +to and from Nexus. ## Connect an application @@ -60,7 +60,7 @@ Port `3301` must remain private to the DAppNode network. ## Development -The package builds the privacy-layer source from the exact public commit set in +The package builds the Nexus SDK from the exact public commit set in `UPSTREAM_VERSION`: ```sh @@ -69,6 +69,6 @@ docker compose build ## License -This package is licensed under the [MIT License](LICENSE). The bundled Nexus -Privacy Layer is licensed under Apache-2.0; dependency notices are in +This package is licensed under the [MIT License](LICENSE). The bundled DAppNode +Nexus SDK is licensed under Apache-2.0; dependency notices are in [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md). diff --git a/dappnode_package.json b/dappnode_package.json index 7229557..637931d 100644 --- a/dappnode_package.json +++ b/dappnode_package.json @@ -1,11 +1,11 @@ { "name": "nexus-local-proxy.dnp.dappnode.eth", "version": "0.2.0", - "upstreamVersion": "1525f9699c03ba8eb987182f9dbe4f2af9f48cd9", + "upstreamVersion": "ea54fd7e6b57bf0d4920280db9d3014eff50fba9", "upstreamRepo": "dappnode/dappnode-nexus-sdk", "upstreamArg": "UPSTREAM_VERSION", - "shortDescription": "Privacy layer for Nexus AI on DAppNode", - "description": "Gives DAppNode applications an OpenAI-compatible local endpoint that verifies the Nexus confidential service and protects prompt and response bodies. Includes a local privacy verification page and model listing.", + "shortDescription": "Private Nexus access for DAppNode applications", + "description": "Runs the DAppNode Nexus SDK as a shared OpenAI-compatible local endpoint that verifies the Nexus Gateway and protects prompt and response bodies. Includes a local verification page and model listing.", "type": "dncore", "backup": [ { diff --git a/docker-compose.yml b/docker-compose.yml index 59f6c17..59b70b0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: context: . dockerfile: Dockerfile args: - UPSTREAM_VERSION: 1525f9699c03ba8eb987182f9dbe4f2af9f48cd9 + UPSTREAM_VERSION: ea54fd7e6b57bf0d4920280db9d3014eff50fba9 image: nexus-local-proxy.dnp.dappnode.eth:0.2.0 container_name: DAppNodePackage-nexus-local-proxy.dnp.dappnode.eth restart: unless-stopped From c8e48fe2d2fbc9bf21adefe8d3efbd576f7b0271 Mon Sep 17 00:00:00 2001 From: Marketen Date: Wed, 2 Sep 2026 17:04:06 +0200 Subject: [PATCH 5/5] chore: pin embeddable SDK revision --- dappnode_package.json | 2 +- docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dappnode_package.json b/dappnode_package.json index 637931d..1a06d60 100644 --- a/dappnode_package.json +++ b/dappnode_package.json @@ -1,7 +1,7 @@ { "name": "nexus-local-proxy.dnp.dappnode.eth", "version": "0.2.0", - "upstreamVersion": "ea54fd7e6b57bf0d4920280db9d3014eff50fba9", + "upstreamVersion": "d49c902f480ae0db34e2c0621b0ef13384e992f0", "upstreamRepo": "dappnode/dappnode-nexus-sdk", "upstreamArg": "UPSTREAM_VERSION", "shortDescription": "Private Nexus access for DAppNode applications", diff --git a/docker-compose.yml b/docker-compose.yml index 59b70b0..7dd8614 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: context: . dockerfile: Dockerfile args: - UPSTREAM_VERSION: ea54fd7e6b57bf0d4920280db9d3014eff50fba9 + UPSTREAM_VERSION: d49c902f480ae0db34e2c0621b0ef13384e992f0 image: nexus-local-proxy.dnp.dappnode.eth:0.2.0 container_name: DAppNodePackage-nexus-local-proxy.dnp.dappnode.eth restart: unless-stopped