Lightning Nodes

Understanding Macaroons

Understanding Macaroons

This guide provides a comprehensive look at what macaroons are, how they work, and how to use them effectively with your Comet nodes.

Estimated reading time: 15 minutes

Macaroons are the authentication mechanism that secures access to your Comet Platform nodes. If you're building applications that interact with LND or Tapd nodes, understanding macaroons is essential.

What Are Macaroons?

Macaroons are a type of cryptographic credential designed for distributed authorization. Think of them as "cookies with superpowers", they're bearer tokens that can be attenuated (restricted) to grant specific permissions without requiring direct communication with the authentication server.

Why Macaroons Instead of Traditional Auth?

Traditional authentication methods like API keys or OAuth tokens have limitations in distributed systems:

  • API Keys are all-or-nothing: either you have full access or no access

  • OAuth requires callbacks and server-to-server communication for token validation

  • JWT tokens can't be easily restricted after issuance

Macaroons solve these problems by:

  1. Granular Permissions: Each macaroon can specify exactly what operations are allowed

  2. Attenuation: You can create a restricted version of a macaroon without contacting the server

  3. Distributed Authorization: Third parties can add restrictions without invalidating the macaroon

  4. Cryptographic Security: Tampering with permissions invalidates the macaroon signature

Real-World Analogy

Imagine a hotel key card (the macaroon) that:

  • Grants access to your room, the gym, and the pool

  • You can create a restricted copy that only opens your room (attenuation)

  • The hotel never needs to know you made this restricted copy

  • If someone tries to modify the restricted copy to add pool access, it stops working (tamper-proof)

That's how macaroons work with your Lightning and Taproot Assets nodes.

Macaroon Structure and Components

Understanding macaroon structure helps you use them effectively and troubleshoot issues.

Core Components

Every macaroon consists of three parts:

1. Identifier

A unique string that identifies which macaroon this is. The identifier is used by the server to look up the root key needed to verify the signature.

2. Caveats

A list of restrictions or conditions that must be satisfied for the macaroon to be valid. There are two types:

First-Party Caveats (enforced by the node itself):

  • Time-based expiration: "valid until 2026-12-31"

  • IP restrictions: "only from 203.0.113.45"

  • Method restrictions: "only for /v1/balance and /v1/channels endpoints"

Third-Party Caveats (require validation from another service):

  • "must authenticate with service X"

  • Rarely used in Lightning/Tapd contexts

Example caveats:


3. Signature

A cryptographic signature that proves the macaroon hasn't been tampered with. This is calculated using HMAC with a secret root key known only to the node.

Macaroon Encoding

Macaroons are typically base64-encoded when transmitted or stored:

When you see strings like this in configuration files or API requests, they're encoded macaroons.

Macaroon-Based Authentication in Comet Platform

Comet Platform uses macaroons to secure access to both LND and Tapd node interfaces.

Authentication Flow


Default Macaroons

When you create a node on Comet Platform, several macaroons are automatically generated:

For LND Nodes:

Macaroon

Permissions

Use Case

admin.macaroon

Full access to all RPC methods

Administrative tasks, full node control

readonly.macaroon

Read-only access (no state changes)

Monitoring, dashboards, analytics

invoice.macaroon

Create and manage invoices

Payment processing applications

router.macaroon

Route queries and updates

Payment routing optimization

signer.macaroon

Sign messages and transactions

Authentication, proof of node ownership

For Tapd Nodes:

Macaroon

Permissions

Use Case

admin.macaroon

Full access to all Taproot Assets operations

Asset minting, transfers, full control

readonly.macaroon

View asset balances and history

Monitoring and reporting

mint.macaroon

Mint new assets only

Asset issuance applications

send.macaroon

Send existing assets

Payment applications

Security Note: Admin macaroons grant complete control over your node. Treat them like private keys—never share them, commit them to version control, or expose them in client-side code.

Permission Levels and Scopes

Macaroons support fine-grained permission control through URI patterns and method restrictions.

LND Permission Examples


Tapd Permission Examples


Creating and Managing Macaroons

Generating New Macaroons

You can create custom macaroons with specific permissions to follow the principle of least privilege.

Using LND CLI (via Comet)

bashCopy# Create a macaroon that can only check balance and list channels
lncli bakemacaroon \
  info:read \
  offchain:read \
  --save_to readonly_monitoring.macaroon

# Create a macaroon for invoice management only
lncli bakemacaroon \
  invoices:write \
  invoices:read \
  --save_to invoice_app.macaroon

# Create a macaroon for a specific third-party app
lncli bakemacaroon \
  info:read \
  offchain:read \
  invoices:write \
  --save_to thunderhub.macaroon

Using Python SDK

pythonCopyimport grpc
from lnd_grpc import lnd_grpc

