resolver: dockerize

This commit is contained in:
sh
2026-06-08 15:09:25 +00:00
parent 40e6510500
commit c95d4bcdc1
5 changed files with 149 additions and 8 deletions
+48
View File
@@ -0,0 +1,48 @@
# syntax=docker/dockerfile:1.7
# ---------- builder ----------
# Use the official uv image (Astral) on top of a slim Python base.
# uv resolves and installs the lockfile-free pyproject.toml in seconds and
# produces a portable .venv we can copy into the runtime stage.
FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim AS builder
ENV UV_LINK_MODE=copy \
UV_COMPILE_BYTECODE=1 \
UV_PYTHON_DOWNLOADS=never \
UV_NO_PROGRESS=1
WORKDIR /app
# Install deps first (separate layer) — script edits won't bust this cache.
COPY pyproject.toml ./
RUN --mount=type=cache,target=/root/.cache/uv \
uv sync --no-dev --no-install-project
# Script is added after the dep layer for cache friendliness.
COPY snrc-resolve.py ./
# ---------- runtime ----------
# Slim runtime — only the venv + script. No uv, no apt.
FROM python:3.13-slim AS runtime
ENV PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1 \
PATH="/app/.venv/bin:$PATH"
# Non-root user (matches resolver privacy posture: it has no need for root).
RUN groupadd --system --gid 10001 snrc && \
useradd --system --uid 10001 --gid snrc --no-create-home --shell /usr/sbin/nologin snrc
WORKDIR /app
COPY --from=builder --chown=snrc:snrc /app /app
USER snrc:snrc
EXPOSE 8000
# Liveness check hits the script's own /health route. ThreadingHTTPServer is
# fast enough that 3s is generous for a localhost probe; restart if it stops
# responding entirely.
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD ["python", "-c", "import urllib.request, sys; sys.exit(0 if urllib.request.urlopen('http://127.0.0.1:8000/health', timeout=3).status == 200 else 1)"]
ENTRYPOINT ["python", "snrc-resolve.py"]
+45 -2
View File
@@ -52,10 +52,21 @@ Registry (SNRC) over a small JSON HTTP API. It talks to the same local
Reth + Nimbus stack described above (set `NETWORK=mainnet` in `.env`),
reading the SNRC contracts directly on Ethereum mainnet.
Install the only runtime dependency (same as `ens-lookup.py`):
Dependencies are declared inline (PEP 723) at the top of `snrc-resolve.py`
and in a sibling `pyproject.toml`. The simplest local run uses
[`uv`](https://docs.astral.sh/uv/):
```sh
pip install --break-system-packages 'eth-hash[pycryptodome]'
uv run scripts/resolver/snrc-resolve.py
```
`uv` resolves and caches `eth-hash[pycryptodome]` on first run. No
virtualenv juggling, no `--break-system-packages`. If you'd rather
manage Python deps yourself:
```sh
pip install 'eth-hash[pycryptodome]>=0.7'
python scripts/resolver/snrc-resolve.py
```
### Deployed registries
@@ -93,6 +104,38 @@ snrc-resolve listening on 0.0.0.0:8000
Override the listen port or bind address with `SNRC_PORT` / `SNRC_BIND`.
### Running in Docker
The compose file ships a `resolver` service alongside reth and nimbus.
`docker compose up -d` builds the image from `Dockerfile` (multi-stage,
non-root, `uv`-based) and exposes the API on `127.0.0.1:8000`:
```sh
docker compose up -d resolver
docker compose logs -f resolver
curl -s http://127.0.0.1:8000/health
```
The container points `SNRC_RPC` at `http://reth:8545` (the compose-internal
DNS name) so the resolver and reth share the bridge network without
exposing reth's RPC to the host beyond loopback.
To change the host-side port, edit the LEFT side of the port mapping in
`docker-compose.yml`:
```yaml
resolver:
ports:
- "127.0.0.1:8000:8000" # host:container
```
The registry address defaults to mainnet `.testing` — to override (Holesky,
a private deployment, or future `.simplex`), uncomment and set the values
in `docker-compose.yml` under the resolver service's `environment:` block.
The image declares a `HEALTHCHECK` against `/health`; `docker compose ps`
will mark the service `(healthy)` once reth is queryable.
### Resolving a name
`foobar.testing` is registered on mainnet with every text and
+28
View File
@@ -127,6 +127,34 @@ services:
--nat=${NAT:-any}
restart: unless-stopped
# SNRC REST resolver. Talks to reth on the compose-internal network,
# exposes /resolve and /health on 127.0.0.1:8000 by default. The
# smp-server points its [NAMES] resolver_endpoint at this URL.
# To change the host port, edit the LEFT side of the port mapping below.
resolver:
build:
context: .
dockerfile: Dockerfile
depends_on:
# reth's `service_started` is sufficient — the resolver tolerates
# eth_call failures gracefully (returns 502 with the error body), so
# starting before reth has finished snapshot replay just yields a few
# 502s until the chain is queryable. The upstream reth image doesn't
# ship a HEALTHCHECK, so we can't gate on healthy.
reth:
condition: service_started
environment:
SNRC_RPC: http://reth:8545
SNRC_BIND: 0.0.0.0
# Registry addresses cascade through the script's own defaults
# (mainnet `.testing`; `.simplex` unconfigured). Set explicitly here
# only if you're deploying against a different network or contract.
# SNRC_REGISTRY_TESTING: 0x...
# SNRC_REGISTRY_SIMPLEX: 0x...
ports:
- "127.0.0.1:8000:8000"
restart: unless-stopped
volumes:
reth-data:
nimbus-data:
+13
View File
@@ -0,0 +1,13 @@
[project]
name = "snrc-resolve"
version = "0.1.0"
description = "SimpleX Namespace (SNRC) resolver — REST API over ENS-shaped Ethereum registries"
readme = "README.md"
requires-python = ">=3.11"
license = "AGPL-3.0-only"
dependencies = [
"eth-hash[pycryptodome]>=0.7",
]
[tool.uv]
package = false
+15 -6
View File
@@ -1,4 +1,10 @@
#!/usr/bin/env python3
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "eth-hash[pycryptodome]>=0.7",
# ]
# ///
"""SimpleX Namespace (SNRC) resolver — REST API.
Resolves names like `alice.testing` / `bob.simplex` against the SNRC
@@ -33,8 +39,9 @@ Environment:
Each TLD is a separate SNRC deployment with its own ENSRegistry; the
resolver dispatches by the queried name's rightmost label.
Same dependency surface as ens-lookup.py:
pip install --break-system-packages 'eth-hash[pycryptodome]'
Dependencies are declared inline (PEP 723) at the top of this file. Run with:
uv run snrc-resolve.py # uv resolves & caches deps; one-line setup
python snrc-resolve.py # if eth-hash[pycryptodome] is already installed
Addresses are returned in each chain's canonical presentation:
eth EIP-55 mixed-case checksummed hex (e.g. 0xEa65A01572)
@@ -62,11 +69,13 @@ PORT = int(os.environ.get("SNRC_PORT", "8000"))
# Each TLD is its own SNRC deployment with its own ENSRegistry. Dispatch
# happens on the rightmost label of the queried name. Empty / unset means
# "not deployed" — requests for that TLD return 400 with a clear error.
# `... or "..."` makes the script's defaults the single source of truth:
# unset AND empty-string both fall through to the literal. docker-compose
# can therefore pass `SNRC_REGISTRY_TESTING=${SNRC_REGISTRY_TESTING:-}`
# without duplicating the registry address.
REGISTRIES = {
"testing": os.environ.get(
"SNRC_REGISTRY_TESTING",
"0x03f438da0bd44da3c6c1d0392f8ba183b8b3a7a6", # mainnet .testing
),
"testing": os.environ.get("SNRC_REGISTRY_TESTING", "")
or "0x03f438da0bd44da3c6c1d0392f8ba183b8b3a7a6", # mainnet .testing
"simplex": os.environ.get("SNRC_REGISTRY_SIMPLEX", ""), # not deployed yet
}