UMA NWC Docker Image

The UMA NWC Docker image is a pre-built Docker image that handles all of the Nostr Wallet Connect communication, budget management, connection setup and management UI, etc. It is configurable via environment variables to control UI branding, database storage, supported NWC commands, and more. You can find the code for the image in this github repo along with steps for running it from source. The image is also available on Github Container Registry at ghcr.io/uma-universal-money-address/uma-nwc-server.
You can pull the image with:
docker pull ghcr.io/uma-universal-money-address/uma-nwc-server:latest
Other tags include main for the tip of tree and vX.Y.Z for specific versions. Releases are tagged in the Github repo and latest is updated with each tagged release.
You also use the image as a base for your own Dockerfile to add customizations or additional services by using this image in the FROM directive:
FROM ghcr.io/uma-universal-money-address/uma-nwc-server:latest
Of course, you can also use the image directly with docker run:
docker run -p 8080:8081 \
-e QUART_CONFIG=config/prod.py \
ghcr.io/uma-universal-money-address/uma-nwc-server:latest
The next section will cover what goes in that QUART_CONFIG file and other environment variables you can use to configure the image.
The docker image is built using the Quart web framework, which is a Python async web framework. Quart can be configured with a config file that sets up variables used by the application. At runtime, you can pass in a file path to the QUART_CONFIG environment variable to tell Quart where to find the configuration file. The github repo includes some sample config files that you can use as a starting point or when running locally during development. See this directory for those configs.
The quart config file can set the many variables that control the behavior of the NWC server. Here is an example configuration:
import os

# You can connect to any string that's a valid SQLAlchemy connection string.
DATABASE_URI: str = f"postgresql://{db_username}@{db_endpoint}/{db_name}"

# For AWS RDS, you can use something like the following settings:
# DATABASE_URI: str = f"postgresql+psycopg://{db_username}@{db_endpoint}/{db_name}?sslmode=verify-full&sslrootcert=/etc/vasp_secrets/rds-ca.pem"
# DATABASE_MODE = "rds"

# For local development, you can use a sqlite database:
# DATABASE_URI: str = "sqlite+aiosqlite:///" + os.path.join(
#     os.getcwd(), "instance", "nwc.sqlite"
# )

# It's a good practice to set secrets up in your secrets manager. For example, helm can mount a secret as a file:

# Create your own constant nostr private key via `openssl rand -hex 32`. Save it somewhere safe!
with open("/etc/secrets/nostr_privkey.pem") as f:
  NOSTR_PRIVKEY = f.read().strip()

# A secret key for encrypting cookies.
with open("/etc/secrets/secret.key") as f:
  SECRET_KEY = f.read().strip()

# The public key for verifying JWTs from the VASP. See the next section for how to generate this.
UMA_VASP_JWT_PUBKEY = "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEEVs/o5+uQbTjL3chynL4wXgUg2R9\nq9UU8I5mEovUf86QZ7kOBIjJwqnzD1omageEHWwHdBO6B+dFabmdT9POxg==\n-----END PUBLIC KEY-----"

# The URL to the VASP's login page. This is where the NWC image will redirect users to login.
UMA_VASP_LOGIN_URL = "https://examplevasp.com/login/umaauth"

# The URL to the VASP's token exchange endpoint. This is where the NWC image will exchange the user's
# short-lived JWT for a long-lived one. See NWC Container Auth Token Exchange.
UMA_VASP_TOKEN_EXCHANGE_URL = "https://examplevasp.com/umanwc/token"

# The base URL of the VASP's API where you set up the OpenAPI schema as described in "VASP UMA Auth API Schema".
# This is where the NWC image will send requests to the VASP.
VASP_UMA_API_BASE_URL = "https://examplevasp.com/umanwc"

# A name and logo for the VASP that will be displayed in the Permission and Connection Management UI.
VASP_NAME = "Example VASP"
VASP_LOGO_URL = "https://examplevasp.com/logo.png"

# The nostr relay where your NWC image will send and receive messages. For development, you can use Alby's public relay.
# See the "Running a Relay" guide for more details.
RELAY = "wss://relay.getalby.com/v1"

# The URL of the NWC image's root domain.
NWC_APP_ROOT_URL = "https://nwc.examplevasp.com"