# Connect to your node
node = lnd_grpc.Client(
    macaroon_filepath='admin.macaroon',
    cert_filepath='tls.cert',
    grpc_host='your-node.comet.platform',
    grpc_port=10009
)

# Create a custom macaroon with specific permissions
permissions = [
    {
        "entity": "info",
        "action": "read"
    },
    {
        "entity": "offchain",
        "action": "read"
    },
    {
        "entity": "invoices",
        "action": "write"
    }
]

custom_macaroon = node.bakemacaroon(permissions=permissions)
print(f"New macaroon: {custom_macaroon.macaroon}")

Using JavaScript SDK

javascriptCopyconst lnService = require('ln-service');

const { lnd } = lnService.authenticatedLndGrpc({
  cert: 'base64_tls_cert',
  macaroon: 'base64_admin_macaroon',
  socket: 'your-node.comet.platform:10009',
});

// Create a custom macaroon
const { macaroon } = await lnService.createMacaroon({
  lnd,
  permissions: [
    {entity: 'info', action: 'read'},
    {entity: 'offchain', action: 'read'},
    {entity: 'invoices', action: 'write'},
  ],
});

console.log('Custom macaroon:', macaroon);

Setting Permissions and Restrictions

Time-Based Expiration

Create macaroons that automatically expire:

bashCopy# Expires in 24 hours (86400 seconds)
lncli bakemacaroon \
  info:read \
  --timeout 86400 \
  --save_to temp_access.macaroon

# Expires at specific time
lncli constrainmacaroon \
  --custom_caveat "time-before 2026-12-31T23:59:59Z" \
  --macaroon_file invoice.macaroon \
  --save_to invoice_limited.macaroon

IP Restrictions

Restrict macaroon usage to specific IP addresses:

bashCopy# Only allow from specific IP
lncli constrainmacaroon \
  --custom_caveat "ipaddr 203.0.113.45" \
  --macaroon_file admin.macaroon \
  --save_to admin_office.macaroon

# Allow from multiple IPs (requires multiple caveats)
lncli constrainmacaroon \
  --custom_caveat "ipaddr 203.0.113.45" \
  --custom_caveat "ipaddr 203.0.113.46" \
  --macaroon_file admin.macaroon \
  --save_to admin_restricted.macaroon

Note: IP restrictions are enforced by the LND node. Make sure your application's outbound IP is stable or use CIDR ranges.

Using Macaroons with API Calls

REST API

Macaroons are passed in the HTTP header Grpc-Metadata-macaroon as hex-encoded strings:

bashCopy# Convert base64 macaroon to hex
MACAROON_HEX=$(xxd -r -p <<< $(base64 -d admin.macaroon) | xxd -p -c 256)

# Make REST API call
curl -X GET \
  https://your-node.comet.platform:8080/v1/getinfo \
  --cacert tls.cert \
  --header "Grpc-Metadata-macaroon: $MACAROON_HEX"

Python Example

pythonCopyimport requests
import codecs

# Read and encode macaroon
with open('admin.macaroon', 'rb') as f:
    macaroon_bytes = f.read()
    macaroon_hex = codecs.encode(macaroon_bytes, 'hex').decode()

# Make authenticated request
headers = {
    'Grpc-Metadata-macaroon': macaroon_hex
}

response = requests.get(
    'https://your-node.comet.platform:8080/v1/getinfo',
    headers=headers,
    verify='tls.cert'
)

print(response.json())

JavaScript/Node.js Example

javascriptCopyconst fs = require('fs');
const axios = require('axios');
const https = require('https');

// Read macaroon and convert to hex
const macaroonFile = fs.readFileSync('admin.macaroon');
const macaroonHex = macaroonFile.toString('hex');

// Create HTTPS agent with TLS cert
const httpsAgent = new https.Agent({
  ca: fs.readFileSync('tls.cert')
});

// Make authenticated request
axios.get('https://your-node.comet.platform:8080/v1/getinfo', {
  httpsAgent,
  headers: {
    'Grpc-Metadata-macaroon': macaroonHex
  }
})
.then(response => {
  console.log(response.data);
})
.catch(error => {
  console.error('Error:', error.message);
});

gRPC API

For gRPC connections, macaroons are passed as metadata:

Python Example

pythonCopyimport grpc
import codecs

# Read certificates and macaroon
with open('tls.cert', 'rb') as f:
    cert = f.read()

with open('admin.macaroon', 'rb') as f:
    macaroon = codecs.encode(f.read(), 'hex')

# Create SSL credentials
ssl_creds = grpc.ssl_channel_credentials(cert)

# Create channel
channel = grpc.secure_channel(
    'your-node.comet.platform:10009',
    ssl_creds
)

