A modern, privacy-focused alias system for cryptocurrency addresses.
Cryptalias provides human-readable aliases (e.g., alice$example.com) for cryptocurrency addresses, building upon OpenAlias with enhanced security and privacy features:
- Dynamic address generation: Request fresh addresses from wallet services to prevent address reuse
- Cryptographic signatures: All responses are signed for client verification
- Per-client address stability: Same client receives consistent addresses within a configurable TTL window
- Extensible architecture: Support for database-backed aliases and third-party integrations
This project is a reference implementation of the Cryptalias protocol. Other implementations can (and should) be built in different languages while remaining compatible with the protocol.
OpenAlias offers familiar and human-friendly crypto aliases but was designed for static address mappings. Cryptalias maintains the same user-friendly name@domain format (using $ instead of @ to avoid protocol confusion) while addressing privacy concerns:
OpenAlias approach:
- Fixed address mappings in DNS
Cryptalias approach:
- Static or dynamic address resolution
- Per-client stability windows to reduce address sniffing
- Cryptographically signed responses
- Clear integration path for external wallet services
For implementation details, see PROTOCOL.md.
Minimal HTTP client helpers are available in clients/ (JavaScript, TypeScript, Rust, Dart, C++, Swift, Kotlin). The Go resolver lives in internal/cryptalias/resolve.go.
Each client verifies signatures and enforces expires.
The cryptalias binary can act as a one-shot resolver without starting the server:
cryptalias resolve 'alice$example.com' xmrAdd --json for structured output:
cryptalias resolve --json 'alice$example.com' xmr- Docker and Docker Compose
- A domain with DNS access
- A Monero wallet (for the example configuration)
-
Create a config file:
mkdir -p cryptalias cd cryptalias -
Prepare your wallet:
Place wallet files in
./monero/wallet/and configure inconfig.yml:tokens: - name: Monero tickers: [xmr] endpoint: type: internal address: http://monero-wallet-rpc:18083/json_rpc username: your-username password: your-password wallet_file: main wallet_password: your-wallet-password
Note: Monero requires
/json_rpcendpoint suffix and uses HTTP Digest authentication. -
Create a docker-compose file (uses the GHCR image):
services: monero-wallet-rpc: image: sethsimmons/simple-monero-wallet-rpc:latest user: 1000:1000 volumes: - ./monero:/home/monero/ command: - --confirm-external-bind - --rpc-bind-ip=0.0.0.0 - --rpc-bind-port=18083 - --rpc-login=${MONERO_RPC_USER:-cryptalias}:${MONERO_RPC_PASS:-change-me} - --wallet-dir=/home/monero/wallet - --daemon-address=${MONERO_DAEMON_ADDRESS:-monerod:18081} cryptalias: image: ghcr.io/kaigoh/cryptalias:main user: 1000:1000 volumes: - ./cryptalias:/config command: ["/config/config.yml"]
This assumes
monerodis reachable asmonerod:18081from the wallet RPC container. Adjust--daemon-addressto match your setup. You can also override defaults via environment variables:MONERO_DAEMON_ADDRESSMONERO_RPC_USERMONERO_RPC_PASS
-
Start the stack:
docker compose up --build
-
Configure DNS:
Cryptalias generates cryptographic keys automatically. Copy the DNS TXT record from the logs:
_cryptalias.yourdomain.com TXT "pubkey=..." -
Test resolution:
http://cryptalias.localhost/_cryptalias/resolve/xmr/me$cryptalias.localhost http://cryptalias.localhost/_cryptalias/resolve/xmr:me$cryptalias.localhost
base_url: http://cryptalias.localhost
public_port: 8080
logging:
level: info
rate_limit:
enabled: true
requests_per_minute: 60
burst: 10
resolution:
ttl_seconds: 60
client_identity:
strategy: xff
header: X-Forwarded-For
verify:
interval_minutes: 5
domains:
- domain: cryptalias.localhost
aliases:
- alias: me
wallet:
ticker: xmr
address: ""
account_index: 0
tokens:
- name: Monero
tickers: [xmr]
endpoint:
type: internal
address: http://monero-wallet-rpc:18083/json_rpc
username: cryptalias
password: change-me
wallet_file: main
wallet_password: change-me-walletThe configuration file is monitored for changes and reloads automatically. Invalid configurations are rejected, preserving the last valid state.
Cryptalias uses client identity for both rate limiting and address stability. Choose the strategy that matches your deployment:
| Strategy | Use Case | Configuration |
|---|---|---|
remote_address |
Direct connections (no proxy) | Uses TCP connection IP |
xff |
Behind reverse proxy (default) | Uses X-Forwarded-For header |
xff_ua |
Shared IPs (office NAT, etc.) | Combines X-Forwarded-For with user agent hash |
header |
Custom proxy setup | Uses specified header |
header_ua |
Custom proxy + shared IPs | Combines custom header with user agent hash |
Reverse proxy examples:
Traefik
resolution:
ttl_seconds: 60
client_identity:
strategy: xff
header: X-Forwarded-ForNginx
resolution:
ttl_seconds: 60
client_identity:
strategy: xff
header: X-Forwarded-Forproxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;Caddy
resolution:
ttl_seconds: 60
client_identity:
strategy: xff
header: X-Forwarded-ForStatic alias (returns fixed address):
wallet:
ticker: xmr
address: "89abc..."Dynamic alias (requests fresh address from wallet):
wallet:
ticker: xmr
address: ""
account_index: 0Configure multiple aliases using different wallet accounts:
aliases:
- alias: personal
wallet:
ticker: xmr
address: ""
account_index: 0
- alias: donations
wallet:
ticker: xmr
address: ""
account_index: 1Optional routing parameters: account_index, account_id, wallet_id
Integrate external wallet services via gRPC:
endpoint:
type: external
address: wallet-service:50051
token: "authentication-token" # OptionalSee proto/cryptalias/v1/wallet_service.proto for the gRPC contract.
Cryptalias requires two routing paths:
- Discovery endpoint on your domain:
/.well-known/cryptalias/ - Resolution endpoint on Cryptalias host:
/_cryptalias/
Traefik Example
labels:
- traefik.enable=true
# Discovery on resolved domain
- traefik.http.routers.cryptalias-wellknown.rule=Host(`example.com`) && PathPrefix(`/.well-known/cryptalias/`)
- traefik.http.routers.cryptalias-wellknown.entrypoints=websecure
- traefik.http.routers.cryptalias-wellknown.tls.certresolver=letsencrypt
- traefik.http.routers.cryptalias-wellknown.service=cryptalias-svc
# Resolution on Cryptalias host
- traefik.http.routers.cryptalias-public.rule=Host(`cryptalias.example.com`) && PathPrefix(`/_cryptalias/`)
- traefik.http.routers.cryptalias-public.entrypoints=websecure
- traefik.http.routers.cryptalias-public.tls.certresolver=letsencrypt
- traefik.http.routers.cryptalias-public.service=cryptalias-svc
- traefik.http.services.cryptalias-svc.loadbalancer.server.port=8080Caddy Example
example.com {
handle_path /.well-known/cryptalias/* {
reverse_proxy cryptalias:8080
}
}
cryptalias.example.com {
reverse_proxy cryptalias:8080
}Nginx Example
server {
server_name example.com;
location ^~ /.well-known/cryptalias/ {
proxy_pass http://cryptalias:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
server_name cryptalias.example.com;
location /_cryptalias/ {
proxy_pass http://cryptalias:8080;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}Cryptalias performs periodic health checks on configured domains:
- Validates
/.well-known/cryptalias/configurationendpoint - Verifies the public key in
/.well-known/cryptalias/configurationmatches the domain key - Checks DNS
_cryptaliasTXT record
Domains failing verification will not resolve aliases until health checks pass.
Monitoring endpoints:
GET /.well-known/cryptalias/status- Domain health statusGET /healthz- Service liveness check
Configure verification interval:
verify:
interval_minutes: 5Browser-based resolvers need CORS headers on the public resolver endpoint (/_cryptalias/...). Cryptalias sets permissive CORS for all endpoints:
Access-Control-Allow-Origin: *Access-Control-Allow-Methods: GET, OPTIONSAccess-Control-Allow-Headers: Accept, Content-Type
This is intentional so any origin can resolve aliases. If you lock this down, browser clients on other domains will fail.
- Rate limiting: Prevents scraping and spam (configurable per-minute limits)
- Address caching: Per-client TTL reduces address enumeration
- Cryptographic signatures: All responses include domain key signatures for client verification
"Unknown alias" (404 error)
- Verify domain matches
domains[].domainin config - Ensure alias exists under that domain
- Confirm ticker is defined in
tokens[].tickers - Check alias format:
alias$domain,alias+tag$domain, orticker:alias+tag$domain
Config changes not taking effect
- Check logs for "config reload rejected"
- Validation errors preserve the previous valid configuration
- Review YAML syntax and required fields
All requests appear to come from same client
- Incorrect
client_identity.strategyfor your deployment - Direct connections require
remote_address - Proxied deployments require
xffwith proper header forwarding
Dynamic alias not working
- Static mode requires non-empty
wallet.address - Empty
addressfield triggers dynamic resolution
YAML unmarshaling errors
- Ensure objects are formatted as maps, not lists
- Example:
wallet:should be a single object, not an array
Monero wallet RPC failures
- Verify wallet RPC can connect to daemon
- Confirm wallet files exist at configured path
- Check RPC credentials match in both compose file and config
- Ensure endpoint address ends with
/json_rpc - Remember Monero uses HTTP Digest authentication
Signature verification failures
- Confirm both
private_keyandpublic_keyare set for each domain - Copy DNS TXT record from logs if keys were auto-generated
- Verify DNS propagation of
_cryptaliasTXT record
├── cmd/cryptalias/ # Main application entry point
├── internal/cryptalias/ # Core server implementation
├── proto/cryptalias/v1/ # gRPC protocol definitions
├── config.example.yml # Example configuration
├── docker-compose.yml # Docker stack definition
└── PROTOCOL.md # Protocol specification
- gRPC contract:
proto/cryptalias/v1/wallet_service.proto - Server implementation:
internal/cryptalias/server.go - Application entry:
cmd/cryptalias/main.go
If you find Cryptalias useful, donations are gratefully accepted to support ongoing development:
Monero: 8BUwkJ4LWiJS7bHAsKxBbaR1dkxzcvMJoNqGeCcLEt42betKeFnnEEA7xEJLBNNA1ngBS4V4pTVt6g8S4XZyePsc1UH5msc / donations$cryptalias.xyz
See LICENSE for details.
- Database-backed alias configuration
- Third-party integration APIs
- Additional cryptocurrency support
- Automated invoice system integration

