Quick Start
Get Blackwalnut running and issue your first license in just a few steps.
Deploy with Docker
The fastest way to get started is with Docker. First, run migrations, then start the server:
# Run database migrations first
$ docker run --rm \
-e DATABASE_URL="ecto://user:pass@host/db" \
-e SECRET_KEY_BASE="your-secret-key" \
-e ENCRYPTION_KEY="your-encryption-key" \
goodway/blackwalnut /app/bin/migrate
# Start Blackwalnut
$ docker run -d \
--name blackwalnut \
-p 4000:4000 \
-e DATABASE_URL="ecto://user:pass@host/db" \
-e SECRET_KEY_BASE="your-secret-key" \
-e ENCRYPTION_KEY="your-encryption-key" \
-e PHX_HOST="localhost" \
goodway/blackwalnut Note: Generate secrets with: openssl rand -hex 64 for SECRET_KEY_BASE and openssl rand -base64 32 for ENCRYPTION_KEY.
Environment Variables
| Variable | Required | Description |
|---|---|---|
DATABASE_URL | Yes | PostgreSQL connection string |
SECRET_KEY_BASE | Yes | 64+ char secret for signing cookies |
ENCRYPTION_KEY | Yes | Base64-encoded key for encrypting sensitive data |
PHX_HOST | No | Production hostname (default: example.com) |
PORT | No | HTTP port (default: 4000) |
POOL_SIZE | No | Database connection pool size (default: 10) |
LICENSE_ISSUER | No | Name shown as license issuer |
CORS_ORIGINS | No | Comma-separated allowed origins for CORS |
DNS_CLUSTER_QUERY | No | DNS query for multi-node clustering |
Create Your Account
Open http://localhost:4000 and register your account.
Then create an API key from the settings page.
Create an Application
Create an application via the API. This automatically generates a 4096-bit RSA key pair for signing licenses.
$ curl -X POST http://localhost:4000/api/v1/apps \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "My App",
"slug": "my-app"
}'
# Response
{
"data": {
"id": "app_abc123",
"name": "My App",
"slug": "my-app",
"public_key_pem": "-----BEGIN PUBLIC KEY-----\n..."
}
} Set Up Tiers
Create pricing tiers for your application. Each license will be associated with a tier.
$ curl -X POST http://localhost:4000/api/v1/apps/my-app/tiers \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"name": "Pro",
"slug": "pro",
"position": 1
}' Issue a License
Create a customer and issue them a license. The response includes a signed JWT token.
# Create a customer first
$ curl -X POST http://localhost:4000/api/v1/apps/my-app/customers \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{"email": "user@example.com", "name": "John Doe"}'
# Issue a license
$ curl -X POST http://localhost:4000/api/v1/apps/my-app/licenses \
-H "Authorization: Bearer sk_live_your_api_key" \
-H "Content-Type: application/json" \
-d '{
"customer_id": "cus_abc123",
"tier_id": "tier_xyz789",
"installation_id": "550e8400-e29b-41d4-a716-446655440000",
"duration_days": 365
}'
# Response includes the signed JWT token
{
"data": {
"id": "lic_def456",
"token": "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...",
"tier": "pro",
"status": "active",
"expires_at": "2027-01-23T00:00:00Z"
}
} Verify the License
Verify licenses using the public API endpoint, or decode the JWT offline using the public key.
import jwt
# Your app's public key (fetch once and cache)
public_key = """-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A...
-----END PUBLIC KEY-----"""
def verify_license(token):
try:
payload = jwt.decode(
token,
public_key,
algorithms=["RS256"],
audience="my-app"
)
return {
"valid": True,
"tier": payload["tier"],
"features": payload.get("features", [])
}
except jwt.InvalidTokenError as e:
return {"valid": False, "error": str(e)}