# Import LND gRPC stubs (assuming you have them)
import lightning_pb2 as ln
import lightning_pb2_grpc as lnrpc

# Create stub
stub = lnrpc.LightningStub(channel)

# Create metadata callback for macaroon
def metadata_callback(context, callback):
    callback([('macaroon', macaroon)], None)

# Create auth credentials
auth_creds = grpc.metadata_call_credentials(metadata_callback)

# Combine SSL and auth credentials
combined_creds = grpc.composite_channel_credentials(ssl_creds, auth_creds)

# Make authenticated call
request = ln.GetInfoRequest()
response = stub.GetInfo(request, metadata=[('macaroon', macaroon)])
print(response)

Go Example

goCopypackage main

import (
    "context"
    "fmt"
    "io/ioutil"
    "log"

    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
    "gopkg.in/macaroon.v2"
)

func main() {
    // Load TLS certificate
    creds, err := credentials.NewClientTLSFromFile("tls.cert", "")
    if err != nil {
        log.Fatal(err)
    }

    // Load macaroon
    macaroonBytes, err := ioutil.ReadFile("admin.macaroon")
    if err != nil {
        log.Fatal(err)
    }

    mac := &macaroon.Macaroon{}
    if err := mac.UnmarshalBinary(macaroonBytes); err != nil {
        log.Fatal(err)
    }

    // Create macaroon credentials
    macCred := macaroons.NewMacaroonCredential(mac)

    // Connect with combined credentials
    conn, err := grpc.Dial(
        "your-node.comet.platform:10009",
        grpc.WithTransportCredentials(creds),
        grpc.WithPerRPCCredentials(macCred),
    )
    if err != nil {
        log.Fatal(err)
    }
    defer conn.Close()

    // Create client and make call
    client := lnrpc.NewLightningClient(conn)
    info, err := client.GetInfo(context.Background(), &lnrpc.GetInfoRequest{})
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("Node info: %v\n", info)
}

Using Macaroons with CLI

The Comet CLI automatically handles macaroon authentication when you configure it:

bashCopy# Configure CLI with macaroon
comet config set node-macaroon /path/to/admin.macaroon
comet config set node-cert /path/to/tls.cert
comet config set node-host your-node.comet.platform:10009

# All subsequent commands use the macaroon automatically
comet lnd getinfo
comet lnd listchannels
comet tapd listassets

Macaroon Security Best Practices

Storage Recommendations

DO:

  • ✅ Store macaroons in secure, encrypted storage

  • ✅ Use environment variables for server-side applications

  • ✅ Use secure credential managers (AWS Secrets Manager, HashiCorp Vault)

  • ✅ Encrypt macaroons at rest with strong encryption

  • ✅ Use file system permissions (chmod 600) to restrict access

  • ✅ Store different macaroons for different environments (dev, staging, prod)

DON'T:

  • ❌ Commit macaroons to version control (Git, SVN, etc.)

  • ❌ Include macaroons in client-side code or mobile apps

  • ❌ Share macaroons via email, chat, or unencrypted channels

  • ❌ Store macaroons in application logs

  • ❌ Use the same macaroon across multiple services

  • ❌ Store admin macaroons in production applications

Rotation Policies

Regularly rotate macaroons, especially after:

  • Team member departures

  • Security incidents or breaches

  • Suspected credential compromise

  • Major application updates

  • Every 90 days (recommended)

Rotation Process:

bashCopy# 1. Generate new macaroon with same permissions
lncli bakemacaroon \
  info:read \
  offchain:read \
  invoices:write \
  --save_to app_v2.macaroon

# 2. Update application configuration
comet config set app-macaroon /path/to/app_v2.macaroon

# 3. Test application with new macaroon
comet lnd getinfo

# 4. Delete old macaroon (after confirming new one works)
lncli deletemacaroonid --root_key_id <old_macaroon_id><

Revocation Procedures

If a macaroon is compromised:

Immediate Actions:

bashCopy# 1. List all macaroon IDs
lncli listmacaroonids

# 2. Delete the compromised macaroon
lncli deletemacaroonid --root_key_id <macaroon_id>

# 3. Generate replacement macaroons
lncli bakemacaroon <permissions> --save_to new_macaroon.macaroon

# 4. Update all applications using the old macaroon</permissions></macaroon_id

For Admin Macaroon Compromise:

If your admin macaroon is compromised, you may need to:

  1. Immediately close all channels (if LND node)

  2. Transfer all assets to a new address (if Tapd node)

  3. Create a new node with fresh credentials

  4. Restore funds/assets to the new node

Critical: Admin macaroon compromise is a serious security incident. Contact Comet support immediately at support@comet.platform.

Troubleshooting Macaroon Issues

Common Error Messages

"permission denied"

Cause: The macaroon doesn't have permission for the requested operation.