# The NWC commands that your VASP supports. This should be a list of strings from the list below that
# corresond to the endpoints you implemented in the OpenAPI schema.
VASP_SUPPORTED_COMMANDS = [
    "pay_invoice",
    "make_invoice",
    "lookup_invoice",
    "get_balance",
    "get_budget",
    "get_info",
    "list_transactions",
    "pay_keysend",
    "lookup_user",
    "fetch_quote",
    "execute_quote",
    "pay_to_address",
]
The NWC image uses a SQL database to store connection information, budgets, and other data. The database is configured with the DATABASE_URI variable in the config file. The image supports any database that is supported by SQLAlchemy. For example, you can use a local sqlite database for development or a managed database like AWS RDS for production. The database schema is managed by the image and will be created automatically when the image is run. See above for some example database configurations.
If your database of choice is not supported, please file an issue in the github repo to request support. Alternatively, we're happy to accept pull requests that add support for additional databases if you'd like to contribute.
The NWC image includes a management UI that allows users to manage their connections, budgets, and permissions. The UI is configurable with the VASP_NAME and VASP_LOGO_URL currently. The UI is intentionally generic to minimize branding conflicts. We may add more branding options (colors, etc.) in the future if there is demand for it. Please file an issue in the github repo if you have a specific request.
You may want to allow your users to manage their UMA Auth connections from your website. To do this, you can link to the management UI with the following URL (replace nwc.examplevasp.com with the URL of your NWC image):
https://nwc.examplevasp.com/connections
UMA uses a configuration document similar to OpenID Connect's discovery document to describe the endpoints and capabilities of the VASP. In UMA Auth, this document is used by client applications to find the authentication and token endpoints, as well as some metadata about the VASP. See UMAD-10 for a full spec on this file. You'll need to serve a JSON response at /.well-known/uma-configuration on your VASP server domain. For example, if you'd hosted the docker image at https://nwc.examplevasp.com/, and your user's UMA addresses are $<user>@examplevasp.com, you would need to respond to GET https://examplevasp.com/.well-known/uma-configuration with something like:
{
  "name": "Cool Example VASP",
  "uma_major_versions": [0, 1],

  // The auth endpoint in the NWC image is /oauth/auth.
  "authorization_endpoint": "https://nwc.examplevasp.com/oauth/auth",
  // The token endpoint in the NWC image is /oauth/token.
  "token_endpoint": "https://nwc.examplevasp.com/oauth/token",
  // The connection management endpoint in the NWC image is /connections.
  "connection_management_endpoint": "https://nwc.examplevasp.com/connections",
  // The revocation endpoint in the NWC image is /oauth/revoke.
  "revocation_endpoint": "https://nwc.examplevasp.com/oauth/revoke",
  // Should always be "authorization_code" for UMA Auth.
  "grant_types_supported": ["authorization_code"],
  // Should always be S256 for UMA Auth.
  "code_challenge_methods_supported": ["S256"],
}
This is an example Helm chart that you can use to deploy the NWC image to a Kubernetes cluster. The chart includes configmaps and secrets for the configuration file and secrets that the NWC image needs to run. You can use this as a starting point for your own deployment.
deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nwc
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nwc
  template:
    metadata:
      labels:
        app: nwc
    spec:
      containers:
        - name: nwc
          image: ghcr.io/uma-universal-money-address/uma-nwc-server:latest
          ports:
            - containerPort: 8081
          env:
            - name: QUART_CONFIG
              value: /etc/nwc/config.py
          volumeMounts:
            - name: nwc-secrets
              mountPath: /etc/secrets
            - name: nwc-config
              mountPath: /etc/nwc
      volumes:
        - name: nwc-secrets
          secret:
            secretName: nwc-secrets
        - name: nwc-config
          configMap:
            name: nwc-config
configmap.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
  name: nwc-config
data:
  config.py: |
    import os

    DATABASE_URI: str = f"postgresql+psycopg://{db_username}@{db_endpoint}/{db_name}"
    # ... other config variables
secrets.yaml (you can use helm-secrets to manage secrets in Helm):
apiVersion: v1
kind: Secret
metadata:
  name: nwc-secrets
data:
  nostr_privkey.pem: <base64 encoded private key>
  secret.key: <base64 encoded secret key>
values.yaml:
db_username: "mydbuser"
db_endpoint: "mydbcluster.us-west-2.rds.amazonaws.com"
db_name: "mydbname"
Chart.yaml:
apiVersion: v2
name: nwc
description: "UMA NWC Server"
version: 0.1.0