Canton Validator
This repository contains:
- A running Canton / Splice validator deployed via Docker on an AWS EC2 instance.
- A Go client that programmatically interacts with the validator wallet APIs (transactions, balance, etc.).
- SSH port-forwarding configuration for secure local access.
- JWT-based authentication for protected API access.
1. Architecture Overview
Components
- AWS EC2 instance running Ubuntu
- Docker + docker-compose
- Splice Validator container
- Wallet API exposed internally on port 5003
- External UI exposed via port 8080
- Go client running locally on Mac
Flow
- Validator runs inside Docker on EC2.
- Wallet API runs inside container (port 5003).
- We do NOT expose 5003 publicly.
- We use SSH port forwarding to securely access it from local machine.
- Go client connects to forwarded localhost port.
- All requests require a valid JWT token.
This ensures:
- No public exposure of wallet API
- Authenticated access only
- Controlled access through SSH
Ledger API vs Wallet API (Important Distinction)
There are two different APIs exposed by the validator setup:
1 Ledger API (gRPC - Port 5001)
- Accessed using
grpcurl - Used for:
- Party creation
- User creation
- Granting act_as rights
- Submitting commands (e.g., WalletAppInstall)
- Querying active contracts
- Reading ledger updates
- Service namespace:
com.daml.ledger.api.v2.*
This is the low-level Canton Ledger API.
2 Wallet API (REST - Port 5003)
- Accessed using HTTP requests
- Base path:
http://localhost:5003/api/validator/v0/ - Used for:
- Listing wallet transactions
- Getting wallet balance
This is a higher-level application API built on top of the ledger.
Important:
- Port 5001 - Ledger (gRPC)
- Port 5003 - Wallet (REST)
They serve different purposes and require separate SSH port forwarding if accessed remotely.
5. SSH Port Forwarding (Security Layer)
We DO NOT expose port 5003 publicly.
Instead, we use:
-L 5003:localhost:5003 \
ubuntu@ec2-<public-ip>.compute.amazonaws.com
This creates:
Local machine - localhost:5003
Tunnel - EC2 container:5003
Meaning:
- Only your machine can access wallet API
- No public attack surface
- Production-safe approach
Generic Multi-Port SSH Tunnel Example
If you need access to multiple internal services (Ledger gRPC, Wallet API, Wallet UI, ANS UI), you can forward multiple ports in one command:
-L 5001:localhost:5001 \
-L 5003:localhost:5003 \
-L 8080:wallet.localhost:80 \
-L 8081:ans.localhost:80 \
ubuntu@<EC2_PUBLIC_IP>
What this does:
-L 5001:localhost:5001- Forwards Ledger gRPC API-L 5003:localhost:5003- Forwards Wallet REST API-L 8080:wallet.localhost:80- Forwards Wallet Web UI-L 8081:ans.localhost:80- Forwards ANS Web UI
Flow:
Local Machine - SSH Tunnel - EC2 - Docker Container - Internal Service
This keeps all internal services private while allowing secure local access for development.
2. Docker Validator Setup
Validator runs inside:
Key commands used:
Start validator:
We verified container:
docker exec -it splice-validator-validator-1 sh
Important environment variables inside container:
SPLICE_APP_VALIDATOR_LEDGER_API_AUTH_USER_NAME=ledger-api-user
This tells us which user the wallet API expects for authentication.
3. JWT Authentication
We generate token using:
Important:
- The username MUST match the wallet user inside container (
administrator) - Otherwise: Authorization Failed
JWT is then passed in header:
4. Wallet API Endpoints Used
Base path (internal):
List Transactions
POST request:
Body:
"page_size": 20
}
Get Balance
GET request:
Response includes:
- effective_unlocked_qty
- effective_locked_qty
- round
- total_holding_fees
6. Go Client Implementation
Located in:
Client responsibilities:
- Create HTTP client with timeout
- Inject JWT into Authorization header
- Call:
- ListTransactions(ctx, pageSize)
- GetBalance(ctx)
- Print structured response
Example main flow:
client.ListTransactions(ctx, 20)
client.GetBalance(ctx)
7. Security Model
Current Security:
- Wallet API not publicly exposed
- JWT authentication required
- Access only via SSH tunnel
- Token tied to wallet user
If additional security required:
- Rotate signing secret
- Restrict EC2 security group to known IP
- Disable public port 8080
- Add reverse proxy with TLS
8. What We Achieved
Deployed Canton validator via Docker
Identified correct wallet user
Fixed Authorization errors
Determined required request body schema
Secured API using SSH tunnel
Built Go client for programmatic control
Fetched transactions successfully
Fetched validator wallet balance
System is now:
- Secure
- Scriptable
- Production-ready for automation
- Extendable for transfers and validator control
9. Next Possible Extensions
- Add transfer execution in Go
- Add structured response parsing instead of map[string]interface{}
- Add CLI flags
- Add metrics
- Add background sync loop
- Add unit tests
- Add structured logging
10. Final Notes
Never expose port 5003 publicly.
Always use SSH tunnel or private networking.
JWT must match wallet user configured inside container.
This repository now serves as:
- Validator control client
- Operational documentation
- Security reference for deployment