Solution:

bashCopy# Check macaroon permissions
lncli printmacaroon --macaroon_file your.macaroon

# Create a new macaroon with correct permissions
lncli bakemacaroon <required_permissions> --save_to new.macaroon</required_permissions>

"invalid macaroon signature"

Cause: The macaroon has been tampered with or is corrupted.

Solution:

  • Download a fresh copy from Comet dashboard

  • Verify the macaroon file isn't corrupted

  • Ensure you're using the correct macaroon for the node

"macaroon expired"

Cause: Time-based caveat has expired.

Solution:

bashCopy# Check expiration
lncli printmacaroon --macaroon_file your.macaroon

# Create new macaroon with extended expiration
lncli bakemacaroon <permissions> --timeout <seconds> --save_to new.macaroon</seconds></permissions

"IP address not authorized"

Cause: Request is coming from an IP not listed in macaroon caveats.

Solution:

  • Verify your application's outbound IP address

  • Update IP restrictions or remove them:

bashCopy# Create macaroon without IP restrictions
lncli bakemacaroon <permissions> --save_to unrestricted.macaroon</permissions>

Debugging Tips

1. Print Macaroon Contents

bashCopy# View all macaroon details
lncli printmacaroon --macaroon_file your.macaroon

# Output shows:
# - Identifier
# - All caveats
# - Permissions
# - Signature

2. Test with Different Macaroons

bashCopy# Try with admin macaroon (full permissions)
lncli --macaroonpath=admin.macaroon getinfo

# If this works, the issue is with custom macaroon permissions

3. Verify Encoding

bashCopy# Check base64 encoding
base64 -d your.macaroon | xxd

# Should show valid binary data, not garbage

4. Test REST vs gRPC

Sometimes REST works but gRPC doesn't (or vice versa). Test both:

bashCopy# REST test
curl -X GET \
  --cacert tls.cert \
  --header "Grpc-Metadata-macaroon: $(xxd -ps -u -c 1000 admin.macaroon)" \
  https://your-node:8080/v1/getinfo

# gRPC test (with lncli)
lncli --macaroonpath=admin.macaroon getinfo

Code Examples for Common Operations

Check Macaroon Permissions

pythonCopyimport base64
import pymacaroons

# Load macaroon
with open('your.macaroon', 'rb') as f:
    macaroon_bytes = f.read()
    macaroon_b64 = base64.b64encode(macaroon_bytes).decode()

# Parse macaroon
mac = pymacaroons.Macaroon.deserialize(macaroon_b64)

print(f"Identifier: {mac.identifier}")
print(f"Location: {mac.location}")
print("Caveats:")
for caveat in mac.caveats:
    print(f"  - {caveat.caveat_id}")

Validate Macaroon Locally

javascriptCopyconst fs = require('fs');
const macaroons = require('macaroons.js');

// Load macaroon
const macaroonBinary = fs.readFileSync('your.macaroon');
const macaroonB64 = macaroonBinary.toString('base64');

// Import macaroon
const mac = macaroons.MacaroonsBuilder.deserialize(macaroonB64);

console.log('Identifier:', mac.identifier);
console.log('Caveats:');
mac.caveatPackets.forEach(caveat => {
  console.log('  -', caveat.getValueAsText());
});

Create Attenuated Macaroon

pythonCopyimport pymacaroons
import base64

# Load admin macaroon
with open('admin.macaroon', 'rb') as f:
    admin_mac = pymacaroons.Macaroon.deserialize(
        base64.b64encode(f.read()).decode()
    )

# Add time restriction (expires in 24 hours)
from datetime import datetime, timedelta
expiry = datetime.utcnow() + timedelta(hours=24)
expiry_str = expiry.strftime('%Y-%m-%dT%H:%M:%SZ')

restricted_mac = admin_mac.add_first_party_caveat(f"time-before {expiry_str}")

# Add IP restriction
restricted_mac = restricted_mac.add_first_party_caveat("ipaddr 203.0.113.45")

# Save attenuated macaroon
with open('restricted.macaroon', 'wb') as f:
    f.write(base64.b64decode(restricted_mac.serialize()))

print("Created restricted macaroon with time and IP caveats")

External Documentation

Need Help?

If you're experiencing issues with macaroons:

  1. Check the Troubleshooting & FAQs guide

  2. Review error messages carefully—they usually indicate the exact problem

  3. Test with admin macaroon to isolate permission issues

  4. Contact Comet support at support@comet.platform

Table of Contents

Copyright © 2025 Comet Cash

Czech Republic VASP License Registration Nº 22053751

info@cometcash.com

All rights reserved.

Copyright © 2025 Comet Cash

Czech Republic VASP License Registration Nº 22053751

info@cometcash.com

All rights reserved.