From 8a8233c74852bc257fc6eb10cb70f7641e8faf3d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 2 Aug 2026 11:05:41 +0000 Subject: [PATCH 1/7] Add Docker image with OpenCL runtime for GPU cloud providers profanity2 needs the vendor ICD registered inside the container: the NVIDIA container runtime mounts libnvidia-opencl.so.1 but does not write /etc/OpenCL/vendors/nvidia.icd, so the image ships it. The entrypoint takes profanity2 options as container arguments, which maps onto the docker ENTRYPOINT launch mode of GPU rental platforms, and falls back to PROFANITY_ARGS/PUBLIC_KEY for launch modes that only expose environment variables. Co-authored-by: Gleb Alekseev --- .dockerignore | 17 ++++++ Dockerfile | 55 ++++++++++++++++++ docker/entrypoint.sh | 133 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100755 docker/entrypoint.sh diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..912e59d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,17 @@ +.git +.github +.gitignore +.dockerignore +Dockerfile +docs +img +tests + +# Makefile artifacts +*.o +*.so +*.x64 +*.exe +cache-opencl.* +bin +__pycache__/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..f603894 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,55 @@ +# syntax=docker/dockerfile:1 + +# --------------------------------------------------------------------------- +# Build stage +# --------------------------------------------------------------------------- +FROM ubuntu:24.04 AS build + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + opencl-headers \ + ocl-icd-opencl-dev \ + && rm -rf /var/lib/apt/lists/* + +WORKDIR /src +COPY Makefile ./ +COPY *.cpp *.hpp *.cl ./ +RUN make -j"$(nproc)" + +# --------------------------------------------------------------------------- +# Runtime stage +# --------------------------------------------------------------------------- +FROM ubuntu:24.04 + +LABEL org.opencontainers.image.title="profanity2" \ + org.opencontainers.image.description="GPU vanity address generator for Ethereum (OpenCL)" \ + org.opencontainers.image.source="https://github.com/1inch/profanity2" \ + org.opencontainers.image.licenses="MIT" + +# ocl-icd-libopencl1 is the ICD loader the binary links against, clinfo is kept +# for diagnosing "no devices found" on rented machines. +RUN apt-get update && apt-get install -y --no-install-recommends \ + ocl-icd-libopencl1 \ + clinfo \ + && rm -rf /var/lib/apt/lists/* + +# The NVIDIA container runtime mounts libnvidia-opencl.so.1 into the container +# but does not register it with the ICD loader, so the vendor file has to be +# part of the image: +# https://github.com/NVIDIA/nvidia-container-toolkit/issues/682 +RUN mkdir -p /etc/OpenCL/vendors \ + && echo "libnvidia-opencl.so.1" > /etc/OpenCL/vendors/nvidia.icd + +# compute enables the OpenCL driver libraries, utility enables nvidia-smi. +ENV NVIDIA_VISIBLE_DEVICES=all \ + NVIDIA_DRIVER_CAPABILITIES=compute,utility + +# profanity2 loads keccak.cl/profanity.cl and stores its compiled kernel cache +# relative to the working directory, so the binary has to run from here. +WORKDIR /opt/profanity2 +COPY --from=build /src/profanity2.x64 /src/keccak.cl /src/profanity.cl ./ +COPY LICENSE ./ +COPY docker/entrypoint.sh /usr/local/bin/profanity2-entrypoint +RUN chmod +x /usr/local/bin/profanity2-entrypoint && mkdir -p /workspace + +ENTRYPOINT ["/usr/local/bin/profanity2-entrypoint"] diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh new file mode 100755 index 0000000..a0e1509 --- /dev/null +++ b/docker/entrypoint.sh @@ -0,0 +1,133 @@ +#!/usr/bin/env bash +# +# Entrypoint of the profanity2 container image. +# +# Arguments are passed straight to profanity2, which is what the "docker +# ENTRYPOINT" launch mode of vast.ai needs: whatever is typed into its +# Arguments field ends up here. Launch modes that replace the entrypoint +# (SSH/Jupyter) only offer environment variables, so the same options can be +# given through PROFANITY_ARGS and PUBLIC_KEY instead. +# +# PUBLIC_KEY seed public key, added as `-z` unless already given +# PROFANITY_ARGS arguments to use when none are passed on the command +# line, split on whitespace (no shell quoting) +# PROFANITY_OUTPUT file the output is copied to, empty value disables it +# [default = /workspace/profanity2.log] +# PROFANITY_TIMEOUT stop after this long, e.g. 30m or 6h (exit code 124) +# PROFANITY_SKIP_GPU_CHECK skip the OpenCL device check before starting + +set -euo pipefail + +readonly BINARY=/opt/profanity2/profanity2.x64 + +log() { + printf 'profanity2-entrypoint: %s\n' "$*" >&2 +} + +count_opencl_platforms() { + local count= + + if command -v clinfo >/dev/null 2>&1; then + count="$(clinfo -l 2>/dev/null | grep -c '^Platform #' || true)" + + # Older clinfo releases format the compact listing differently, so fall + # back to the summary line of the full report before giving up. + case "$count" in + ''|0|*[!0-9]*) + count="$(clinfo 2>/dev/null | awk '/^Number of platforms/ { print $NF; exit }' || true)" + ;; + esac + fi + + case "$count" in + ''|*[!0-9]*) count=0 ;; + esac + + printf '%s\n' "$count" +} + +no_opencl_platform() { + log "error: no OpenCL platform found inside the container" + log "" + log " installed ICDs: $(echo /etc/OpenCL/vendors/*.icd)" + log " libnvidia-opencl.so.1: $(ldconfig -p | grep -c libnvidia-opencl.so.1) entries in the linker cache" + log "" + log " The container is running without a usable GPU driver. Start it with the" + log " NVIDIA runtime (docker run --gpus all ...) and keep \"compute\" in" + log " NVIDIA_DRIVER_CAPABILITIES. On vast.ai make sure the offer has an NVIDIA" + log " GPU. Run this image with the argument \"clinfo\" for the full diagnosis," + log " or set PROFANITY_SKIP_GPU_CHECK=1 to start anyway." +} + +args=("$@") + +# Anything that does not look like a profanity2 option (they all start with a +# dash) is treated as a command to run instead, e.g. `clinfo` or `bash`. +if [ ${#args[@]} -gt 0 ] && [ "${args[0]#-}" = "${args[0]}" ]; then + exec "${args[@]}" +fi + +if [ ${#args[@]} -eq 0 ] && [ -n "${PROFANITY_ARGS:-}" ]; then + read -r -a args <<<"$PROFANITY_ARGS" +fi + +if [ ${#args[@]} -eq 0 ]; then + log "no arguments given, printing help" + log "pass the scoring mode as container arguments or in PROFANITY_ARGS" + args=(--help) +fi + +wants_help=0 +has_public_key=0 +for arg in "${args[@]}"; do + case "$arg" in + -h|--help) wants_help=1 ;; + -z|--publicKey) has_public_key=1 ;; + esac +done + +public_key="${PUBLIC_KEY:-${PROFANITY_PUBLIC_KEY:-}}" +if [ "$has_public_key" -eq 0 ] && [ -n "$public_key" ]; then + args+=(-z "$public_key") +fi + +if [ "$wants_help" -eq 0 ] && [ -z "${PROFANITY_SKIP_GPU_CHECK:-}" ]; then + platforms="$(count_opencl_platforms)" + + if [ "$platforms" -eq 0 ]; then + no_opencl_platform + exit 1 + fi + + log "OpenCL platforms found: $platforms" +fi + +cmd=("$BINARY" "${args[@]}") +if [ -n "${PROFANITY_TIMEOUT:-}" ]; then + log "run time limited to $PROFANITY_TIMEOUT" + cmd=(timeout "$PROFANITY_TIMEOUT" "${cmd[@]}") +fi + +output="${PROFANITY_OUTPUT-/workspace/profanity2.log}" +if [ "$wants_help" -eq 1 ]; then + output= +fi + +if [ -n "$output" ]; then + if mkdir -p "$(dirname "$output")" 2>/dev/null && touch "$output" 2>/dev/null; then + log "results are also appended to $output" + else + log "warning: $output is not writable, results only go to the container log" + output= + fi +fi + +log "running: profanity2.x64 ${args[*]}" + +if [ -n "$output" ]; then + # Only stdout is copied: the hashrate counter is printed to stderr with + # carriage returns and would fill the file with terminal escapes. + "${cmd[@]}" | tee -a "$output" +else + exec "${cmd[@]}" +fi From 184165a888ec0f00d8f332b0bb015f48ccfdc89a Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 2 Aug 2026 11:06:27 +0000 Subject: [PATCH 2/7] Publish the container image to GHCR from CI Builds and smoke tests the image on every push and pull request, and publishes it to ghcr.io on master and version tags so it can be selected directly on GPU rental platforms. Co-authored-by: Gleb Alekseev --- .github/workflows/docker.yml | 71 ++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 .github/workflows/docker.yml diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 0000000..2270acb --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,71 @@ +name: docker + +on: + push: + branches: [master] + tags: ['v*'] + pull_request: + workflow_dispatch: + +env: + REGISTRY: ghcr.io + IMAGE_NAME: ${{ github.repository }} + +jobs: + image: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - uses: actions/checkout@v4 + + - uses: docker/setup-buildx-action@v3 + + - name: Build image + uses: docker/build-push-action@v6 + with: + context: . + load: true + tags: profanity2:ci + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Smoke test image + run: | + docker run --rm profanity2:ci --help | grep -q '^usage: ' + docker run --rm profanity2:ci clinfo --version + # No GPU on the runner, so the entrypoint must refuse to start. + ! docker run --rm profanity2:ci --benchmark -z "$(printf 'a%.0s' {1..128})" + + - name: Log in to the container registry + if: github.event_name != 'pull_request' + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Collect image tags + if: github.event_name != 'pull_request' + id: meta + uses: docker/metadata-action@v5 + with: + images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }} + tags: | + type=ref,event=branch + type=semver,pattern={{version}} + type=semver,pattern={{major}}.{{minor}} + type=sha,format=short + type=raw,value=latest,enable={{is_default_branch}} + + - name: Push image + if: github.event_name != 'pull_request' + uses: docker/build-push-action@v6 + with: + context: . + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha From de8f4dc20d8d7278033714aa6299a6f7b8d4d767 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 2 Aug 2026 11:08:24 +0000 Subject: [PATCH 3/7] Document running the image on rented GPUs Covers the vast.ai template and CLI equivalents, the environment variables the entrypoint understands, why an untrusted machine is acceptable for this tool, building your own image and the common failure modes. Co-authored-by: Gleb Alekseev --- README.md | 13 ++++ docs/VASTAI.md | 200 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 213 insertions(+) create mode 100644 docs/VASTAI.md diff --git a/README.md b/README.md index 2e7d213..a9137f6 100644 --- a/README.md +++ b/README.md @@ -85,6 +85,19 @@ or against a vendor OpenCL SDK, plus troubleshooting for the most common errors (`CL/cl.h: No such file or directory`, `CreateProcess(NULL, uname -s, ...) failed`, empty device list, WSL2 limitations). +### Docker and rented GPUs + +A prebuilt image with the OpenCL runtime is available, so nothing has to be +installed on the machine that does the searching: + +```bash +docker run --rm --gpus all ghcr.io/1inch/profanity2:latest --matching dead -z $PUBLIC_KEY +``` + +Since the tool only needs your public key, that machine does not have to be +yours. See [docs/VASTAI.md](docs/VASTAI.md) for renting a GPU on vast.ai and +running the image there with your own parameters. + # Usage ``` usage: ./profanity2 [OPTIONS] diff --git a/docs/VASTAI.md b/docs/VASTAI.md new file mode 100644 index 0000000..c157be9 --- /dev/null +++ b/docs/VASTAI.md @@ -0,0 +1,200 @@ +# Running profanity2 on a rented GPU (vast.ai) + +The [Dockerfile](../Dockerfile) in the repository root builds an image that +contains the compiled binary, the OpenCL kernels and the OpenCL runtime glue +needed inside a container. Nothing has to be installed on the rented machine: +you pick the image, type the profanity2 options into the template and start the +instance. + +The image is published as `ghcr.io/1inch/profanity2` (see +[Building your own image](#building-your-own-image) if you prefer your own +registry). It works on any Docker host with an NVIDIA GPU, vast.ai is just the +cheapest way to rent one. + +## Why this is safe on someone else's machine + +profanity2 never sees your private key. You pass a *public* key with `-z`, and +the private key it prints is an offset that is worthless without the seed +private key that stays on your machine — the two are added together locally +afterwards, see [Adding private keys](../README.md#adding-private-keys-never-use-online-calculators) +in the README. That is what makes renting GPU time from strangers acceptable +here. + +## Quick start on vast.ai + +1. Generate a seed key pair as described in + [Getting public key for mandatory `-z` parameter](../README.md#getting-public-key-for-mandatory--z-parameter). + Keep the private key, you will need it when the search finishes. + +2. Create a template ([cloud.vast.ai](https://cloud.vast.ai/templates/) → *New*): + + | Field | Value | + |---|---| + | Image Path:Tag | `ghcr.io/1inch/profanity2:latest` | + | Launch Mode | **docker ENTRYPOINT** | + | Arguments | `--matching dead -z YOUR_128_HEX_PUBLIC_KEY` | + | Docker Options | *(optional)* `-e PROFANITY_TIMEOUT=6h` | + | Disk Space | 12 GB (the image is under 100 MB, this is just the minimum) | + + Launch mode matters: in the SSH and Jupyter modes vast.ai replaces the image + entrypoint with its own setup script, so the miner would never start. The + ENTRYPOINT mode runs the image as is and appends the *Arguments* field to the + entrypoint, which is exactly the profanity2 command line. + +3. Pick an offer on the [search page](https://cloud.vast.ai/create/) and rent it. + profanity2 is pure compute, so sort by price and compare against the hashrate + table in the [README](../README.md#benchmarks---current-version) — an RTX 4090 + does about 1096 MH/s. + +4. Watch the instance log (the terminal icon on the instance card). Every + improvement is printed as it is found: + + ``` + Mode: matching + Target: Address + Devices: + GPU0: NVIDIA GeForce RTX 4090, 25757220864 bytes available, 128 compute units (precompiled = no) + Time: 31s Score: 4 Private: 0x5b9…c41 Address: 0xdead4b0… + ``` + +5. Add the printed private key to your seed private key to get the final key, + then verify the address by importing it into a wallet. + +Everything that reaches stdout is also appended to `/workspace/profanity2.log` +inside the instance, so a result is not lost when the log view scrolls away. Copy +it out with `vastai copy :/workspace/profanity2.log .` or read it +over SSH if the instance has it. + +## The same thing from the CLI + +```bash +pip install vastai +vastai set api-key YOUR_API_KEY + +# find something cheap with a single 4090 +vastai search offers 'gpu_name=RTX_4090 num_gpus=1 rentable=true' -o 'dph+' | head + +# rent it and start the search immediately +vastai create instance \ + --image ghcr.io/1inch/profanity2:latest \ + --disk 12 \ + --label profanity2 \ + --runtype args \ + --env '-e PROFANITY_TIMEOUT=6h' \ + --args --matching dead -z YOUR_128_HEX_PUBLIC_KEY + +vastai show instances +vastai logs +vastai destroy instance +``` + +`--runtype args` is the CLI name of the ENTRYPOINT launch mode. `--args` must be +the **last** option, everything after it is passed to the container. + +## Configuration + +Options can be given as container arguments (the *Arguments* field) or through +environment variables (the *Docker Options* field, `-e NAME=value`). Environment +variables are the only way to configure the run in the SSH and Jupyter launch +modes, where the on-start script has to call `profanity2-entrypoint` itself. + +| Variable | Effect | +|---|---| +| `PUBLIC_KEY` | Added as `-z` unless the arguments already contain `-z` | +| `PROFANITY_ARGS` | Arguments to use when none are passed to the container, e.g. `--matching dead` | +| `PROFANITY_OUTPUT` | File the output is copied to, default `/workspace/profanity2.log`, empty value disables it | +| `PROFANITY_TIMEOUT` | Stop the search after this long, e.g. `30m`, `6h`. The container then exits with code 124 | +| `PROFANITY_SKIP_GPU_CHECK` | Start even when no OpenCL platform is detected | + +So this template configuration + +``` +Arguments: --matching dead -z YOUR_128_HEX_PUBLIC_KEY +``` + +and this one + +``` +Arguments: (empty) +Docker Options: -e PUBLIC_KEY=YOUR_128_HEX_PUBLIC_KEY -e PROFANITY_ARGS="--matching dead" +``` + +do the same thing. All scoring modes of the tool are available, see +[Usage examples](../README.md#usage-examples) in the README. + +Note that most modes never finish on their own: `--matching` keeps looking for a +better score and `--exact` keeps printing matches until it is stopped. Either set +`PROFANITY_TIMEOUT` or destroy the instance yourself once you have what you +wanted — a running instance keeps costing money. + +Two arguments are handled by the entrypoint instead of profanity2: + +```bash +# anything that does not start with a dash runs instead of the miner +docker run --rm --gpus all ghcr.io/1inch/profanity2:latest clinfo +docker run --rm --gpus all ghcr.io/1inch/profanity2:latest bash +``` + +## Running the image on your own GPU + +```bash +docker run --rm --gpus all ghcr.io/1inch/profanity2:latest \ + --matching dead -z YOUR_128_HEX_PUBLIC_KEY +``` + +Requires the NVIDIA driver and the +[NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) +on the host. The OpenCL kernel is compiled again on every start; to keep the +compiled kernel cache (`cache-opencl.*`) between runs, put a named volume on the +working directory — Docker seeds a fresh named volume with the contents of the +image, so the binary and the kernels stay in place: + +```bash +docker run --rm --gpus all -v profanity2-cache:/opt/profanity2 \ + ghcr.io/1inch/profanity2:latest --matching dead -z YOUR_128_HEX_PUBLIC_KEY +``` + +## Building your own image + +The published image lags behind `master` and lives in a registry you do not +control. Building your own takes about a minute: + +```bash +git clone https://github.com/1inch/profanity2 +cd profanity2 +docker build -t YOUR_DOCKERHUB_USER/profanity2 . +docker push YOUR_DOCKERHUB_USER/profanity2 +``` + +Then use `YOUR_DOCKERHUB_USER/profanity2` as the image path in the vast.ai +template. Private repositories work too, vast.ai has a field for the +`docker login` credentials in the template. + +## Troubleshooting + +**`error: no OpenCL platform found inside the container`** — the container has no +usable GPU driver. Run `clinfo` in the same image (`Arguments: clinfo`) to see +what the ICD loader finds. On a self-hosted machine, check that the container was +started with `--gpus all` and that the NVIDIA Container Toolkit is installed. On +vast.ai, verify that the offer really has an NVIDIA GPU; if it does and the error +persists, the host driver is broken — destroy the instance and rent another one, +you should not pay for it. + +**The instance shows as exited immediately** — read the log. Without arguments +the entrypoint prints the help text and exits, and profanity2 itself refuses to +start when `-z` is missing or is not exactly 128 hex characters (the `04` prefix +of the public key must be removed). + +**`Devices:` is printed but the list is empty** — an OpenCL platform exists but +exposes no GPU device. This is usually a mismatched driver on the host. + +**Results look wrong (the private key does not match the address)** — always +verify a found key in a wallet before using it. On AMD hardware this used to be +caused by old drivers, see issue +[#13](https://github.com/1inch/profanity2/issues/13). + +**AMD GPUs** — this image only ships the NVIDIA ICD. ROCm needs its own OpenCL +runtime inside the image and access to `/dev/kfd` and `/dev/dri` in the +container, which vast.ai does not expose on every AMD host. Building on +[docs/BUILD_UBUNTU.md](BUILD_UBUNTU.md) and a `rocm/dev-ubuntu-24.04` base image +is the starting point if you need it. From 3d6994d7ab12fc7226849e9b5dfaf3d32aaf7f93 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 2 Aug 2026 11:11:18 +0000 Subject: [PATCH 4/7] Correct the vast.ai CLI and sample output details in the guide Co-authored-by: Gleb Alekseev --- docs/VASTAI.md | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/docs/VASTAI.md b/docs/VASTAI.md index c157be9..2dc20ff 100644 --- a/docs/VASTAI.md +++ b/docs/VASTAI.md @@ -37,7 +37,7 @@ here. | Disk Space | 12 GB (the image is under 100 MB, this is just the minimum) | Launch mode matters: in the SSH and Jupyter modes vast.ai replaces the image - entrypoint with its own setup script, so the miner would never start. The + entrypoint with its own setup script, so the search would never start. The ENTRYPOINT mode runs the image as is and appends the *Arguments* field to the entrypoint, which is exactly the profanity2 command line. @@ -46,24 +46,25 @@ here. table in the [README](../README.md#benchmarks---current-version) — an RTX 4090 does about 1096 MH/s. -4. Watch the instance log (the terminal icon on the instance card). Every - improvement is printed as it is found: +4. Watch the instance log (the log button on the instance card, or + `vastai logs `). Every improvement is printed as it is found: ``` Mode: matching Target: Address Devices: GPU0: NVIDIA GeForce RTX 4090, 25757220864 bytes available, 128 compute units (precompiled = no) - Time: 31s Score: 4 Private: 0x5b9…c41 Address: 0xdead4b0… + ... + Time: 31s Score: 4 Private: 0x5b9...c41 Address: 0xdead4b0... ``` 5. Add the printed private key to your seed private key to get the final key, then verify the address by importing it into a wallet. Everything that reaches stdout is also appended to `/workspace/profanity2.log` -inside the instance, so a result is not lost when the log view scrolls away. Copy -it out with `vastai copy :/workspace/profanity2.log .` or read it -over SSH if the instance has it. +inside the instance, so a result is not lost when the log view scrolls away. +Fetch it with `vastai copy C.:/workspace/ local:results/` before +destroying the instance. ## The same thing from the CLI @@ -96,7 +97,12 @@ the **last** option, everything after it is passed to the container. Options can be given as container arguments (the *Arguments* field) or through environment variables (the *Docker Options* field, `-e NAME=value`). Environment variables are the only way to configure the run in the SSH and Jupyter launch -modes, where the on-start script has to call `profanity2-entrypoint` itself. +modes, where the entrypoint is replaced and the on-start script has to start it: + +```bash +env >> /etc/environment +/usr/local/bin/profanity2-entrypoint +``` | Variable | Effect | |---|---| @@ -127,10 +133,10 @@ better score and `--exact` keeps printing matches until it is stopped. Either se `PROFANITY_TIMEOUT` or destroy the instance yourself once you have what you wanted — a running instance keeps costing money. -Two arguments are handled by the entrypoint instead of profanity2: +An argument that does not start with a dash is run as a command instead of +profanity2, which is what you want when an instance misbehaves: ```bash -# anything that does not start with a dash runs instead of the miner docker run --rm --gpus all ghcr.io/1inch/profanity2:latest clinfo docker run --rm --gpus all ghcr.io/1inch/profanity2:latest bash ``` @@ -156,8 +162,8 @@ docker run --rm --gpus all -v profanity2-cache:/opt/profanity2 \ ## Building your own image -The published image lags behind `master` and lives in a registry you do not -control. Building your own takes about a minute: +If you have local changes, or would rather not depend on a registry someone else +controls, building your own takes about a minute: ```bash git clone https://github.com/1inch/profanity2 From 4991625f1fc2a267bf621f945beaa8b0ca2af783 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 2 Aug 2026 22:45:29 +0000 Subject: [PATCH 5/7] Ignore a PUBLIC_KEY that is not a seed public key Rental platforms hand out the name PUBLIC_KEY for their own SSH key, and it can also sit in an account-wide environment variable, in which case the entrypoint appended it as -z and profanity2 refused to start. It is now only used when it holds 128 hexadecimal characters, and PROFANITY_PUBLIC_KEY takes precedence. Co-authored-by: Gleb Alekseev --- docker/entrypoint.sh | 17 +++++++++++++++-- docs/VASTAI.md | 22 +++++++++++++++++++--- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/docker/entrypoint.sh b/docker/entrypoint.sh index a0e1509..db8db4a 100755 --- a/docker/entrypoint.sh +++ b/docker/entrypoint.sh @@ -8,7 +8,10 @@ # (SSH/Jupyter) only offer environment variables, so the same options can be # given through PROFANITY_ARGS and PUBLIC_KEY instead. # -# PUBLIC_KEY seed public key, added as `-z` unless already given +# PROFANITY_PUBLIC_KEY seed public key, added as `-z` unless already given +# PUBLIC_KEY same, but only when it holds 128 hexadecimal +# characters - GPU rental platforms hand out that name +# for their own SSH key # PROFANITY_ARGS arguments to use when none are passed on the command # line, split on whitespace (no shell quoting) # PROFANITY_OUTPUT file the output is copied to, empty value disables it @@ -86,7 +89,17 @@ for arg in "${args[@]}"; do esac done -public_key="${PUBLIC_KEY:-${PROFANITY_PUBLIC_KEY:-}}" +public_key="${PROFANITY_PUBLIC_KEY:-}" +if [ -z "$public_key" ] && [ -n "${PUBLIC_KEY:-}" ]; then + if printf '%s' "$PUBLIC_KEY" | grep -qE '^[0-9a-fA-F]{128}$'; then + public_key="$PUBLIC_KEY" + else + log "warning: ignoring PUBLIC_KEY, it does not hold 128 hexadecimal characters" + log " rental platforms set that name to their own SSH key; use" + log " PROFANITY_PUBLIC_KEY, or pass -z, to be unambiguous" + fi +fi + if [ "$has_public_key" -eq 0 ] && [ -n "$public_key" ]; then args+=(-z "$public_key") fi diff --git a/docs/VASTAI.md b/docs/VASTAI.md index 2dc20ff..ebbdbae 100644 --- a/docs/VASTAI.md +++ b/docs/VASTAI.md @@ -106,7 +106,8 @@ env >> /etc/environment | Variable | Effect | |---|---| -| `PUBLIC_KEY` | Added as `-z` unless the arguments already contain `-z` | +| `PROFANITY_PUBLIC_KEY` | Added as `-z` unless the arguments already contain `-z` | +| `PUBLIC_KEY` | The same, but only when it holds 128 hexadecimal characters | | `PROFANITY_ARGS` | Arguments to use when none are passed to the container, e.g. `--matching dead` | | `PROFANITY_OUTPUT` | File the output is copied to, default `/workspace/profanity2.log`, empty value disables it | | `PROFANITY_TIMEOUT` | Stop the search after this long, e.g. `30m`, `6h`. The container then exits with code 124 | @@ -122,12 +123,18 @@ and this one ``` Arguments: (empty) -Docker Options: -e PUBLIC_KEY=YOUR_128_HEX_PUBLIC_KEY -e PROFANITY_ARGS="--matching dead" +Docker Options: -e PROFANITY_PUBLIC_KEY=YOUR_128_HEX_PUBLIC_KEY -e PROFANITY_ARGS="--matching dead" ``` do the same thing. All scoring modes of the tool are available, see [Usage examples](../README.md#usage-examples) in the README. +`PUBLIC_KEY` works too, but prefer the longer name: rental platforms hand out +that generic name for their own purposes (it is the SSH public key on RunPod, +and it may already sit in your vast.ai account-wide environment variables), and +a value that is not 128 hexadecimal characters is ignored with a warning rather +than passed on to profanity2. + Note that most modes never finish on their own: `--matching` keeps looking for a better score and `--exact` keeps printing matches until it is stopped. Either set `PROFANITY_TIMEOUT` or destroy the instance yourself once you have what you @@ -189,7 +196,16 @@ you should not pay for it. **The instance shows as exited immediately** — read the log. Without arguments the entrypoint prints the help text and exits, and profanity2 itself refuses to start when `-z` is missing or is not exactly 128 hex characters (the `04` prefix -of the public key must be removed). +of the public key must be removed). If the same startup banner appears several +times over, the platform is restarting the failing container in a loop and +billing you for it — destroy the instance. + +**`error: public key must be 128 hexademical characters long` although you +passed a correct key** — something else in the environment is called +`PUBLIC_KEY`. Check the account-wide environment variables on your +[account page](https://cloud.vast.ai/account/), and pass the key as `-z` or in +`PROFANITY_PUBLIC_KEY` instead. Running the image with `env` as its only +argument prints the whole environment. **`Devices:` is printed but the list is empty** — an OpenCL platform exists but exposes no GPU device. This is usually a mismatched driver on the host. From 46b3da05eb3810e457866a06da585fca10394575 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 2 Aug 2026 23:22:05 +0000 Subject: [PATCH 6/7] Fix the vast.ai CLI examples and unwrap the guide There is no --runtype flag in the CLI: passing --args is what selects the entrypoint launch mode, and the documented command failed outright. Ascending price is -o dph, not dph+. And vastai copy needs SSH, which the entrypoint launch mode does not provide, so the instance log is the way to keep a result. Paragraphs are no longer hard wrapped. Co-authored-by: Gleb Alekseev --- README.md | 7 +-- docs/VASTAI.md | 137 +++++++++++++------------------------------------ 2 files changed, 38 insertions(+), 106 deletions(-) diff --git a/README.md b/README.md index a9137f6..9f36135 100644 --- a/README.md +++ b/README.md @@ -87,16 +87,13 @@ empty device list, WSL2 limitations). ### Docker and rented GPUs -A prebuilt image with the OpenCL runtime is available, so nothing has to be -installed on the machine that does the searching: +A prebuilt image with the OpenCL runtime is available, so nothing has to be installed on the machine that does the searching: ```bash docker run --rm --gpus all ghcr.io/1inch/profanity2:latest --matching dead -z $PUBLIC_KEY ``` -Since the tool only needs your public key, that machine does not have to be -yours. See [docs/VASTAI.md](docs/VASTAI.md) for renting a GPU on vast.ai and -running the image there with your own parameters. +Since the tool only needs your public key, that machine does not have to be yours. See [docs/VASTAI.md](docs/VASTAI.md) for renting a GPU on vast.ai and running the image there with your own parameters. # Usage ``` diff --git a/docs/VASTAI.md b/docs/VASTAI.md index ebbdbae..2c6d283 100644 --- a/docs/VASTAI.md +++ b/docs/VASTAI.md @@ -1,30 +1,16 @@ # Running profanity2 on a rented GPU (vast.ai) -The [Dockerfile](../Dockerfile) in the repository root builds an image that -contains the compiled binary, the OpenCL kernels and the OpenCL runtime glue -needed inside a container. Nothing has to be installed on the rented machine: -you pick the image, type the profanity2 options into the template and start the -instance. +The [Dockerfile](../Dockerfile) in the repository root builds an image that contains the compiled binary, the OpenCL kernels and the OpenCL runtime glue needed inside a container. Nothing has to be installed on the rented machine: you pick the image, type the profanity2 options into the template and start the instance. -The image is published as `ghcr.io/1inch/profanity2` (see -[Building your own image](#building-your-own-image) if you prefer your own -registry). It works on any Docker host with an NVIDIA GPU, vast.ai is just the -cheapest way to rent one. +The image is published as `ghcr.io/1inch/profanity2` (see [Building your own image](#building-your-own-image) if you prefer your own registry). It works on any Docker host with an NVIDIA GPU, vast.ai is just the cheapest way to rent one. ## Why this is safe on someone else's machine -profanity2 never sees your private key. You pass a *public* key with `-z`, and -the private key it prints is an offset that is worthless without the seed -private key that stays on your machine — the two are added together locally -afterwards, see [Adding private keys](../README.md#adding-private-keys-never-use-online-calculators) -in the README. That is what makes renting GPU time from strangers acceptable -here. +profanity2 never sees your private key. You pass a *public* key with `-z`, and the private key it prints is an offset that is worthless without the seed private key that stays on your machine — the two are added together locally afterwards, see [Adding private keys](../README.md#adding-private-keys-never-use-online-calculators) in the README. That is what makes renting GPU time from strangers acceptable here. ## Quick start on vast.ai -1. Generate a seed key pair as described in - [Getting public key for mandatory `-z` parameter](../README.md#getting-public-key-for-mandatory--z-parameter). - Keep the private key, you will need it when the search finishes. +1. Generate a seed key pair as described in [Getting public key for mandatory `-z` parameter](../README.md#getting-public-key-for-mandatory--z-parameter). Keep the private key, you will need it when the search finishes. 2. Create a template ([cloud.vast.ai](https://cloud.vast.ai/templates/) → *New*): @@ -36,18 +22,11 @@ here. | Docker Options | *(optional)* `-e PROFANITY_TIMEOUT=6h` | | Disk Space | 12 GB (the image is under 100 MB, this is just the minimum) | - Launch mode matters: in the SSH and Jupyter modes vast.ai replaces the image - entrypoint with its own setup script, so the search would never start. The - ENTRYPOINT mode runs the image as is and appends the *Arguments* field to the - entrypoint, which is exactly the profanity2 command line. + Launch mode matters: in the SSH and Jupyter modes vast.ai replaces the image entrypoint with its own setup script, so the search would never start. The ENTRYPOINT mode runs the image as is and appends the *Arguments* field to the entrypoint, which is exactly the profanity2 command line. -3. Pick an offer on the [search page](https://cloud.vast.ai/create/) and rent it. - profanity2 is pure compute, so sort by price and compare against the hashrate - table in the [README](../README.md#benchmarks---current-version) — an RTX 4090 - does about 1096 MH/s. +3. Pick an offer on the [search page](https://cloud.vast.ai/create/) and rent it. profanity2 is pure compute, so sort by price and compare against the hashrate table in the [README](../README.md#benchmarks---current-version) — an RTX 4090 does about 1096 MH/s. -4. Watch the instance log (the log button on the instance card, or - `vastai logs `). Every improvement is printed as it is found: +4. Watch the instance log (the log button on the instance card, or `vastai logs `). Every improvement is printed as it is found: ``` Mode: matching @@ -58,13 +37,15 @@ here. Time: 31s Score: 4 Private: 0x5b9...c41 Address: 0xdead4b0... ``` -5. Add the printed private key to your seed private key to get the final key, - then verify the address by importing it into a wallet. +5. Add the printed private key to your seed private key to get the final key, then verify the address by importing it into a wallet. -Everything that reaches stdout is also appended to `/workspace/profanity2.log` -inside the instance, so a result is not lost when the log view scrolls away. -Fetch it with `vastai copy C.:/workspace/ local:results/` before -destroying the instance. +Everything that reaches stdout is also appended to `/workspace/profanity2.log` inside the instance, so a result is not lost when the log view scrolls away. In the ENTRYPOINT launch mode the instance log is the only channel out of the container, though - there is no SSH to copy that file over. Save what you need before destroying the instance: + +```bash +vastai logs --tail 500 > profanity2-results.log +``` + +If you want a shell as well, rent in the SSH launch mode instead and start the search from the on-start script shown under [Configuration](#configuration). ## The same thing from the CLI @@ -73,14 +54,13 @@ pip install vastai vastai set api-key YOUR_API_KEY # find something cheap with a single 4090 -vastai search offers 'gpu_name=RTX_4090 num_gpus=1 rentable=true' -o 'dph+' | head +vastai search offers 'gpu_name=RTX_4090 num_gpus=1' -o 'dph' | head # rent it and start the search immediately vastai create instance \ --image ghcr.io/1inch/profanity2:latest \ --disk 12 \ --label profanity2 \ - --runtype args \ --env '-e PROFANITY_TIMEOUT=6h' \ --args --matching dead -z YOUR_128_HEX_PUBLIC_KEY @@ -89,15 +69,11 @@ vastai logs vastai destroy instance ``` -`--runtype args` is the CLI name of the ENTRYPOINT launch mode. `--args` must be -the **last** option, everything after it is passed to the container. +There is no flag for the launch mode: passing `--args` is what selects it, the way `--ssh` and `--jupyter` select theirs. `--args` must be the **last** option, everything after it is passed to the container. Sorting by `dph` lists the cheapest offers first, `dph-` the most expensive. ## Configuration -Options can be given as container arguments (the *Arguments* field) or through -environment variables (the *Docker Options* field, `-e NAME=value`). Environment -variables are the only way to configure the run in the SSH and Jupyter launch -modes, where the entrypoint is replaced and the on-start script has to start it: +Options can be given as container arguments (the *Arguments* field) or through environment variables (the *Docker Options* field, `-e NAME=value`). Environment variables are the only way to configure the run in the SSH and Jupyter launch modes, where the entrypoint is replaced and the on-start script has to start it: ```bash env >> /etc/environment @@ -126,22 +102,13 @@ Arguments: (empty) Docker Options: -e PROFANITY_PUBLIC_KEY=YOUR_128_HEX_PUBLIC_KEY -e PROFANITY_ARGS="--matching dead" ``` -do the same thing. All scoring modes of the tool are available, see -[Usage examples](../README.md#usage-examples) in the README. +do the same thing. All scoring modes of the tool are available, see [Usage examples](../README.md#usage-examples) in the README. -`PUBLIC_KEY` works too, but prefer the longer name: rental platforms hand out -that generic name for their own purposes (it is the SSH public key on RunPod, -and it may already sit in your vast.ai account-wide environment variables), and -a value that is not 128 hexadecimal characters is ignored with a warning rather -than passed on to profanity2. +`PUBLIC_KEY` works too, but prefer the longer name: rental platforms hand out that generic name for their own purposes (it is the SSH public key on RunPod, and it may already sit in your vast.ai account-wide environment variables), and a value that is not 128 hexadecimal characters is ignored with a warning rather than passed on to profanity2. -Note that most modes never finish on their own: `--matching` keeps looking for a -better score and `--exact` keeps printing matches until it is stopped. Either set -`PROFANITY_TIMEOUT` or destroy the instance yourself once you have what you -wanted — a running instance keeps costing money. +Note that most modes never finish on their own: `--matching` keeps looking for a better score and `--exact` keeps printing matches until it is stopped. Either set `PROFANITY_TIMEOUT` or destroy the instance yourself once you have what you wanted — a running instance keeps costing money. -An argument that does not start with a dash is run as a command instead of -profanity2, which is what you want when an instance misbehaves: +An argument that does not start with a dash is run as a command instead of profanity2, which is what you want when an instance misbehaves: ```bash docker run --rm --gpus all ghcr.io/1inch/profanity2:latest clinfo @@ -155,12 +122,7 @@ docker run --rm --gpus all ghcr.io/1inch/profanity2:latest \ --matching dead -z YOUR_128_HEX_PUBLIC_KEY ``` -Requires the NVIDIA driver and the -[NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) -on the host. The OpenCL kernel is compiled again on every start; to keep the -compiled kernel cache (`cache-opencl.*`) between runs, put a named volume on the -working directory — Docker seeds a fresh named volume with the contents of the -image, so the binary and the kernels stay in place: +Requires the NVIDIA driver and the [NVIDIA Container Toolkit](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/latest/install-guide.html) on the host. The OpenCL kernel is compiled again on every start; to keep the compiled kernel cache (`cache-opencl.*`) between runs, put a named volume on the working directory — Docker seeds a fresh named volume with the contents of the image, so the binary and the kernels stay in place: ```bash docker run --rm --gpus all -v profanity2-cache:/opt/profanity2 \ @@ -169,8 +131,7 @@ docker run --rm --gpus all -v profanity2-cache:/opt/profanity2 \ ## Building your own image -If you have local changes, or would rather not depend on a registry someone else -controls, building your own takes about a minute: +If you have local changes, or would rather not depend on a registry someone else controls, building your own takes about a minute: ```bash git clone https://github.com/1inch/profanity2 @@ -179,44 +140,18 @@ docker build -t YOUR_DOCKERHUB_USER/profanity2 . docker push YOUR_DOCKERHUB_USER/profanity2 ``` -Then use `YOUR_DOCKERHUB_USER/profanity2` as the image path in the vast.ai -template. Private repositories work too, vast.ai has a field for the -`docker login` credentials in the template. +Then use `YOUR_DOCKERHUB_USER/profanity2` as the image path in the vast.ai template. Private repositories work too, vast.ai has a field for the `docker login` credentials in the template. ## Troubleshooting -**`error: no OpenCL platform found inside the container`** — the container has no -usable GPU driver. Run `clinfo` in the same image (`Arguments: clinfo`) to see -what the ICD loader finds. On a self-hosted machine, check that the container was -started with `--gpus all` and that the NVIDIA Container Toolkit is installed. On -vast.ai, verify that the offer really has an NVIDIA GPU; if it does and the error -persists, the host driver is broken — destroy the instance and rent another one, -you should not pay for it. - -**The instance shows as exited immediately** — read the log. Without arguments -the entrypoint prints the help text and exits, and profanity2 itself refuses to -start when `-z` is missing or is not exactly 128 hex characters (the `04` prefix -of the public key must be removed). If the same startup banner appears several -times over, the platform is restarting the failing container in a loop and -billing you for it — destroy the instance. - -**`error: public key must be 128 hexademical characters long` although you -passed a correct key** — something else in the environment is called -`PUBLIC_KEY`. Check the account-wide environment variables on your -[account page](https://cloud.vast.ai/account/), and pass the key as `-z` or in -`PROFANITY_PUBLIC_KEY` instead. Running the image with `env` as its only -argument prints the whole environment. - -**`Devices:` is printed but the list is empty** — an OpenCL platform exists but -exposes no GPU device. This is usually a mismatched driver on the host. - -**Results look wrong (the private key does not match the address)** — always -verify a found key in a wallet before using it. On AMD hardware this used to be -caused by old drivers, see issue -[#13](https://github.com/1inch/profanity2/issues/13). - -**AMD GPUs** — this image only ships the NVIDIA ICD. ROCm needs its own OpenCL -runtime inside the image and access to `/dev/kfd` and `/dev/dri` in the -container, which vast.ai does not expose on every AMD host. Building on -[docs/BUILD_UBUNTU.md](BUILD_UBUNTU.md) and a `rocm/dev-ubuntu-24.04` base image -is the starting point if you need it. +**`error: no OpenCL platform found inside the container`** — the container has no usable GPU driver. Run `clinfo` in the same image (`Arguments: clinfo`) to see what the ICD loader finds. On a self-hosted machine, check that the container was started with `--gpus all` and that the NVIDIA Container Toolkit is installed. On vast.ai, verify that the offer really has an NVIDIA GPU; if it does and the error persists, the host driver is broken — destroy the instance and rent another one, you should not pay for it. + +**The instance shows as exited immediately** — read the log. Without arguments the entrypoint prints the help text and exits, and profanity2 itself refuses to start when `-z` is missing or is not exactly 128 hex characters (the `04` prefix of the public key must be removed). If the same startup banner appears several times over, the platform is restarting the failing container in a loop and billing you for it — destroy the instance. + +**`error: public key must be 128 hexademical characters long` although you passed a correct key** — something else in the environment is called `PUBLIC_KEY`. Check the account-wide environment variables on your [account page](https://cloud.vast.ai/account/), and pass the key as `-z` or in `PROFANITY_PUBLIC_KEY` instead. Running the image with `env` as its only argument prints the whole environment. + +**`Devices:` is printed but the list is empty** — an OpenCL platform exists but exposes no GPU device. This is usually a mismatched driver on the host. + +**Results look wrong (the private key does not match the address)** — always verify a found key in a wallet before using it. On AMD hardware this used to be caused by old drivers, see issue [#13](https://github.com/1inch/profanity2/issues/13). + +**AMD GPUs** — this image only ships the NVIDIA ICD. ROCm needs its own OpenCL runtime inside the image and access to `/dev/kfd` and `/dev/dri` in the container, which vast.ai does not expose on every AMD host. Building on [docs/BUILD_UBUNTU.md](BUILD_UBUNTU.md) and a `rocm/dev-ubuntu-24.04` base image is the starting point if you need it. From 58720aa2a906a37f9198e414a2b08b57b1fcf6d5 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Sun, 2 Aug 2026 23:42:58 +0000 Subject: [PATCH 7/7] Document how to build and publish the image to GHCR and ttl.sh The guide told you to build your own image but not how to get it onto a rented machine, which needs a registry. GHCR covers repeated use, including the part that trips everyone up - a fresh package is private and the instance cannot pull it - and ttl.sh covers a single throwaway run. Both note that the build has to target amd64. Co-authored-by: Gleb Alekseev --- docs/VASTAI.md | 49 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 45 insertions(+), 4 deletions(-) diff --git a/docs/VASTAI.md b/docs/VASTAI.md index 2c6d283..ecccef8 100644 --- a/docs/VASTAI.md +++ b/docs/VASTAI.md @@ -131,21 +131,62 @@ docker run --rm --gpus all -v profanity2-cache:/opt/profanity2 \ ## Building your own image -If you have local changes, or would rather not depend on a registry someone else controls, building your own takes about a minute: +Build your own if you have local changes, if you want a revision that is not published yet, or if you would rather not depend on a registry someone else controls. A rented machine can only pull from a registry, so the image has to be pushed somewhere first - GHCR if you are going to use it more than once, ttl.sh for a single throwaway run. ```bash git clone https://github.com/1inch/profanity2 cd profanity2 -docker build -t YOUR_DOCKERHUB_USER/profanity2 . -docker push YOUR_DOCKERHUB_USER/profanity2 +docker build --platform linux/amd64 -t profanity2 . ``` -Then use `YOUR_DOCKERHUB_USER/profanity2` as the image path in the vast.ai template. Private repositories work too, vast.ai has a field for the `docker login` credentials in the template. +`--platform linux/amd64` is not optional. The Linux branch of the Makefile passes `-mmmx` and `-mcmodel=large`, which do not exist on arm64, so a native build on an Apple Silicon Mac fails with `unrecognized command-line option '-mmmx'`. Rented GPU machines are x86_64 anyway. Under emulation the build takes a few minutes rather than the half minute it needs on an x86 host. + +### GitHub Container Registry + +The image stays until you delete it and the name is readable, which is what you want if you are going to rent machines more than once. Create a personal access token with the `write:packages` scope, then: + +```bash +echo YOUR_TOKEN | docker login ghcr.io -u YOUR_USER --password-stdin +docker tag profanity2 ghcr.io/YOUR_USER/profanity2:latest +docker push ghcr.io/YOUR_USER/profanity2:latest +``` + +A package pushed to GHCR is **private by default**, so a rented machine cannot pull it yet. Either make it public once, under Packages on your GitHub profile, or hand the credentials to vast.ai: + +```bash +# public package +vastai create instance --image ghcr.io/YOUR_USER/profanity2:latest \ + --disk 12 --args --matching dead -z YOUR_128_HEX_PUBLIC_KEY + +# private package +vastai create instance --image ghcr.io/YOUR_USER/profanity2:latest \ + --login '-u YOUR_USER -p YOUR_TOKEN ghcr.io' \ + --disk 12 --args --matching dead -z YOUR_128_HEX_PUBLIC_KEY +``` + +In the GUI the same credentials go into the *Docker login* field of the template, next to the image path. + +### ttl.sh + +[ttl.sh](https://ttl.sh) is an anonymous registry that deletes what you push after the time given in the tag. No account, no login, no cleanup - convenient for a one-off search: + +```bash +IMAGE=ttl.sh/profanity2-$(uuidgen | tr '[:upper:]' '[:lower:]'):24h +docker build --platform linux/amd64 -t "$IMAGE" . +docker push "$IMAGE" +echo "$IMAGE" +``` + +The tag is the lifetime and 24 hours is the maximum, so the image needs a unique name instead - hence the UUID. Anyone who learns that name can pull the image, which is harmless here: it holds nothing but public source code and no key of yours. Do keep the lifetime longer than the search, because an instance that gets recreated or moved after the image expired will fail to start. + +Whichever registry you use, its full name goes into the *Image Path:Tag* field of the template, or into `--image` on the command line. ## Troubleshooting **`error: no OpenCL platform found inside the container`** — the container has no usable GPU driver. Run `clinfo` in the same image (`Arguments: clinfo`) to see what the ICD loader finds. On a self-hosted machine, check that the container was started with `--gpus all` and that the NVIDIA Container Toolkit is installed. On vast.ai, verify that the offer really has an NVIDIA GPU; if it does and the error persists, the host driver is broken — destroy the instance and rent another one, you should not pay for it. +**The instance stays in `loading` and never runs** — the image could not be pulled. A package you pushed to GHCR is private until you say otherwise, and a ttl.sh image is gone once the lifetime in its tag has passed. Check `status_msg` in `vastai show instance --raw`. + **The instance shows as exited immediately** — read the log. Without arguments the entrypoint prints the help text and exits, and profanity2 itself refuses to start when `-z` is missing or is not exactly 128 hex characters (the `04` prefix of the public key must be removed). If the same startup banner appears several times over, the platform is restarting the failing container in a loop and billing you for it — destroy the instance. **`error: public key must be 128 hexademical characters long` although you passed a correct key** — something else in the environment is called `PUBLIC_KEY`. Check the account-wide environment variables on your [account page](https://cloud.vast.ai/account/), and pass the key as `-z` or in `PROFANITY_PUBLIC_KEY` instead. Running the image with `env` as its only argument prints the whole environment.